diff --git a/Makefile b/Makefile index fe9d66e..9069a50 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,19 @@ # Copyright (C) 2011 Associated Universities, Inc. Washington DC, USA. -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # 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, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -# +# # Correspondence concerning GBT software should be addressed as follows: # GBT Operations # National Radio Astronomy Observatory @@ -24,7 +24,7 @@ CC = g++ AR = ar DOXY = doxygen CFLAGS = -c -Wall -fPIC -DLINUX -LDFLAGS = +LDFLAGS = SOURCES = ValonSynth.cc Serial.cc OBJECTS = $(SOURCES:.cc=.o) PLATFORM = LINUX @@ -52,4 +52,4 @@ clean: .PHONY: clobber clobber: clean - rm -rf $(STARGET) $(DTARGET) \ No newline at end of file + rm -rf $(STARGET) $(DTARGET) diff --git a/README-Python.md b/README-Python.md new file mode 100644 index 0000000..0216de1 --- /dev/null +++ b/README-Python.md @@ -0,0 +1,52 @@ +# Valon Synth + +## Installation + +In order to use the Python code, it is necessary to build the C++ +library first: + + $ make + +This should create a `libValonSynth.so` shared object. + +The Python software can now be installed for development use. As a +first step, create a Python virtual environment. + + $ virtualenv venv + +Optionally, activate it; the steps below assumes that the virtual +environment has not been activated. + +Use the virtual environment to setup the package: + + $ ./venv/bin/pip install -r requirements.txt + +To be able to use the package, we also need to be able to import it. +Normally the source directory name matches the package name, but +unfortunately in this instance it doesn't. We create a symbolic link to +fudge it. + + $ ln -s src valon_synth + +## Communication checks + +We provide a example script that tests basic communications. It +requires knowledge of the (USB-connected) serial port the Valon is +connected to. In Linux, plug in both USB plugs on the Valon, and do + + $ dmesg + +The last lines in the log should be similar to the following: + + usb 5-1: FTDI USB Serial Device converter now attached to ttyUSB0 + +This indicates the port we are interested in is `/dev/ttyUSB0`, which +coincidentally is the default port in the communications test example +script. + +To run the communications check, simply do: + + $ ./venv/bin/python example/commtest.py --port /dev/ttyUSB0 + +Feel free to replace `/dev/ttyUSB0` in the command shown above with your +locally identified port. diff --git a/Serial.cc b/Serial.cc index cf2674f..56a62d6 100644 --- a/Serial.cc +++ b/Serial.cc @@ -1,19 +1,19 @@ //# Copyright (C) 2011 Associated Universities, Inc. Washington DC, USA. -//# +//# //# This program is free software; you can redistribute it and/or modify //# it under the terms of the GNU General Public License as published by //# the Free Software Foundation; either version 2 of the License, or //# (at your option) any later version. -//# +//# //# 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, write to the Free Software //# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -//# +//# //# Correspondence concerning GBT software should be addressed as follows: //# GBT Operations //# National Radio Astronomy Observatory @@ -121,7 +121,7 @@ int Serial::serial_write(const unsigned char *output_buffer, } -int Serial::serial_read(unsigned char *input_buffer, const int &number_of_bytes, +int Serial::serial_read(unsigned char *input_buffer, const int &number_of_bytes, const int timeo_us) { int bytes_received = 0; @@ -156,19 +156,19 @@ int Serial::serial_read(unsigned char *input_buffer, const int &number_of_bytes, &input_buffer[bytes_received], #endif (number_of_bytes - bytes_received)); - - // was an error condition present? + + // was an error condition present? if (read_bytes < 0) { // Flush input buffer. #if defined (SOLARIS) || defined (LINUX) - ioctl(the_serial_port, TCFLSH, 0); + ioctl(the_serial_port, TCIFLUSH, 0); #else ioctl(the_serial_port, FIORFLUSH, 0); #endif return(read_bytes); } - + bytes_received += read_bytes; if (the_input_mode == Serial::canonical && bytes_received > 0) @@ -207,7 +207,7 @@ int Serial::open_serial_port(const char *port_name) { // Flush serial port. #if defined (SOLARIS) || defined (LINUX) - ioctl(the_serial_port, TCFLSH, 0); + ioctl(the_serial_port, TCIOFLUSH, 0); #endif #if defined(VXWORKS) ioctl(the_serial_port, FIOFLUSH, 0); @@ -234,11 +234,11 @@ int Serial::update_parity(const Serial::parity_choices &parity) switch (parity) { case Serial::odd: - the_termios.c_cflag |= (PARODD | PARENB); + the_termios.c_cflag |= (PARODD | PARENB); break; case Serial::even: - the_termios.c_cflag |= PARENB; - the_termios.c_cflag &= ~(PARODD); + the_termios.c_cflag |= PARENB; + the_termios.c_cflag &= ~(PARODD); break; case Serial::none: the_termios.c_cflag &= ~PARENB; @@ -288,7 +288,7 @@ int Serial::update_baud_rate(const int &baud_rate) cerr << "Cannot set baud rate" << endl; return (-1); } - + speed_t speed; switch (baud_rate) @@ -602,7 +602,7 @@ int Serial::set_raw_input_mode() // as BS-SP-BS. the_termios.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOE); the_termios.c_iflag &= ~(INPCK | ISTRIP); - + if (tcsetattr(the_serial_port, TCSAFLUSH, &the_termios) != 0) { // TBF: Error message @@ -646,7 +646,7 @@ int Serial::set_canonical_input_mode() // Enable canonical mode, enable echoing of input characters, enable // echoing of erase character as BS-SP-BS. the_termios.c_lflag |= (ICANON | ECHO | ECHOE); - + if (tcsetattr(the_serial_port, TCSAFLUSH, &the_termios) != 0) { // TBF: Error message @@ -691,19 +691,19 @@ int Serial::set_other_flags() the_termios.c_lflag &= ~IEXTEN; // input flags:- - // Send a SIGINT when a break condition is detected, map CR to NL, + // Send a SIGINT when a break condition is detected, map CR to NL, the_termios.c_iflag &= ~(BRKINT | ICRNL); - + // control flags:- // Setting CLOCAL and CREAD ensures that the program will not become // the owner of the port and the serial interface driver will read // incoming data bytes. the_termios.c_cflag |= (CLOCAL | CREAD); - + // output flags:- // Turn off the output processing the_termios.c_oflag &= ~(OPOST); - + // Set up 0 character minimum with no timeout. the_termios.c_cc[VMIN] = 0; the_termios.c_cc[VTIME] = 0; diff --git a/Serial.h b/Serial.h index af360ea..aff619f 100644 --- a/Serial.h +++ b/Serial.h @@ -1,19 +1,19 @@ //# Copyright (C) 2011 Associated Universities, Inc. Washington DC, USA. -//# +//# //# This program is free software; you can redistribute it and/or modify //# it under the terms of the GNU General Public License as published by //# the Free Software Foundation; either version 2 of the License, or //# (at your option) any later version. -//# +//# //# 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, write to the Free Software //# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -//# +//# //# Correspondence concerning GBT software should be addressed as follows: //# GBT Operations //# National Radio Astronomy Observatory @@ -26,13 +26,13 @@ // -// This class provides a vehicle for serial communication on the vxWorks, +// This class provides a vehicle for serial communication on the vxWorks, // Linux, and Solaris platforms. // // -//
  • The configuration of the serial port that you wish to use (i.e. -// baud rate, parity, port name, etc). +//
  • The configuration of the serial port that you wish to use (i.e. +// baud rate, parity, port name, etc). //
  • //
    @@ -41,7 +41,7 @@ // // -// This class provides a portable way to use the serial port for +// This class provides a portable way to use the serial port for // program communication. The two main member functions that enable this // communication are read and write. Other member functions are provided // to configure the serial port as needed. The default serial port @@ -69,13 +69,13 @@ // // The valid choices for input mode are raw and canonical. These are // defined via the enumeration input_choices below. Raw mode processes -// the characters as they are typed. Canonical mode handles the +// the characters as they are typed. Canonical mode handles the // characters on a line by line basis - i.e. it waits for the '/n' // character before reading/writing. // // -// To contain all serial communication needs across multiple platforms +// To contain all serial communication needs across multiple platforms // within a single class. // @@ -134,7 +134,7 @@ class Serial // -1 on failure. int set_stop_bits(const int &stop_bits); - // set_hardware_flow_control accepts either 0 = No hardware flow + // set_hardware_flow_control accepts either 0 = No hardware flow // control or 1 = Hardware flow control. If the input parameter is // neither 0 nor 1, hardware flow control is disabled. Returns 0 on // success, -1 on failure. @@ -177,7 +177,7 @@ class Serial input_choices the_input_mode; // - // These member functions set up parameters for the serial port over + // These member functions set up parameters for the serial port over // which the user of this class has no control. // int open_serial_port(const char *port_name); @@ -193,7 +193,7 @@ class Serial virtual int serial_write(const unsigned char *output_buffer, const int &number_of_bytes); virtual int serial_read(unsigned char *input_buffer, - const int &number_of_bytes, + const int &number_of_bytes, const int timeout_usec = 200000); virtual int update_parity(const parity_choices &parity); virtual int update_baud_rate(const int &baud_rate); @@ -215,7 +215,7 @@ inline int Serial::write(const unsigned char *output_buffer, } -inline int Serial::read(unsigned char *input_buffer, const int &number_of_bytes, +inline int Serial::read(unsigned char *input_buffer, const int &number_of_bytes, const int tmo_usec) { return (serial_read(input_buffer, number_of_bytes, tmo_usec)); diff --git a/ValonSynth.cc b/ValonSynth.cc index d1ca213..495503b 100644 --- a/ValonSynth.cc +++ b/ValonSynth.cc @@ -1,19 +1,19 @@ //# Copyright (C) 2011 Associated Universities, Inc. Washington DC, USA. -//# +//# //# This program is free software; you can redistribute it and/or modify //# it under the terms of the GNU General Public License as published by //# the Free Software Foundation; either version 2 of the License, or //# (at your option) any later version. -//# +//# //# 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, write to the Free Software //# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -//# +//# //# Correspondence concerning GBT software should be addressed as follows: //# GBT Operations //# National Radio Astronomy Observatory diff --git a/ValonSynth.h b/ValonSynth.h index 2b917ee..4afe50c 100644 --- a/ValonSynth.h +++ b/ValonSynth.h @@ -1,19 +1,19 @@ //# Copyright (C) 2011 Associated Universities, Inc. Washington DC, USA. -//# +//# //# This program is free software; you can redistribute it and/or modify //# it under the terms of the GNU General Public License as published by //# the Free Software Foundation; either version 2 of the License, or //# (at your option) any later version. -//# +//# //# 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, write to the Free Software //# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -//# +//# //# Correspondence concerning GBT software should be addressed as follows: //# GBT Operations //# National Radio Astronomy Observatory @@ -33,20 +33,20 @@ /** * Interface to a Valon 5007 dual synthesizer. - * + * * The reference signal, reference select, and flash commands are shared between * synthesizers. All other settings are independent, so a synthesizer must be * specified when getting and setting values. Frequency values are specified in * MegaHertz (MHz) unless otherwise noted. - * + * * \section calculations Calculations - * + * * The output frequency of the synthesizer is controlled by a number of * parameters. The relationship between these parameters may be expressed by the * following equations. \f$EPDF\f$ is the Effective Phase Detector Frequency and is * the reference frequency(?) after applying the relevant options. (doubler, * halver and divider) - * + * * \f[ * 1 \le dbf=2^n \le 16 * \f] @@ -62,11 +62,11 @@ * \f[ * mod = \lfloor\frac{EPDF}{channel\_spacing}+0.5\rfloor * \f] - * + * * \f$frac\f$ and \f$mod\f$ are a ratio, and can be reduced to the simplest * fraction after the calculations above. To compute the output frequency, use * the following equation. - * + * * \f[ * frequency = (ncount+\frac{frac}{mod})\times\frac{EPDF}{dbf} * \f] @@ -79,7 +79,7 @@ class ValonSynth * referenced. **/ enum Synthesizer { A = 0x00, B = 0x08 }; - + /** * Holds various options used to control the operation of a synthesizer. **/ @@ -90,17 +90,17 @@ class ValonSynth * this mode it is in "low noise" mode. **/ bool low_spur; - + /** * The reference frequency doubler is active. **/ bool double_ref; - + /** * The reference frequency halver is active. **/ bool half_ref; - + /** * The reference frequency divider value; **/ @@ -116,7 +116,7 @@ class ValonSynth * Minimum frequency the VCO is capable of producing. **/ uint16_t min; - + /** * Maximum frequency the VCO is capable of producing. **/ @@ -133,14 +133,14 @@ class ValonSynth * \name Methods relating to output frequency * \{ **/ - + /** * Read the current settings from the synthesizer. * @param[in] synth The synthesizer to be read. * @return Frequency in MHz. **/ float get_frequency(enum Synthesizer synth); - + /** * Read the current settings from the synthesizer. * @param[in] synth The synthesizer to be read. @@ -148,7 +148,7 @@ class ValonSynth * @return True on succesful completion. **/ bool get_frequency(enum Synthesizer synth, float &frequency); - + /** * Set the synthesizer to the desired frequency, or best approximation based * on channel spacing. See the section on \ref calculations. @@ -165,14 +165,14 @@ class ValonSynth * \name Methods relating to the reference frequency * \{ **/ - + /** * Read the current reference frequency. This is shared between the two * channels. * @return The reference frequency in Hz. **/ uint32_t get_reference(); - + /** * Read the current reference frequency. This is shared between the two * channels. @@ -180,7 +180,7 @@ class ValonSynth * @return True on successful completion. **/ bool get_reference(uint32_t &reference); - + /** * Set the synthesizer reference frequency. This does not change the actual * reference frequency of the synthesizer for either internal or external @@ -225,12 +225,12 @@ class ValonSynth /** * \} * \name Members relating to the synthesizer options - * + * * Note that although the reference frequency is shared these options are * specific to a particular synthesizer. * \{ **/ - + /** * Read the current options for a synthesizer. * @param[in] synth The synthesizer to be read. @@ -238,7 +238,7 @@ class ValonSynth * @return True on successful completion. **/ bool get_options(enum Synthesizer synth, struct options &opts); - + /** * Set the options for a synthesizer. * @param[in] synth The synthesizer to be set. @@ -252,13 +252,13 @@ class ValonSynth * \name Methods relating to the reference source * \{ **/ - + /** * Read the current reference source. * @return True if external, false if internal. **/ bool get_ref_select(); - + /** * Read the current reference source. * @param[out] e_not_i Receives the refernce source. True if external, @@ -266,7 +266,7 @@ class ValonSynth * @return True on successful completion. **/ bool get_ref_select(bool &e_not_i); - + /** * Set the reference source. * @param[in] e_not_i True for external, false for internal. @@ -279,7 +279,7 @@ class ValonSynth * \name Methods relating to the voltage controlled oscillator * \{ **/ - + /** * Read the current range of the VCO. * @param[in] synth The synthesizer to be read. @@ -287,7 +287,7 @@ class ValonSynth * @return True on successful completion. **/ bool get_vco_range(enum Synthesizer synth, vco_range &vcor); - + /** * Set the range of the VCO. This affects the allowable frequency range of * the output. See \ref calculations for details. @@ -302,7 +302,7 @@ class ValonSynth * \name Methods relating to phase lock. * \{ **/ - + /** * Read the current state of phase lock. * @param[in] synth The synthesizer to be read. @@ -323,7 +323,7 @@ class ValonSynth * \name Methods relating to synthesizer labels. * \{ **/ - + /** * Read the current label of the specified synthesizer. * @param[in] synth The synthesizer to be read. @@ -332,7 +332,7 @@ class ValonSynth * @return True on successful completion. **/ bool get_label(enum Synthesizer synth, char *label); - + /** * Set the label of the specified synthesizer. * @param[in] synth The synthesizer to be read. @@ -344,7 +344,7 @@ class ValonSynth /** * \} **/ - + /** * Copies all current settings for both synthesizers to non-volatile flash * memory. @@ -379,7 +379,7 @@ class ValonSynth void pack_short(uint16_t num, uint8_t *bytes); void unpack_int(const uint8_t *bytes, uint32_t &num); void unpack_short(const uint8_t *bytes, uint16_t &num); - + Serial s; }; diff --git a/example/commtest.py b/example/commtest.py new file mode 100755 index 0000000..2d43317 --- /dev/null +++ b/example/commtest.py @@ -0,0 +1,61 @@ +#! /usr/bin/env python + +from optparse import OptionParser +import time + +import valon_synth +from valon_synth import SYNTH_B + + +# Simple example of using the Valon Synth directly +def main(port): + print """ +Connect a Valon 5007 to a spectrum analyser -- a 20dB attenuator is used. +The Valon 5007 is specified to have a range of 137 MHz to 4400MHz. +""" + raw_input('Enter to connect to Valon') + + # MTS uses only one of the available synthesizers (currently SYNTH 2) + synth = valon_synth.Synthesizer(port, timeout=None, checksum=True) + + if synth.get_rf_level(SYNTH_B) != -4: + synth.set_rf_level(SYNTH_B, -4) + + # Set CW signal frequency + synth.set_frequency(SYNTH_B, freq=137, chan_spacing=1.) + print 'CW frequency set to %s MHz' % synth.get_frequency(SYNTH_B) + raw_input('Enter to continue') + + print 'Setting Valon to Low Spur Mode' + synth.set_options(SYNTH_B, low_spur=1) + raw_input('Enter to continue') + + print 'Settings Valon to Low Noise Mode' + synth.set_options(SYNTH_B, low_spur=0) + raw_input('Enter to continue') + + print 'Sweep over frequencies 137MHz to 1500MHz' + for freq_mhz in range(137, 1500, 20): + synth.set_frequency(SYNTH_B, freq=freq_mhz, chan_spacing=1.) + print 'CW frequency set to %s MHz' % synth.get_frequency(SYNTH_B) + raw_input('Enter to continue') + + print 'Sweep over frequencies 137MHz to 4400MHz' + raw_input('Enter to continue') + for freq_mhz in range(137, 4400, 100): + synth.set_frequency(SYNTH_B, freq=freq_mhz, chan_spacing=1.) + print 'CW frequency set to %s MHz' % synth.get_frequency(SYNTH_B) + time.sleep(1) + + +if __name__ == '__main__': + parser = OptionParser(version="%prog 0.1") + parser.add_option('-p', '--port', + action='store', + dest='tty', + default='/dev/ttyUSB0', + help="Set Serial Port, default is '%default'.") + (opts, _) = parser.parse_args() + main(opts.tty) + +# -fin- diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..de620ec --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +# development requirements +--editable . diff --git a/setup.py b/setup.py index dc4ec10..e80f2c5 100644 --- a/setup.py +++ b/setup.py @@ -7,5 +7,5 @@ maintainer = 'NRAO', packages = ['valon_synth'], package_dir = {'valon_synth': 'src'}, - requires = ['pyserial'], + install_requires = ['pyserial'], ) diff --git a/src/__init__.py b/src/__init__.py index ebcfb4c..a497480 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,23 +1,23 @@ # Copyright (C) 2011 Associated Universities, Inc. Washington DC, USA. -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # 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, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -# +# # Correspondence concerning GBT software should be addressed as follows: -# GBT Operations -# National Radio Astronomy Observatory -# P. O. Box 2 -# Green Bank, WV 24944-0002 USA +# GBT Operations +# National Radio Astronomy Observatory +# P. O. Box 2 +# Green Bank, WV 24944-0002 USA -from valon_synth import Synthesizer, SYNTH_A, SYNTH_B, INT_REF, EXT_REF +from valon_synth import Synthesizer, SYNTH_A, SYNTH_B, INT_REF, EXT_REF # noqa diff --git a/src/valon_synth.py b/src/valon_synth.py index 04822bf..6bf6dcb 100644 --- a/src/valon_synth.py +++ b/src/valon_synth.py @@ -1,28 +1,26 @@ # Copyright (C) 2011 Associated Universities, Inc. Washington DC, USA. -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # 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, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -# +# # Correspondence concerning GBT software should be addressed as follows: -# GBT Operations -# National Radio Astronomy Observatory -# P. O. Box 2 -# Green Bank, WV 24944-0002 USA +# GBT Operations +# National Radio Astronomy Observatory +# P. O. Box 2 +# Green Bank, WV 24944-0002 USA -""" -Provides a serial interface to the Valon 500x. -""" +"""Provides a serial interface to the Valon 500x.""" # Python modules import struct @@ -32,9 +30,10 @@ __author__ = "Patrick Brandt" __copyright__ = "Copyright 2011, Associated Universities, Inc." -__credits__ = ["Patrick Brandt, Stewart Rumley, Steven Stark"] +__credits__ = ["Patrick Brandt, Stewart Rumley, Steven Stark, Glenn Jones," + " Jack Hickish, Deneys Maartens"] __license__ = "GPL" -#__version__ = "1.0" +__version__ = "1.0" __maintainer__ = "Patrick Brandt" @@ -48,13 +47,19 @@ ACK = 0x06 NACK = 0x15 + def _generate_checksum(data): "Generate a checksum for the data provided." return chr(sum([ord(b) for b in data]) % 256) + def _verify_checksum(data, checksum): "Verify a checksum for the data provided." - return (_generate_checksum(data) == checksum) + if _generate_checksum(data) != checksum: + print 'checksum failure' + return False + return True + def _pack_freq_registers(ncount, frac, mod, dbf, old_data): "Do bit packing for the frequency setting registers." @@ -68,6 +73,7 @@ def _pack_freq_registers(ncount, frac, mod, dbf, old_data): reg4 |= (dbf_table.get(dbf, 0)) << 20 return struct.pack('>IIIIII', reg0, reg1, reg2, reg3, reg4, reg5) + def _unpack_freq_registers(data): "Do bit unpacking for the frequency setting registers." dbf_rev_table = {0: 1, 1: 2, 2: 4, 3: 8, 4: 16} @@ -78,16 +84,18 @@ def _unpack_freq_registers(data): dbf = dbf_rev_table.get((reg4 >> 20) & 0x07, 1) return ncount, frac, mod, dbf + class Synthesizer: """A simple interface to the Valon 500x synthesizer.""" - def __init__(self, port): + def __init__(self, port, timeout=1.0, checksum=False): self.conn = serial.Serial(None, 9600, serial.EIGHTBITS, serial.PARITY_NONE, serial.STOPBITS_ONE) - self.conn.setPort(port) + self.conn.port = port + self.conn.timeout = timeout + self.do_checksum = checksum def get_frequency(self, synth): - """ - Returns the current output frequency for the selected synthesizer. + """Returns the current output frequency for the selected synthesizer. @param synth : synthesizer this command affects (0 for 1, 8 for 2). @type synth : int @@ -96,21 +104,23 @@ def get_frequency(self, synth): """ self.conn.open() data = struct.pack('>B', 0x80 | synth) - self.conn.write(data) - data = self.conn.read(24) - checksum = self.conn.read(1) - self.conn.close() - #_verify_checksum(data, checksum) + try: + self.conn.write(data) + data = self.conn.read(24) + checksum = self.conn.read(1) + finally: + self.conn.close() + if self.do_checksum: + _verify_checksum(data, checksum) ncount, frac, mod, dbf = _unpack_freq_registers(data) epdf = self._get_epdf(synth) return (ncount + float(frac) / mod) * epdf / dbf - def set_frequency(self, synth, freq, chan_spacing = 10.): - """ - Sets the synthesizer to the desired frequency + def set_frequency(self, synth, freq, chan_spacing=10.): + """Sets the synthesizer to the desired frequency. - Sets to the closest possible frequency, depending on the channel spacing. - Range is determined by the minimum and maximum VCO frequency. + Sets to the closest possible frequency, depending on the channel + spacing. Range is determined by the minimum and maximum VCO frequency. @param synth : synthesizer this command affects (0 for 1, 8 for 2). @type synth : int @@ -143,37 +153,52 @@ def set_frequency(self, synth, freq, chan_spacing = 10.): mod = 1 self.conn.open() data = struct.pack('>B', 0x80 | synth) - self.conn.write(data) - old_data = self.conn.read(24) - checksum = self.conn.read(1) - #_verify_checksum(old_data, checksum) + try: + self.conn.write(data) + old_data = self.conn.read(24) + checksum = self.conn.read(1) + except: + self.conn.close() + raise + if self.do_checksum: + _verify_checksum(old_data, checksum) data = struct.pack('>B24s', 0x00 | synth, _pack_freq_registers(ncount, frac, mod, dbf, old_data)) checksum = _generate_checksum(data) - self.conn.write(data + checksum) - data = self.conn.read(1) - self.conn.close() + try: + self.conn.write(data + checksum) + data = self.conn.read(1) + finally: + self.conn.close() ack = struct.unpack('>B', data)[0] return ack == ACK def get_reference(self): - """ - Get reference frequency in MHz + """Get reference frequency in MHz. + + @param No input parameters + + @return Int: Reference input frequency (MHz) """ self.conn.open() data = struct.pack('>B', 0x81) - self.conn.write(data) - data = self.conn.read(4) - checksum = self.conn.read(1) - self.conn.close() - #_verify_checksum(data, checksum) + try: + self.conn.write(data) + data = self.conn.read(4) + checksum = self.conn.read(1) + finally: + self.conn.close() + if self.do_checksum: + _verify_checksum(data, checksum) freq = struct.unpack('>I', data)[0] return freq def set_reference(self, freq): - """ - Set reference frequency in MHz + """Set reference frequency in MHz. + + For the 5007: this value must be between 5 MHz and 150 MHz, according + to the data sheet. @param freq : frequency in MHz @type freq : float @@ -183,15 +208,18 @@ def set_reference(self, freq): self.conn.open() data = struct.pack('>BI', 0x01, freq) checksum = _generate_checksum(data) - self.conn.write(data + checksum) - data = self.conn.read(1) - self.conn.close() + try: + self.conn.write(data + checksum) + data = self.conn.read(1) + finally: + self.conn.close() ack = struct.unpack('>B', data)[0] return ack == ACK def get_rf_level(self, synth): - """ - Returns RF level in dBm + """Returns RF level in dBm. + + The current output power level can be one of four values: -4, -1, 2, 5 @param synth : synthesizer address, 0 or 8 @type synth : int @@ -201,19 +229,88 @@ def get_rf_level(self, synth): rfl_table = {0: -4, 1: -1, 2: 2, 3: 5} self.conn.open() data = struct.pack('>B', 0x80 | synth) - self.conn.write(data) - data = self.conn.read(24) - checksum = self.conn.read(1) - self.conn.close() - #_verify_checksum(data, checksum) + try: + self.conn.write(data) + data = self.conn.read(24) + checksum = self.conn.read(1) + finally: + self.conn.close() + if self.do_checksum: + _verify_checksum(data, checksum) _, _, _, _, reg4, _ = struct.unpack('>IIIIII', data) rfl = (reg4 >> 3) & 0x03 rf_level = rfl_table.get(rfl) return rf_level - def set_rf_level(self, synth, rf_level): + def rf_disable(self, synth): """ - Set RF level + Disable RF output. + + @param synth : synthesizer address, 0 or 8 + @type synth : int + """ + self.conn.open() + data = struct.pack('>B', 0x80 | synth) + try: + self.conn.write(data) + data = self.conn.read(24) + checksum = self.conn.read(1) + except: + self.conn.close() + raise + if self.do_checksum: + _verify_checksum(data, checksum) + reg0, reg1, reg2, reg3, reg4, reg5 = struct.unpack('>IIIIII', data) + reg4 &= 0xffffffdf # RF Output power up + reg4 |= 1 << 9 # VCO power down + data = struct.pack('>BIIIIII', 0x00 | synth, + reg0, reg1, reg2, reg3, reg4, reg5) + checksum = _generate_checksum(data) + try: + self.conn.write(data + checksum) + data = self.conn.read(1) + finally: + self.conn.close() + ack = struct.unpack('>B', data)[0] + return ack == ACK + + def rf_enable(self, synth): + """ + Enable RF output. + + @param synth : synthesizer address, 0 or 8 + @type synth : int + """ + self.conn.open() + data = struct.pack('>B', 0x80 | synth) + try: + self.conn.write(data) + data = self.conn.read(24) + checksum = self.conn.read(1) + except: + self.conn.close() + raise + if self.do_checksum: + _verify_checksum(data, checksum) + reg0, reg1, reg2, reg3, reg4, reg5 = struct.unpack('>IIIIII', data) + reg4 &= 0xfffff7ff # VCO power up + reg4 |= 1 << 5 # RF Output power up + data = struct.pack('>BIIIIII', 0x00 | synth, + reg0, reg1, reg2, reg3, reg4, reg5) + checksum = _generate_checksum(data) + try: + self.conn.write(data + checksum) + data = self.conn.read(1) + finally: + self.conn.close() + ack = struct.unpack('>B', data)[0] + return ack == ACK + + def set_rf_level(self, synth, rf_level): + """Set RF level. + + Allows user to select one of four output power levels: -4, -1, 2, 5 + These levels corresponds approximately to some preset output power. @param synth : synthesizer address, 0 or 8 @type synth : int @@ -229,25 +326,57 @@ def set_rf_level(self, synth, rf_level): return False self.conn.open() data = struct.pack('>B', 0x80 | synth) - self.conn.write(data) - data = self.conn.read(24) - checksum = self.conn.read(1) - #_verify_checksum(data, checksum) + try: + self.conn.write(data) + data = self.conn.read(24) + checksum = self.conn.read(1) + except: + self.conn.close() + raise + if self.do_checksum: + _verify_checksum(data, checksum) reg0, reg1, reg2, reg3, reg4, reg5 = struct.unpack('>IIIIII', data) reg4 &= 0xffffffe7 reg4 |= (rfl & 0x03) << 3 data = struct.pack('>BIIIIII', 0x00 | synth, - reg0, reg1, reg2, reg3, reg4, reg5) + reg0, reg1, reg2, reg3, reg4, reg5) checksum = _generate_checksum(data) - self.conn.write(data + checksum) - data = self.conn.read(1) - self.conn.close() + try: + self.conn.write(data + checksum) + data = self.conn.read(1) + finally: + self.conn.close() ack = struct.unpack('>B', data)[0] return ack == ACK def get_options(self, synth): - """ - Get options tuple: + """Get options tuple: + + Output a Tuple of 4 parameters, which can be 0 (disabled) or 1(enabled) + + - double: The reference doubler is used to enable a multiply by 2 + function before the internal reference divider. + + Enable the doubler when using a 5 MHz external reference frequency. + When using the internal 10 MHz reference the doubler should be + disabled. + + - half: The reference divide by 2 is used to enable a divide by 2 + function after the intercal reference divider. + + When enabled, the input to phase-frequency detector will have a 50% + duty cycle which will allow for faster lock up time. In order to use + this mode a 20 MHz external reference would have to be available. + For normal operations set the reference div by 2 to disabled. + + - r: reference frequency divisor + + - spur: Low noise mode vs Low spur mode. + + Low noise mode affects the operation of the fractional synthesizer, + and this mode will produce the lowest phase noise but there may be + some spurious output signals. Low spur mode will reduce spurious + output response but the overall phase noise will be higher. bool double: if True, reference frequency is doubled bool half: if True, reference frequency is halved @@ -262,11 +391,14 @@ def get_options(self, synth): """ self.conn.open() data = struct.pack('>B', 0x80 | synth) - self.conn.write(data) - data = self.conn.read(24) - checksum = self.conn.read(1) - self.conn.close() - #_verify_checksum(data, checksum) + try: + self.conn.write(data) + data = self.conn.read(24) + checksum = self.conn.read(1) + finally: + self.conn.close() + if self.do_checksum: + _verify_checksum(data, checksum) _, _, reg2, _, _, _ = struct.unpack('>IIIIII', data) low_spur = ((reg2 >> 30) & 1) & ((reg2 >> 29) & 1) double = (reg2 >> 25) & 1 @@ -274,10 +406,8 @@ def get_options(self, synth): divider = (reg2 >> 14) & 0x03ff return double, half, divider, low_spur - def set_options(self, synth, double = 0, half = 0, divider = 1, - low_spur = 0): - """ - Set options. + def set_options(self, synth, double=0, half=0, divider=1, low_spur=0): + """Set options. double and half both True is same as both False. @@ -301,42 +431,53 @@ def set_options(self, synth, double = 0, half = 0, divider = 1, """ self.conn.open() data = struct.pack('>B', 0x80 | synth) - self.conn.write(data) - data = self.conn.read(24) - checksum = self.conn.read(1) - #_verify_checksum(data, checksum) + try: + self.conn.write(data) + data = self.conn.read(24) + checksum = self.conn.read(1) + except: + self.conn.close() + raise + if self.do_checksum: + _verify_checksum(data, checksum) reg0, reg1, reg2, reg3, reg4, reg5 = struct.unpack('>IIIIII', data) reg2 &= 0x9c003fff reg2 |= (((low_spur & 1) << 30) | ((low_spur & 1) << 29) | ((double & 1) << 25) | ((half & 1) << 24) | ((divider & 0x03ff) << 14)) data = struct.pack('>BIIIIII', 0x00 | synth, - reg0, reg1, reg2, reg3, reg4, reg5) + reg0, reg1, reg2, reg3, reg4, reg5) checksum = _generate_checksum(data) - self.conn.write(data + checksum) - data = self.conn.read(1) - self.conn.close() + try: + self.conn.write(data + checksum) + data = self.conn.read(1) + finally: + self.conn.close() ack = struct.unpack('>B', data)[0] return ack == ACK def get_ref_select(self): """Returns the currently selected reference clock. - Returns 1 if the external reference is selected, 0 otherwise. + @param No input parameters + + @return: 1 if the external reference is selected, 0 otherwise. """ self.conn.open() data = struct.pack('>B', 0x86) - self.conn.write(data) - data = self.conn.read(1) - checksum = self.conn.read(1) - self.conn.close() - #_verify_checksum(data, checksum) + try: + self.conn.write(data) + data = self.conn.read(1) + checksum = self.conn.read(1) + finally: + self.conn.close() + if self.do_checksum: + _verify_checksum(data, checksum) is_ext = struct.unpack('>B', data)[0] return is_ext & 1 - def set_ref_select(self, e_not_i = 1): - """ - Selects either internal or external reference clock. + def set_ref_select(self, e_not_i=1): + """Selects either internal or external reference clock. @param e_not_i : 1 (external) or 0 (internal); default 1 @type e_not_i : int @@ -346,33 +487,44 @@ def set_ref_select(self, e_not_i = 1): self.conn.open() data = struct.pack('>BB', 0x06, e_not_i & 1) checksum = _generate_checksum(data) - self.conn.write(data + checksum) - data = self.conn.read(1) - self.conn.close() + try: + self.conn.write(data + checksum) + data = self.conn.read(1) + finally: + self.conn.close() ack = struct.unpack('>B', data)[0] return ack == ACK def get_vco_range(self, synth): - """ - Returns (min, max) VCO range tuple. + """Returns (min, max) VCO range tuple. + + The VCO Frequency Range information is used to limit and check the + resulting VCO output frequency, entered in the set frequency request + function. @param synth : synthesizer base address @type synth : int - @return: min,max in MHz + @return: Tuple: (lowest VCO output frequency, highest VCO output + frequency) in MHz """ self.conn.open() data = struct.pack('>B', 0x83 | synth) - self.conn.write(data) - data = self.conn.read(4) - checksum = self.conn.read(1) - self.conn.close() - #_verify_checksum(data, checksum) + try: + self.conn.write(data) + data = self.conn.read(4) + checksum = self.conn.read(1) + finally: + self.conn.close() + if self.do_checksum: + _verify_checksum(data, checksum) return struct.unpack('>HH', data) def set_vco_range(self, synth, low, high): - """ - Sets VCO range. + """Sets VCO range. + + Set the minimum and maximum frequency range of the selected synthesizer + VCO. @param synth : synthesizer base address @type synth : int @@ -388,15 +540,16 @@ def set_vco_range(self, synth, low, high): self.conn.open() data = struct.pack('>BHH', 0x03 | synth, low, high) checksum = _generate_checksum(data) - self.conn.write(data + checksum) - data = self.conn.read(1) - self.conn.close() + try: + self.conn.write(data + checksum) + data = self.conn.read(1) + finally: + self.conn.close() ack = struct.unpack('>B', data)[0] return ack == ACK def get_phase_lock(self, synth): - """ - Get phase lock status + """Get phase lock status. @param synth : synthesizer base address @type synth : int @@ -405,18 +558,23 @@ def get_phase_lock(self, synth): """ self.conn.open() data = struct.pack('>B', 0x86 | synth) - self.conn.write(data) - data = self.conn.read(1) - checksum = self.conn.read(1) - self.conn.close() - #_verify_checksum(data, checksum) - mask = (synth << 1) or 0x20 + try: + self.conn.write(data) + data = self.conn.read(1) + checksum = self.conn.read(1) + finally: + self.conn.close() + if self.do_checksum: + _verify_checksum(data, checksum) + if synth == SYNTH_A: + mask = 1 << 4 + else: + mask = 1 << 5 lock = struct.unpack('>B', data)[0] & mask return lock > 0 def get_label(self, synth): - """ - Get synthesizer label or name + """Get synthesizer label or name. @param synth : synthesizer base address @type synth : int @@ -425,21 +583,23 @@ def get_label(self, synth): """ self.conn.open() data = struct.pack('>B', 0x82 | synth) - self.conn.write(data) - data = self.conn.read(16) - checksum = self.conn.read(1) - self.conn.close() - #_verify_checksum(data, checksum) + try: + self.conn.write(data) + data = self.conn.read(16) + checksum = self.conn.read(1) + finally: + self.conn.close() + if self.do_checksum: + _verify_checksum(data, checksum) return data def set_label(self, synth, label): - """ - Set synthesizer label or name + """Set synthesizer label or name. @param synth : synthesizer base address @type synth : int - @param label : up to 16 data of text + @param label : up to 16 bytes of text @type label : str @return: True if success (bool) @@ -447,39 +607,52 @@ def set_label(self, synth, label): self.conn.open() data = struct.pack('>B16s', 0x02 | synth, label) checksum = _generate_checksum(data) - self.conn.write(data + checksum) - data = self.conn.read(1) - self.conn.close() + try: + self.conn.write(data + checksum) + data = self.conn.read(1) + finally: + self.conn.close() ack = struct.unpack('>B', data)[0] return ack == ACK def flash(self): - """ - Flash current settings for both synthesizers into non-volatile memory. + """Flash current settings for both synthesizers into non-volatile + memory. + + The next time the board is powered up, the registers will be set to the + values in the non-volatile flash memory. If the board is powered down + before the write flash command command is issued, all the data in the + registers will be lost. @return: True if success (bool) """ self.conn.open() data = struct.pack('>B', 0x40) checksum = _generate_checksum(data) - self.conn.write(data + checksum) - data = self.conn.read(1) - self.conn.close() + try: + self.conn.write(data + checksum) + data = self.conn.read(1) + finally: + self.conn.close() ack = struct.unpack('>B', data)[0] return ack == ACK def _get_epdf(self, synth): - """ - Returns effective phase detector frequency. + """Returns effective phase detector frequency. This is the reference frequency with options applied. + + @param synth : synthesizer base address + @type synth : int + + @return: frequency """ reference = self.get_reference() / 1e6 double, half, divider, _ = self.get_options(synth) - if(double): + if (double): reference *= 2.0 - if(half): + if (half): reference /= 2.0 - if(divider > 1): + if (divider > 1): reference /= divider return reference