Mercurial > ~darius > hgwebdir.cgi > pyinst
diff fsp7_phasenoise.py @ 74:b6ebe05f250f
Add some commentry about what it works with
author | Daniel O'Connor <doconnor@gsoft.com.au> |
---|---|
date | Wed, 25 Sep 2024 21:10:01 +0930 |
parents | |
children | e2bb136bd2ed |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fsp7_phasenoise.py Wed Sep 25 21:10:01 2024 +0930 @@ -0,0 +1,170 @@ +#!/usr/bin/env python + +# Copyright (c) 2014 +# Daniel O'Connor <darius@dons.net.au>. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +import argparse +import math +import numpy +import rsib +import scipy +import scpi +import sys + +def setup2(r, centre, span, rbw, vbw): + r.write('SENSE1:FREQ:CENT %f Hz' % (centre)) + r.write('SENSE1:FREQ:SPAN %f Hz' % (span)) + r.write('SENS1:BAND:RES %f Hz' % (rbw)) + r.write('SENS1:BAND:VID %f Hz' % (vbw)) + +def dosweep(r, sweeps): + swt = float(r.ask('SWE:TIME?')) + tout = swt * 5 * sweeps + if tout < 1: + tout = 1 + #print('Sweep time', swt) + + # Trigger the sweep + r.write('INIT;*WAI') + + # Wait for it to be done + opc = int(r.ask('*OPC?', timeout = tout)) + assert(opc == 1) + + +def setup(r, carrier, sweeps, rbw, vbw, ofs, atten): + cpwrlim = -30 + measurements = ( + { 'offset' : 10, 'rbw' : 10, 'vbw' : 10 }, + { 'offset' : 100, 'rbw' : 10, 'vbw' : 10 }, + { 'offset' : 1e3, 'rbw' : 300, 'vbw' : 300 }, + { 'offset' : 10e3, 'rbw' : 300, 'vbw' : 300 }, + { 'offset' : 100e3, 'rbw' : 300, 'vbw' : 300 }, + { 'offset' : 1e6, 'rbw' : 1e3, 'vbw' : 3e3 }, + ) + # Reset to defaults + r.write('*RST') + + # Set to single sweep mode + r.write('INIT:CONT OFF') + + # Enable display updates + r.write('SYST:DISP:UPD ON') + + # Set attenuation + r.write('INP:ATT %f' % atten) + + # + # Look for carrier + # + print('Measuring carrier') + # Set frequency range etc + setup2(r, carrier, 1e3, 300, 300) + + # Switch marker 1 on in screen A + r.write('CALC:MARK1 ON') + + # Do the sweep + dosweep(r, 1) + + # Check the instrument is happy + status = int(r.ask('STAT:QUES:COND?')) + pwrstat = int(r.ask('STAT:QUES:POW:COND?')) + if status != 0 or pwrstat != 0: + raise Exception('Instrument warning, status %s power status %s' % + (bin(status), bin(pwrstat))) + # Look for the carrier + r.write('CALC:MARK1:MAX') + cpwr = float(r.ask('CALC:MARK1:Y?')) + if cpwr < cpwrlim: + raise Exception('Carrier power too low / not found: %.1f dBm vs %.1f dBm' % (cpwr, cpwrlim)) + cmeas = float(r.ask('CALC:MARK1:X?')) + print('Found carrier at %.7f MHz with power %.1f dBm' % (cmeas / 1e6, cpwr)) + + # Turn averaging on + r.write('AVER:STAT ON') + + # Set number of sweeps + r.write('SWE:COUN %d' % (sweeps)) + + # Enable phase noise measurement + r.write('CALC:DELT1:FUNC:PNO') + + for idx, m in enumerate(measurements): + # Setup measurement + setup2(r, carrier, m['offset'] * 2.1, m['rbw'], m['vbw']) + + # Do the sweep + dosweep(r, sweeps) + + # Set offset of phase noise measurement + r.write('CALC:DELT2:FUNC:FIX:RPO:X %f Hz' % (m['offset'])) + meas = float(r.ask('CALC:DELT:FUNC:PNO:RES?')) + print('Offset %.0fHz %.2f dBc/Hz' % (m['offset'], meas)) + return + +def getphnoise(r): + # Trigger the sweep + r.write('INIT;*WAI') + + # Wait for it to be done + opc = int(r.ask('*OPC?', timeout = 1000)) + #print 'OPC - %d' % (opc) + assert(opc == 1) + + # Set data format + r.write('FORM:DATA ASC') + + # Read phase noise value + data = r.ask('CALC:MARK2:FUNC:NOIS:RES?') + #print 'Data - ' + data + + return float(data) + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description = 'Configures a Rhode Schwartz FSP7 spectrum analyser to do a phase noise measurement') + parser.add_option('-s', '--span', dest = 'span', default = 1e6, help = 'Span frequency in Hz (default: %default)', type = float) + parser.add_option('-w', '--sweeps', dest = 'sweeps', default = 20, help = 'Number of sweeps (default: %default)', type = int) + parser.add_option('-r', '--rbw', dest = 'rbw', default = 1000, help = 'Resolution bandwidth in Hz (default: %default)', type = float) + parser.add_option('-v', '--vbw', dest = 'vbw', default = 3000, help = 'Video bandwidth in Hz (default: %default)', type = float) + parser.add_option('address', '--address', dest = 'address', help = 'Address of analyser', type = str) + parser.add_option('centre', '--centre', dest = 'centreq', help = 'Centre frequency of measurement', type = float) + parser.add_option('offset', '--offset', dest = 'offset', help = 'Offset frequency to take measurement at', type = float) + + (options, args) = parser.parse_args() + + # Connect to the analyser + r = rsib.RSIBDevice(addr) + + # ID instrument + print('ID is', r.ask('*IDN?').decode('ascii')) + + # Setup parameters + setup(r, options.centre, options.span, options.sweeps, options.rbw, options.vbw, options.offset) + + while True: + print('Centre: %.1f Mhz, Span %.1f Mhz, RBW %.1f kHz, %d sweeps, Offset %.1fkHz: %.2f dBc/Hz' % ( + options.centre / 1e6, options.span / 1e6, options.rbw / 1e3, options.sweeps, + options.offset / 1e3, getphnoise(r)))