diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 6741060f4..d635fb592 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -2,7 +2,7 @@ name: C/C++ CI on: push: - branches: [ master ] + branches: [ master, feature/* ] pull_request: branches: [ master ] @@ -21,6 +21,7 @@ jobs: sudo apt install libusb-1.0-0-dev sudo apt install libgpiod-dev sudo apt install grep + sudo apt install python3-dev python3-pytest - name: Install dependencies on macOS if: runner.os == 'macOS' run: | @@ -38,7 +39,11 @@ jobs: - name: README Debug run: grep README Makefile.am - name: configure - run: ./configure + if: runner.os != 'macOS' + run: ./configure --with-python-binding + - name: configure on macOS + if: runner.os == 'macOS' + run: ./configure --without-python-binding - name: make run: make -j 4 - name: make check diff --git a/README.developer b/README.developer index 1c1a7c27c..bdca0f715 100644 --- a/README.developer +++ b/README.developer @@ -285,6 +285,7 @@ Optional, but highly recommended: * perl devel # h2xs * tcl devel # tcltk-depends * python devel # python-config +* pytest * lua devel * libxml2 devel # xml2-config --version * libgd2 devel # gdlib-config --version (rigmatrix) @@ -440,6 +441,46 @@ as above and once the run is complete: The resulting generated document in the 'latex' directory is 'refman.pdf'. +1.3.2 Automated tests + +Automated tests are executed running: + + make check + +The makefiles run the simple tests with automake. +The make variable TESTS contains the tests to be run and the variables +check_PROGRAMS and check_SCRIPTS contain the executables needed to run the +tests that aren't built by make all. +For more information see the automake manual at +https://www.gnu.org/software/automake/manual/html_node/Scripts_002dbased-Testsuites.html + +1.3.2.1 C tests + +Tests written in C are available in the tests/ directory. They are run with: + + make -C tests/ check + +1.3.2.2 Python tests with pytest + +Tests written in Python are available in the bindings/python directory if +Hamlib is configured to build the Python bindings and if pytest is installed, +eg.: + + ./configure --with-python-binding --enable-pytest + +They are run with: + + make -C bindings/ check + +The Python scripts consist in handwritten tests, meant to test realistic use +cases, and autogenerated tests, meant to detect unwanted changes in the +bindings. When a public symbol is added to the bindings or removed, the +autogenerated tests must be updated: + + make -C bindings/ generate-pytests + +and the handwritten tests should be updated to reflect the change. + 1.4. Feedback diff --git a/bindings/Makefile.am b/bindings/Makefile.am index 11ecea1ec..41b44a92a 100644 --- a/bindings/Makefile.am +++ b/bindings/Makefile.am @@ -9,7 +9,7 @@ AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(top_srcdir)/include -I$(top_srcdir)/bindings \ AM_CFLAGS = @AM_CPPFLAGS@ -fno-strict-aliasing AM_CXXFLAGS = -O2 -SWGFILES = hamlib.swg ignore.swg rig.swg rotator.swg amplifier.swg +SWGFILES = hamlib.swg ignore.swg rig.swg rotator.swg amplifier.swg macros.swg SWGDEP = \ $(top_srcdir)/include/hamlib/rig.h \ @@ -21,7 +21,13 @@ SWGDEP = \ $(SWGFILES) EXTRA_DIST = $(SWGFILES) \ - Makefile.PL perltest.pl tcltest.tcl.in pytest.py py3test.py \ + Makefile.PL perltest.pl tcltest.tcl.in \ + python/generate_tests.py \ + python/test_Hamlib_Amp_class.py \ + python/test_Hamlib_class.py \ + python/test_Hamlib_Rig_class.py \ + python/test_Hamlib_Rot_class.py \ + python/test_startup.py \ luatest.lua README.python exampledir = $(docdir)/examples @@ -95,16 +101,6 @@ pyexec_ltlib = _Hamlib.la MOSTLYCLEANFILES += hamlibpy_wrap.c Hamlib.py *.pyc BUILT_SOURCES += hamlibpy_wrap.c Hamlib.py -# Set by AM_CONDITIONAL in configure.ac -if PYVER_3 -pytest = py3test.py -else !PYVER_3 -pytest = pytest.py -endif -# PYVER_3 - -example_DATA += $(pytest) - nodist_pyexec_PYTHON = Hamlib.py nodist__Hamlib_la_SOURCES = hamlibpy_wrap.c @@ -115,10 +111,18 @@ _Hamlib_la_LTLIBRARIES = $(pyexec_ltlib) all-py: $(pyexec_ltlib) -check-py: all-py +check-py: all-py pytest-${enable_pytest} + +generate-pytests: all-py $(AM_V_at)PYTHONPATH=$(builddir):$(builddir)/.libs \ - $(PYTHON) $(srcdir)/$(pytest) \ - || echo "Python test failed" 1>&2 + $(PYTHON) "$(srcdir)/python/generate_tests.py" "$(srcdir)/python/" + +pytest-yes: + $(AM_V_at)PYTHONPATH=$(builddir):$(builddir)/.libs \ + $(PYTEST) --capture=no $(srcdir)/ + +pytest-no: + @echo "Skipping pytest run" Hamlib.py: hamlibpy_wrap.c diff --git a/bindings/amplifier.swg b/bindings/amplifier.swg index 3d445361f..a22a02765 100644 --- a/bindings/amplifier.swg +++ b/bindings/amplifier.swg @@ -19,6 +19,17 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ +%{ +#include +%} + +%immutable amp_caps::model_name; +%immutable amp_caps::mfg_name; +%immutable amp_caps::version; +%immutable amp_caps::copyright; + +%include +%include %inline %{ @@ -43,7 +54,7 @@ typedef struct Amp { { self->error_status = amp_##f(self->amp, _##t1##_1, _##t2##_2); } #define AMPMETHOD3(f, t1, t2, t3) void f (t1 _##t1##_1, t2 _##t2##_2, t3 _##t3##_3) \ { self->error_status = amp_##f(self->amp, _##t1##_1, _##t2##_2, t3 _##t3##_3); } -#define AMPMETHOD4(f, t1, t2, t3, t4) void f (t1 _##t1##_1, t2 _##t2##_3, t3 _##t3##_3, ##t4##_4) \ +#define AMPMETHOD4(f, t1, t2, t3, t4) void f (t1 _##t1##_1, t2 _##t2##_3, t3 _##t3##_3, t4 ##t4##_4) \ { self->error_status = amp_##f(self->amp, _##t1##_1, _##t2##_3, t3 _##t3##_3, ##t4##_4); } %extend Amp { @@ -104,7 +115,7 @@ typedef struct Amp { /* * declare wrapper method with one output argument besides AMP* */ -#define AMPMETHOD1VGET(f, t1) t1 f \ +#define AMPMETHOD1GET(f, t1) t1 f () \ { t1 _##t1; self->error_status = amp_##f(self->amp, &_##t1); return _##t1; } @@ -126,6 +137,8 @@ typedef struct Amp { return s; } + AMPMETHOD1GET(get_freq, freq_t) + const char * get_info(void) { const char *s; s = amp_get_info(self->amp); @@ -133,15 +146,22 @@ typedef struct Amp { return s; } -}; -%{ + AMPMETHOD1GET(get_powerstat, powerstat_t) - void Amp_get_freq(Amp *self, freq_t *freq) + PyObject * get_level(setting_t level) { - self->error_status = amp_get_freq(self->amp, freq); + value_t val; + + self->error_status = amp_get_level(self->amp, level, &val); + if (self->error_status != RIG_OK) + return Py_None; + + if (AMP_LEVEL_IS_FLOAT(level)) + return PyFloat_FromDouble(val.f); + else if (AMP_LEVEL_IS_STRING(level)) + return PyUnicode_FromString(val.s); + + return PyLong_FromLong(val.i); } - void Amp_get_powerstat(Amp *self, powerstat_t *status) - { - self->error_status = amp_get_powerstat(self->amp, status); - } -%} + +}; diff --git a/bindings/hamlib.swg b/bindings/hamlib.swg index 05284c282..04f3968b5 100644 --- a/bindings/hamlib.swg +++ b/bindings/hamlib.swg @@ -29,9 +29,6 @@ #include -#include -#include -#include //mdblack98 -- Commented this out -- is anybody using the functions in misc.h? //If so, then those functions should be moved to rig.h //#include "misc.h" @@ -40,6 +37,17 @@ %} +/* + * symbols that shouldn't be changed + */ +%immutable clone_combo_get; +%immutable clone_combo_set; +%immutable confparams::dflt; +%immutable confparams::label; +%immutable confparams::name; +%immutable confparams::tooltip; +%immutable cs; + /* * symbols that won't be wrapped */ @@ -200,34 +208,8 @@ #endif #endif -%immutable confparams::name; -%immutable confparams::label; -%immutable confparams::tooltip; -%immutable confparams::dflt; -%immutable cs; -%immutable rig_caps::model_name; -%immutable rig_caps::mfg_name; -%immutable rig_caps::version; -%immutable rig_caps::copyright; -%immutable hamlib_version2; -%immutable hamlib_copyright2; - -%immutable clone_combo_set; -%immutable clone_combo_get; - -%immutable rot_caps::model_name; -%immutable rot_caps::mfg_name; -%immutable rot_caps::version; -%immutable rot_caps::copyright; - %include -%include -%include - -%include -%include - %inline { typedef const char * const_char_string; } diff --git a/bindings/ignore.swg b/bindings/ignore.swg index 258b7677c..d09b1a232 100644 --- a/bindings/ignore.swg +++ b/bindings/ignore.swg @@ -1,213 +1,103 @@ -/* unsupported Hamlib's calls */ +/* Unsupported Hamlib's methods, properties and constants */ -/* useless macros */ -%ignore _RIG_H; -%ignore _RIGLIST_H; -%ignore _ROTLIST_H; -%ignore _ROTATOR_H; -%ignore RIG_DUMMY; -%ignore RIG_BACKEND_DUMMY; -%ignore RIG_YAESU; -%ignore RIG_BACKEND_YAESU; -%ignore RIG_KENWOOD; -%ignore RIG_BACKEND_KENWOOD; -%ignore RIG_ICOM; -%ignore RIG_BACKEND_ICOM; -%ignore RIG_PCR; -%ignore RIG_BACKEND_PCR; -%ignore RIG_AOR; -%ignore RIG_BACKEND_AOR; -%ignore RIG_JRC; -%ignore RIG_BACKEND_JRC; -%ignore RIG_RADIOSHACK; -%ignore RIG_BACKEND_RADIOSHACK; -%ignore RIG_UNIDEN; -%ignore RIG_BACKEND_UNIDEN; -%ignore RIG_DRAKE; -%ignore RIG_BACKEND_DRAKE; -%ignore RIG_LOWE; -%ignore RIG_BACKEND_LOWE; -%ignore RIG_RACAL; -%ignore RIG_BACKEND_RACAL; -%ignore RIG_WJ; -%ignore RIG_BACKEND_WJ; -%ignore RIG_EK; -%ignore RIG_BACKEND_EK; -%ignore RIG_SKANTI; -%ignore RIG_BACKEND_SKANTI; -%ignore RIG_PRM80; -%ignore RIG_BACKEND_PRM80; -%ignore RIG_WINRADIO; -%ignore RIG_BACKEND_WINRADIO; -%ignore RIG_TENTEC; -%ignore RIG_BACKEND_TENTEC; -%ignore RIG_ALINCO; -%ignore RIG_BACKEND_ALINCO; -%ignore RIG_KACHINA; -%ignore RIG_BACKEND_KACHINA; -%ignore RIG_RPC; -%ignore RIG_BACKEND_RPC; -%ignore RIG_GNURADIO; -%ignore RIG_BACKEND_GNURADIO; -%ignore RIG_MICROTUNE; -%ignore RIG_BACKEND_MICROTUNE; -%ignore RIG_TAPR; -%ignore RIG_BACKEND_TAPR; -%ignore RIG_FLEXRADIO; -%ignore RIG_BACKEND_FLEXRADIO; -%ignore RIG_RFT; -%ignore RIG_BACKEND_RFT; -%ignore RIG_KIT; -%ignore RIG_BACKEND_KIT; -%ignore RIG_TUNER; -%ignore RIG_BACKEND_TUNER; -%ignore RIG_RS; -%ignore RIG_BACKEND_RS; -%ignore RIG_ADAT; -%ignore RIG_BACKEND_ADAT; -%ignore RIG_ICMARINE; -%ignore RIG_BACKEND_ICMARINE; +// remove defines from Hamlib class +%rename("$ignore", regexmatch$name="^_") ""; +%rename("$ignore", regexmatch$name="^AMP_") ""; +%rename("$ignore", regexmatch$name="^RIG_") ""; +%rename("$ignore", regexmatch$name="^ROT_") ""; -%ignore ROT_DUMMY; -%ignore ROT_BACKEND_DUMMY; -%ignore ROT_RPC; -%ignore ROT_BACKEND_RPC; -%ignore ROT_EASYCOMM; -%ignore ROT_BACKEND_EASYCOMM; -%ignore ROT_FODTRACK; -%ignore ROT_BACKEND_FODTRACK; -%ignore ROT_ROTOREZ; -%ignore ROT_BACKEND_ROTOREZ; -%ignore ROT_SARTEK; -%ignore ROT_BACKEND_SARTEK; -%ignore ROT_GS232A; -%ignore ROT_BACKEND_GS232A; -%ignore ROT_KIT; -%ignore ROT_BACKEND_KIT; -%ignore ROT_HEATHKIT; -%ignore ROT_BACKEND_HEATHKIT; -%ignore ROT_SPID; -%ignore ROT_BACKEND_SPID; -%ignore ROT_M2; -%ignore ROT_BACKEND_M2; -%ignore ROT_ARS; -%ignore ROT_BACKEND_ARS; -%ignore ROT_AMSAT; -%ignore ROT_BACKEND_AMSAT; -%ignore ROT_TS7400; -%ignore ROT_BACKEND_TS7400; -%ignore ROT_CELESTRON; -%ignore ROT_BACKEND_CELESTRON; +// re-add useful defines -%ignore PRIfreq; -%ignore SCNfreq; -%ignore FREQFMT; +// Amplifiers +%rename("%s", regexmatch$name="^AMP_FLAG_") ""; +%rename("%s", regexmatch$name="^AMP_LEVEL_") ""; +%rename("%s", regexmatch$name="^AMP_MODEL_") ""; +%rename("%s", regexmatch$name="^AMP_RESET_") ""; +%rename("%s", regexmatch$name="^AMP_TYPE_") ""; -%ignore rig_open; -%ignore rig_set_freq; -%ignore rig_get_freq; -%ignore rig_set_mode; -%ignore rig_get_mode; -%ignore rig_set_vfo; -%ignore rig_get_vfo; -%ignore rig_set_ptt; -%ignore rig_get_ptt; -%ignore rig_get_dcd; -%ignore rig_set_rptr_shift; -%ignore rig_get_rptr_shift; -%ignore rig_set_rptr_offs; -%ignore rig_get_rptr_offs; -%ignore rig_set_ctcss_tone; -%ignore rig_get_ctcss_tone; -%ignore rig_set_dcs_code; -%ignore rig_get_dcs_code; -%ignore rig_set_ctcss_sql; -%ignore rig_get_ctcss_sql; -%ignore rig_set_dcs_sql; -%ignore rig_get_dcs_sql; -%ignore rig_set_split_freq; -%ignore rig_get_split_freq; -%ignore rig_set_split_mode; -%ignore rig_get_split_mode; -%ignore rig_set_split; -%ignore rig_get_split; -%ignore rig_set_rit; -%ignore rig_get_rit; -%ignore rig_set_xit; -%ignore rig_get_xit; -%ignore rig_set_ts; -%ignore rig_get_ts; -%ignore rig_power2mW; -%ignore rig_mW2power; -%ignore rig_get_resolution; -%ignore rig_set_level; -%ignore rig_get_level; -%ignore rig_set_parm; -%ignore rig_get_parm; -%ignore rig_set_conf; -%ignore rig_get_conf; -%ignore rig_set_powerstat; -%ignore rig_get_powerstat; -%ignore rig_reset; -%ignore rig_set_ext_level; -%ignore rig_get_ext_level; -%ignore rig_set_ext_parm; -%ignore rig_get_ext_parm; -%ignore rig_ext_level_foreach; -%ignore rig_ext_parm_foreach; -%ignore rig_token_lookup; -%ignore rig_close; -%ignore rig_cleanup; -%ignore rig_probe; -%ignore rig_set_ant; -%ignore rig_get_ant; -%ignore rig_has_get_level; -%ignore rig_has_set_level; -%ignore rig_has_get_parm; -%ignore rig_has_set_parm; -%ignore rig_has_get_func; -%ignore rig_has_set_func; -%ignore rig_set_func; -%ignore rig_get_func; -%ignore rig_send_dtmf; -%ignore rig_recv_dtmf; -%ignore rig_send_morse; -%ignore rig_set_bank; -%ignore rig_set_mem; -%ignore rig_get_mem; -%ignore rig_vfo_op; -%ignore rig_has_vfo_op; -%ignore rig_scan; -%ignore rig_has_scan; -%ignore rig_set_channel; -%ignore rig_get_channel; -%ignore rig_set_trn; -%ignore rig_get_trn; -%ignore rig_set_freq_callback; -%ignore rig_set_mode_callback; -%ignore rig_set_vfo_callback; -%ignore rig_set_ptt_callback; -%ignore rig_set_dcd_callback; -%ignore rig_set_pltune_callback; -%ignore rig_get_info; -%ignore rig_passband_normal; -%ignore rig_passband_narrow; -%ignore rig_passband_wide; -%ignore rig_get_vfo_info; +// Rigs +%rename("%s", regexmatch$name="^RIG_AGC_") ""; +%rename("%s", regexmatch$name="^RIG_ANN_") ""; +%rename("%s", regexmatch$name="^RIG_ANT_") ""; +%rename("%s", regexmatch$name="^RIG_BANDSELECT_") ""; +%rename("%s", regexmatch$name="^RIG_BAND_") ""; +%rename("%s") RIG_BUSBUSY; +%rename("%s") RIG_BUSERROR; +%rename("%s", regexmatch$name="^RIG_CAPS_") ""; +%rename("%s", regexmatch$name="^RIG_CHFLAG_") ""; +%rename("%s", regexmatch$name="^RIG_CLIENT_") ""; +%rename("%s", regexmatch$name="^RIG_COMM_") ""; +%rename("%s", regexmatch$name="^RIG_CONF_") ""; +%rename("%s", regexmatch$name="^RIG_COOKIE_") ""; +%rename("%s", regexmatch$name="^RIG_DCD_") ""; +%rename("%s", regexmatch$name="^RIG_DEBUG_") ""; +%rename("%s", regexmatch$name="^RIG_E") ""; +%rename("%s", regexmatch$name="^RIG_FLAG_") ""; +%rename("%s", regexmatch$name="^RIG_FLT_") ""; +%rename("%s", regexmatch$name="^RIG_FUNCTION_") ""; +%rename("%s", regexmatch$name="^RIG_FUNC_") ""; +%rename("%s", regexmatch$name="^RIG_HANDSHAKE_") ""; +%rename("%s", regexmatch$name="^RIG_KEYERTYPE_") ""; +%rename("%s", regexmatch$name="^RIG_LEVEL_") ""; +%rename("%s", regexmatch$name="^RIG_MEM_CAPS_") ""; +%rename("%s", regexmatch$name="^RIG_METER_") ""; +%rename("%s", regexmatch$name="^RIG_MODEL_") ""; +%rename("%s", regexmatch$name="^RIG_MODE_") ""; +%rename("%s", regexmatch$name="^RIG_MTYPE_") ""; +%rename("%s", regexmatch$name="^RIG_MULTICAST_") ""; +%rename("%s", regexmatch$name="^RIG_OP_") ""; +%rename("%s") RIG_OK; +%rename("%s", regexmatch$name="^RIG_PARITY_") ""; +%rename("%s", regexmatch$name="^RIG_PARM_") ""; +%rename("%s", regexmatch$name="^RIG_PORT_") ""; +%rename("%s", regexmatch$name="^RIG_POWER_") ""; +%rename("%s", regexmatch$name="^RIG_PTRX_") ""; +%rename("%s", regexmatch$name="^RIG_PTT_") ""; +%rename("%s", regexmatch$name="^RIG_RESET_") ""; +%rename("%s", regexmatch$name="^RIG_RPT_SHIFT_") ""; +%rename("%s", regexmatch$name="^RIG_SCAN_") ""; +%rename("%s", regexmatch$name="^RIG_SETTING_") ""; +%rename("%s", regexmatch$name="^RIG_SIGNAL_") ""; +%rename("%s", regexmatch$name="^RIG_SPECTRUM_MODE_") ""; +%rename("%s", regexmatch$name="^RIG_SPLIT_") ""; +%rename("%s", regexmatch$name="^RIG_STATUS_") ""; +%rename("%s", regexmatch$name="^RIG_TARGETABLE_") ""; +%rename("%s", regexmatch$name="^RIG_TRN_") ""; +%rename("%s", regexmatch$name="^RIG_TS_") ""; +%rename("%s", regexmatch$name="^RIG_TYPE_") ""; +%rename("%s", regexmatch$name="^RIG_VFO_") ""; +%ignore RIG_EK; // an internal macro +%ignore RIG_ELAD; // an internal macro -%ignore rot_open; -%ignore rot_close; -%ignore rot_cleanup; -%ignore rot_set_conf; -%ignore rot_get_conf; -%ignore rot_set_position; -%ignore rot_get_position; -%ignore rot_stop; -%ignore rot_park; -%ignore rot_reset; -%ignore rot_move; -%ignore rot_get_info; +// Rotators +%rename("%s", regexmatch$name="^ROT_FLAG_") ""; +%rename("%s", regexmatch$name="^ROT_FUNC_") ""; +%rename("%s", regexmatch$name="^ROT_LEVEL_") ""; +%rename("%s", regexmatch$name="^ROT_MODEL_") ""; +%rename("%s", regexmatch$name="^ROT_MOVE_") ""; +%rename("%s", regexmatch$name="^ROT_PARM_") ""; +%rename("%s", regexmatch$name="^ROT_RESET_") ""; +%rename("%s", regexmatch$name="^ROT_SPEED_") ""; +%rename("%s", regexmatch$name="^ROT_STATUS_") ""; +%rename("%s", regexmatch$name="^ROT_TYPE_") ""; + +// remove methods from Hamlib class +%rename("$ignore", regexmatch$name="^amp_", %$isfunction) ""; +%rename("$ignore", regexmatch$name="^rig_", %$isfunction) ""; +%rename("$ignore", regexmatch$name="^rot_", %$isfunction) ""; +// re-add some methods +%rename("%s") rig_set_debug; +%rename("%s", regexmatch$name="^rig_str", %$isfunction) ""; + +// remove typedefs +%ignore s_rig; +%ignore s_rot; + +%ignore amp; +%ignore debugmsgsave; +%ignore debugmsgsave2; +%ignore debugmsgsave3; %ignore hamlib_copyright2; %ignore hamlib_version2; %ignore macro_name; @@ -216,8 +106,10 @@ %ignore fd_sync_error_write; %ignore fd_sync_error_read; - %ignore FIFO_RIG; +%ignore FREQFMT; +%ignore PRIfreq; +%ignore SCNfreq; #ifdef SWIGLUA %ignore Rig::set_level(setting_t,int,vfo_t); diff --git a/bindings/macros.swg b/bindings/macros.swg new file mode 100644 index 000000000..9f16b9f30 --- /dev/null +++ b/bindings/macros.swg @@ -0,0 +1,92 @@ +/* +Common SWIG macros +*/ + +/* + * Macro GET_TOKEN + * + * function_prefix: + * the first part of the function name (eg. one of: amp_ rig_ rot_) + * + * function_name: + * the name that will be seen in the bindings and that creates a valid Hamlib function + * name when appended to the function_prefix + * + * class_pointer: + * the pointer to the instance of the class (eg. one of self->amp self->rig self->rot) + */ +%define GET_TOKEN(function_prefix, function_name, class_pointer) +PyObject * ##function_name(hamlib_token_t token) +{ + int value; + + self->error_status = ##function_prefix ##function_name(##class_pointer, token, &value); + if (self->error_status != RIG_OK) + return Py_None; + + return PyLong_FromLong(value); +} +%enddef + +/* + * Macro GET_VALUE_T + * + * function_prefix: + * the first part of the function name (eg. one of: amp_ rig_ rot_) + * + * function_name: + * the name that creates a valid Hamlib function name when appended to the + * function_prefix + * + * class_pointer: + * the pointer to the instance of the class (eg. one of self->amp self->rig self->rot) + * + * level_prefix: + * the prefix of the macro that checks the datatype (eg. one of AMP_ RIG_ ROT_) + */ +%define GET_VALUE_T(function_prefix, function_name, class_pointer, level_prefix) +PyObject * ##function_name(hamlib_token_t token) +{ + value_t value; + + self->error_status = ##function_prefix ##function_name(##class_pointer, token, &value); + if (self->error_status != RIG_OK) + return Py_None; + +#if defined(##level_prefix LEVEL_IS_FLOAT) + if (##level_prefix LEVEL_IS_FLOAT(token)) + return PyFloat_FromDouble(value.f); +#endif + +#if defined(##level_prefix LEVEL_IS_STRING) + if (##level_prefix LEVEL_IS_STRING(token)) + return PyUnicode_FromString(value.s); +#endif + + return PyLong_FromLong(value.i); +} +%enddef + +/* + * Macro ROT_GET_LONG + * + * function_name: + * the name that will be seen in the bindings when appended to get_ + * and that creates a valid Hamlib function when appended to rot_get_ + * eg. ROT_GET_LONG(func) creates Rot.get_func() that calls rot_get_func() + */ +%define ROT_GET_LONG(function_name) + GET_TOKEN(rot_, get_ ##function_name, self->rot) +%enddef + +/* +* Macro ROT_GET_VALUE_T +* +* function_name: +* the name that will be seen in the bindings when appended to get_ +* and that creates a valid Hamlib function when appended to rot_get_ +* eg. ROT_GET_VALUE_T(level) creates Rot.get_level() that calls rot_get_level() +*/ +%define ROT_GET_VALUE_T(function_name) + GET_VALUE_T(rot_, get_ ##function_name, self->rot, ROT_) +%enddef diff --git a/bindings/py3test.py b/bindings/py3test.py deleted file mode 100755 index 52f85e1e7..000000000 --- a/bindings/py3test.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -import sys -# Change this path to match your "make install" path -sys.path.append('/usr/local/lib/python3.10/site-packages') - -## Uncomment to run this script from an in-tree build (or adjust to the -## build directory) without installing the bindings. -#sys.path.append ('.') -#sys.path.append ('.libs') - -import Hamlib - -def StartUp(): - """Simple script to test the Hamlib.py module with Python3.""" - - print("%s: Python %s; %s\n" \ - % (sys.argv[0], sys.version.split()[0], Hamlib.cvar.hamlib_version)) - - Hamlib.rig_set_debug(Hamlib.RIG_DEBUG_NONE) - - # Init RIG_MODEL_DUMMY - my_rig = Hamlib.Rig(Hamlib.RIG_MODEL_DUMMY) - my_rig.set_conf("rig_pathname", "/dev/Rig") - my_rig.set_conf("retry", "5") - - my_rig.open () - - rpath = my_rig.get_conf("rig_pathname") - retry = my_rig.get_conf("retry") - - print("status(str):\t\t%s" % Hamlib.rigerror(my_rig.error_status)) - print("get_conf:\t\tpath = %s, retry = %s" \ - % (rpath, retry)) - - my_rig.set_freq(Hamlib.RIG_VFO_B, 5700000000) - my_rig.set_vfo(Hamlib.RIG_VFO_B) - - print("freq:\t\t\t%s" % my_rig.get_freq()) - - my_rig.set_freq(Hamlib.RIG_VFO_A, 145550000) - (mode, width) = my_rig.get_mode(Hamlib.RIG_VFO_A) - - print("mode:\t\t\t%s\nbandwidth:\t\t%s" % (Hamlib.rig_strrmode(mode), width)) - - my_rig.set_mode(Hamlib.RIG_MODE_CW) - (mode, width) = my_rig.get_mode() - - print("mode:\t\t\t%s\nbandwidth:\t\t%s" % (Hamlib.rig_strrmode(mode), width)) - - print("Backend copyright:\t%s" % my_rig.caps.copyright) - print("Model:\t\t\t%s" % my_rig.caps.model_name) - print("Manufacturer:\t\t%s" % my_rig.caps.mfg_name) - print("Backend version:\t%s" % my_rig.caps.version) - print("Backend status:\t\t%s" % Hamlib.rig_strstatus(my_rig.caps.status)) - print("Rig info:\t\t%s" % my_rig.get_info()) - - my_rig.set_level("VOXDELAY", 1) - - print("VOX delay:\t\t%s" % my_rig.get_level_i("VOXDELAY")) - - my_rig.set_level(Hamlib.RIG_LEVEL_VOXDELAY, 5) - - print("VOX delay:\t\t%s" % my_rig.get_level_i(Hamlib.RIG_LEVEL_VOXDELAY)) - - af = 12.34 - - print("Setting AF to %0.2f...." % (af)) - - my_rig.set_level("AF", af) - - print("status:\t\t\t%s - %s" % (my_rig.error_status, - Hamlib.rigerror(my_rig.error_status))) - - print("AF level:\t\t%0.2f" % my_rig.get_level_f(Hamlib.RIG_LEVEL_AF)) - print("Power level:\t\t%0.2f" % my_rig.get_level_f(Hamlib.RIG_LEVEL_RFPOWER_METER)) - print("Power level Watts:\t\t%0.2f" % my_rig.get_level_f(Hamlib.RIG_LEVEL_RFPOWER_METER_WATTS)) - print("strength:\t\t%s" % my_rig.get_level_i(Hamlib.RIG_LEVEL_STRENGTH)) - print("status:\t\t\t%s" % my_rig.error_status) - print("status(str):\t\t%s" % Hamlib.rigerror(my_rig.error_status)) - - chan = Hamlib.channel(Hamlib.RIG_VFO_B) - my_rig.get_channel(chan,1) - - print("get_channel status:\t%s" % my_rig.error_status) - print("VFO:\t\t\t%s, %s" % (Hamlib.rig_strvfo(chan.vfo), chan.freq)) - print("Attenuators:\t\t%s" % my_rig.caps.attenuator) - # Can't seem to get get_vfo_info to work - #(freq, width, mode, split) = my_rig.get_vfo_info(Hamlib.RIG_VFO_A,freq,width,mode,split) - #print("Rig vfo_info:\t\tfreq=%s, mode=%s, width=%s, split=%s" % (freq, mode, width, split)) - print("\nSending Morse, '73'") - - my_rig.send_morse(Hamlib.RIG_VFO_A, "73") - my_rig.close() - - print("\nSome static functions:") - - err, lon1, lat1 = Hamlib.locator2longlat("IN98XC") - err, lon2, lat2 = Hamlib.locator2longlat("DM33DX") - err, loc1 = Hamlib.longlat2locator(lon1, lat1, 3) - err, loc2 = Hamlib.longlat2locator(lon2, lat2, 3) - - print("Loc1:\t\tIN98XC -> %9.4f, %9.4f -> %s" % (lon1, lat1, loc1)) - print("Loc2:\t\tDM33DX -> %9.4f, %9.4f -> %s" % (lon2, lat2, loc2)) - - err, dist, az = Hamlib.qrb(lon1, lat1, lon2, lat2) - longpath = Hamlib.distance_long_path(dist) - - print("Distance:\t%.3f km, azimuth %.2f, long path:\t%.3f km" \ - % (dist, az, longpath)) - - # dec2dms expects values from 180 to -180 - # sw is 1 when deg is negative (west or south) as 0 cannot be signed - err, deg1, mins1, sec1, sw1 = Hamlib.dec2dms(lon1) - err, deg2, mins2, sec2, sw2 = Hamlib.dec2dms(lat1) - - lon3 = Hamlib.dms2dec(deg1, mins1, sec1, sw1) - lat3 = Hamlib.dms2dec(deg2, mins2, sec2, sw2) - - print('Longitude:\t%4.4f, %4d° %2d\' %2d" %1s\trecoded: %9.4f' \ - % (lon1, deg1, mins1, sec1, ('W' if sw1 else 'E'), lon3)) - - print('Latitude:\t%4.4f, %4d° %2d\' %2d" %1s\trecoded: %9.4f' \ - % (lat1, deg2, mins2, sec2, ('S' if sw2 else 'N'), lat3)) - - my_rig.set_vfo_opt(0); - -if __name__ == '__main__': - StartUp() diff --git a/bindings/pytest.py b/bindings/pytest.py deleted file mode 100755 index b271834a5..000000000 --- a/bindings/pytest.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env python2 -# -*- coding: utf-8 -*- - -import sys - -## Uncomment to run this script from an in-tree build (or adjust to the -## build directory) without installing the bindings. -#sys.path.append ('.') -#sys.path.append ('.libs') - -import Hamlib - -def StartUp(verbose): - """Simple script to test the Hamlib.py module with Python2.""" - - print "%s: Python %s; %s\n" \ - % (sys.argv[0], sys.version.split()[0], Hamlib.cvar.hamlib_version) - - Hamlib.rig_set_debug([Hamlib.RIG_DEBUG_NONE, Hamlib.RIG_DEBUG_VERBOSE][verbose]) - - # Init RIG_MODEL_DUMMY - my_rig = Hamlib.Rig(Hamlib.RIG_MODEL_DUMMY) - my_rig.set_conf("rig_pathname", "/dev/Rig") - my_rig.set_conf("retry", "5") - - my_rig.open() - - rpath = my_rig.get_conf("rig_pathname") - retry = my_rig.get_conf("retry") - - print "status(str):\t\t", Hamlib.rigerror(my_rig.error_status) - print "get_conf:\t\tpath = %s, retry = %s" \ - % (rpath, retry) - - my_rig.set_freq(Hamlib.RIG_VFO_B, 5700000000) - my_rig.set_vfo(Hamlib.RIG_VFO_B) - - print "freq:\t\t\t", my_rig.get_freq() - - my_rig.set_freq(Hamlib.RIG_VFO_A, 145550000) - (mode, width) = my_rig.get_mode() - - print "mode:\t\t\t", Hamlib.rig_strrmode(mode), "\nbandwidth:\t\t", width - - my_rig.set_mode(Hamlib.RIG_MODE_CW) - (mode, width) = my_rig.get_mode() - - print "mode:\t\t\t", Hamlib.rig_strrmode(mode), "\nbandwidth:\t\t", width - - print "Backend copyright:\t", my_rig.caps.copyright - print "Model:\t\t\t", my_rig.caps.model_name - print "Manufacturer:\t\t", my_rig.caps.mfg_name - print "Backend version:\t", my_rig.caps.version - print "Backend status:\t\t", Hamlib.rig_strstatus(my_rig.caps.status) - print "Rig info:\t\t", my_rig.get_info() - - my_rig.set_level("VOXDELAY", 1) - - print "VOX delay:\t\t", my_rig.get_level_i("VOXDELAY") - - my_rig.set_level(Hamlib.RIG_LEVEL_VOXDELAY, 5) - - print "VOX delay:\t\t", my_rig.get_level_i(Hamlib.RIG_LEVEL_VOXDELAY) - - af = 12.34 - - print "Setting AF to %0.2f...." % (af) - - my_rig.set_level("AF", af) - - print "status:\t\t\t%s - %s" % (my_rig.error_status, - Hamlib.rigerror(my_rig.error_status)) - - print "AF level:\t\t%0.2f" % my_rig.get_level_f(Hamlib.RIG_LEVEL_AF) - print "strength:\t\t", my_rig.get_level_i(Hamlib.RIG_LEVEL_STRENGTH) - print "status:\t\t\t", my_rig.error_status - print "status(str):\t\t", Hamlib.rigerror(my_rig.error_status) - - chan = Hamlib.channel(Hamlib.RIG_VFO_B) - my_rig.get_channel(chan,1) - - print "get_channel status:\t", my_rig.error_status - print "VFO:\t\t\t", Hamlib.rig_strvfo(chan.vfo), ", ", chan.freq - print "Attenuators:\t\t", my_rig.caps.attenuator - print "\nSending Morse, '73'" - - my_rig.send_morse(Hamlib.RIG_VFO_A, "73") - my_rig.close () - - print "\nSome static functions:" - - err, lon1, lat1 = Hamlib.locator2longlat("IN98XC") - err, lon2, lat2 = Hamlib.locator2longlat("DM33DX") - err, loc1 = Hamlib.longlat2locator(lon1, lat1, 3) - err, loc2 = Hamlib.longlat2locator(lon2, lat2, 3) - - print "Loc1:\t\tIN98XC -> %9.4f, %9.4f -> %s" % (lon1, lat1, loc1) - print "Loc2:\t\tDM33DX -> %9.4f, %9.4f -> %s" % (lon2, lat2, loc2) - - err, dist, az = Hamlib.qrb(lon1, lat1, lon2, lat2) - longpath = Hamlib.distance_long_path(dist) - - print "Distance:\t%.3f km, azimuth %.2f, long path:\t%.3f km" \ - % (dist, az, longpath) - - # dec2dms expects values from 180 to -180 - # sw is 1 when deg is negative (west or south) as 0 cannot be signed - err, deg1, mins1, sec1, sw1 = Hamlib.dec2dms(lon1) - err, deg2, mins2, sec2, sw2 = Hamlib.dec2dms(lat1) - - lon3 = Hamlib.dms2dec(deg1, mins1, sec1, sw1) - lat3 = Hamlib.dms2dec(deg2, mins2, sec2, sw2) - - print 'Longitude:\t%4.4f, %4d° %2d\' %2d" %1s\trecoded: %9.4f' \ - % (lon1, deg1, mins1, sec1, ('W' if sw1 else 'E'), lon3) - - print 'Latitude:\t%4.4f, %4d° %2d\' %2d" %1s\trecoded: %9.4f' \ - % (lat1, deg2, mins2, sec2, ('S' if sw2 else 'N'), lat3) - - -if __name__ == '__main__': - StartUp([0,1]["-v" in sys.argv]) diff --git a/bindings/python/generate_tests.py b/bindings/python/generate_tests.py new file mode 100755 index 000000000..e7c79bd68 --- /dev/null +++ b/bindings/python/generate_tests.py @@ -0,0 +1,92 @@ +#! /bin/env python +""" +Creates tests for the Python bindings for Hamlib + +Running this script directly will use the installed bindings. +For an in-tree run use "make check", or set PYTHONPATH to point to +the directories containing Hamlib.py and _Hamlib.so. +""" +import inspect +import os +import sys + +import Hamlib + +# This class gets copied to the generated file with replacements +class TestClass: + """A pytest class for {object_name}""" + + @classmethod + def setup_class(cls): + """Common initialization before calling test methods""" + cls.actual_callables, cls.actual_properties = cls.generate_data({object_name}) + + def test_callables(self): + """Check that nothing was added or removed""" + expected_callables = {expected_callables} + assert expected_callables == self.actual_callables + + def test_properties(self): + """Check that nothing was added or removed""" + expected_properties = {expected_properties} + assert expected_properties == self.actual_properties + + @classmethod + def generate_data(cls, the_object): + """Extract callables and properties from the given object""" + callables = [] + properties = [] + for method_or_property in dir(the_object): + if not method_or_property.startswith("_"): + if callable(getattr(the_object, method_or_property)): + callables.append(method_or_property) + else: + properties.append(method_or_property) + + return callables, properties + + +def generate_file(object_name, callables, properties): + """Create a complete file for pytest""" + + file_header = '''#! /bin/env pytest +""" +Tests of the Python bindings for Hamlib + +DO NOT EDIT this autogenerated file, run "make generate-pytests" instead +""" +import Hamlib +''' + filename = f"test_{object_name.replace(".", "_")}_class.py" + class_source = inspect.getsource(TestClass) + expected_callables = repr(callables) + expected_properties = repr(properties) + + print(f"Generating {filename}") + with open(filename, "w", encoding='utf-8') as output_file: + print(file_header, file=output_file) + print(class_source.format( + object_name=object_name, + expected_callables=expected_callables, + expected_properties=expected_properties), + file=output_file, end='') + + +if __name__ == "__main__": + if len(sys.argv) == 2 and not sys.argv[1].startswith("-"): + os.chdir(sys.argv[1]) + else: + print("Usage: {sys.argv[0]} [OUTPUT_DIRECTORY]", file=sys.stderr) + sys.exit(1) + for the_object in ( + Hamlib, + Hamlib.Amp, + Hamlib.Rig, + Hamlib.Rot, + ): + try: + full_name = f"{the_object.__module__}.{the_object.__name__}" + except AttributeError: + full_name = the_object.__name__ + expected_callables, expected_properties = TestClass.generate_data(the_object) + generate_file(full_name, expected_callables, expected_properties) diff --git a/bindings/python/test_Hamlib_Amp_class.py b/bindings/python/test_Hamlib_Amp_class.py new file mode 100755 index 000000000..77a2dc0ae --- /dev/null +++ b/bindings/python/test_Hamlib_Amp_class.py @@ -0,0 +1,39 @@ +#! /bin/env pytest +""" +Tests of the Python bindings for Hamlib + +DO NOT EDIT this autogenerated file, run "make generate-pytests" instead +""" +import Hamlib + +class TestClass: + """A pytest class for Hamlib.Amp""" + + @classmethod + def setup_class(cls): + """Common initialization before calling test methods""" + cls.actual_callables, cls.actual_properties = cls.generate_data(Hamlib.Amp) + + def test_callables(self): + """Check that nothing was added or removed""" + expected_callables = ['close', 'get_conf', 'get_freq', 'get_info', 'get_level', 'get_powerstat', 'open', 'reset', 'set_conf', 'set_freq', 'set_powerstat', 'token_lookup'] + assert expected_callables == self.actual_callables + + def test_properties(self): + """Check that nothing was added or removed""" + expected_properties = ['caps', 'do_exception', 'error_status', 'state', 'thisown'] + assert expected_properties == self.actual_properties + + @classmethod + def generate_data(cls, the_object): + """Extract callables and properties from the given object""" + callables = [] + properties = [] + for method_or_property in dir(the_object): + if not method_or_property.startswith("_"): + if callable(getattr(the_object, method_or_property)): + callables.append(method_or_property) + else: + properties.append(method_or_property) + + return callables, properties diff --git a/bindings/python/test_Hamlib_Rig_class.py b/bindings/python/test_Hamlib_Rig_class.py new file mode 100755 index 000000000..7f5d440ac --- /dev/null +++ b/bindings/python/test_Hamlib_Rig_class.py @@ -0,0 +1,39 @@ +#! /bin/env pytest +""" +Tests of the Python bindings for Hamlib + +DO NOT EDIT this autogenerated file, run "make generate-pytests" instead +""" +import Hamlib + +class TestClass: + """A pytest class for Hamlib.Rig""" + + @classmethod + def setup_class(cls): + """Common initialization before calling test methods""" + cls.actual_callables, cls.actual_properties = cls.generate_data(Hamlib.Rig) + + def test_callables(self): + """Check that nothing was added or removed""" + expected_callables = ['close', 'ext_token_lookup', 'get_ant', 'get_chan_all', 'get_channel', 'get_conf', 'get_ctcss_sql', 'get_ctcss_tone', 'get_dcd', 'get_dcs_code', 'get_dcs_sql', 'get_ext_func', 'get_ext_level', 'get_ext_parm', 'get_freq', 'get_func', 'get_info', 'get_level', 'get_level_f', 'get_level_i', 'get_mem', 'get_mode', 'get_parm', 'get_parm_f', 'get_parm_i', 'get_powerstat', 'get_ptt', 'get_rit', 'get_rptr_offs', 'get_rptr_shift', 'get_split_freq', 'get_split_mode', 'get_split_vfo', 'get_trn', 'get_ts', 'get_vfo', 'get_vfo_info', 'get_xit', 'has_get_level', 'has_scan', 'has_set_func', 'has_set_parm', 'has_vfo_op', 'lookup_mem_caps', 'mem_count', 'open', 'passband_narrow', 'passband_normal', 'passband_wide', 'recv_dtmf', 'reset', 'scan', 'send_dtmf', 'send_morse', 'set_ant', 'set_bank', 'set_channel', 'set_conf', 'set_ctcss_sql', 'set_ctcss_tone', 'set_dcs_code', 'set_dcs_sql', 'set_ext_func', 'set_ext_level', 'set_ext_parm', 'set_freq', 'set_func', 'set_level', 'set_mem', 'set_mode', 'set_parm', 'set_powerstat', 'set_ptt', 'set_rit', 'set_rptr_offs', 'set_rptr_shift', 'set_split_freq', 'set_split_freq_mode', 'set_split_mode', 'set_split_vfo', 'set_trn', 'set_ts', 'set_vfo', 'set_vfo_opt', 'set_xit', 'token_lookup', 'vfo_op'] + assert expected_callables == self.actual_callables + + def test_properties(self): + """Check that nothing was added or removed""" + expected_properties = ['caps', 'do_exception', 'error_status', 'rig', 'state', 'thisown'] + assert expected_properties == self.actual_properties + + @classmethod + def generate_data(cls, the_object): + """Extract callables and properties from the given object""" + callables = [] + properties = [] + for method_or_property in dir(the_object): + if not method_or_property.startswith("_"): + if callable(getattr(the_object, method_or_property)): + callables.append(method_or_property) + else: + properties.append(method_or_property) + + return callables, properties diff --git a/bindings/python/test_Hamlib_Rot_class.py b/bindings/python/test_Hamlib_Rot_class.py new file mode 100755 index 000000000..107d07bee --- /dev/null +++ b/bindings/python/test_Hamlib_Rot_class.py @@ -0,0 +1,39 @@ +#! /bin/env pytest +""" +Tests of the Python bindings for Hamlib + +DO NOT EDIT this autogenerated file, run "make generate-pytests" instead +""" +import Hamlib + +class TestClass: + """A pytest class for Hamlib.Rot""" + + @classmethod + def setup_class(cls): + """Common initialization before calling test methods""" + cls.actual_callables, cls.actual_properties = cls.generate_data(Hamlib.Rot) + + def test_callables(self): + """Check that nothing was added or removed""" + expected_callables = ['close', 'get_conf', 'get_ext_func', 'get_ext_level', 'get_ext_parm', 'get_func', 'get_info', 'get_level', 'get_parm', 'get_position', 'move', 'open', 'park', 'reset', 'set_conf', 'set_ext_func', 'set_ext_level', 'set_ext_parm', 'set_func', 'set_level', 'set_parm', 'set_position', 'stop', 'token_lookup'] + assert expected_callables == self.actual_callables + + def test_properties(self): + """Check that nothing was added or removed""" + expected_properties = ['caps', 'do_exception', 'error_status', 'rot', 'state', 'thisown'] + assert expected_properties == self.actual_properties + + @classmethod + def generate_data(cls, the_object): + """Extract callables and properties from the given object""" + callables = [] + properties = [] + for method_or_property in dir(the_object): + if not method_or_property.startswith("_"): + if callable(getattr(the_object, method_or_property)): + callables.append(method_or_property) + else: + properties.append(method_or_property) + + return callables, properties diff --git a/bindings/python/test_Hamlib_class.py b/bindings/python/test_Hamlib_class.py new file mode 100755 index 000000000..663a6db98 --- /dev/null +++ b/bindings/python/test_Hamlib_class.py @@ -0,0 +1,39 @@ +#! /bin/env pytest +""" +Tests of the Python bindings for Hamlib + +DO NOT EDIT this autogenerated file, run "make generate-pytests" instead +""" +import Hamlib + +class TestClass: + """A pytest class for Hamlib""" + + @classmethod + def setup_class(cls): + """Common initialization before calling test methods""" + cls.actual_callables, cls.actual_properties = cls.generate_data(Hamlib) + + def test_callables(self): + """Check that nothing was added or removed""" + expected_callables = ['Amp', 'Rig', 'Rot', 'add2debugmsgsave', 'amp_caps', 'amp_state', 'azimuth_long_path', 'cal_table', 'cal_table_float', 'cal_table_float_table', 'cal_table_table', 'chan_list', 'channel', 'channelArray', 'channel_cap', 'confparams', 'confparams_u', 'confparams_u_c', 'confparams_u_n', 'dec2dmmm', 'dec2dms', 'deferred_config_header', 'deferred_config_item', 'distance_long_path', 'dmmm2dec', 'dms2dec', 'ext_list', 'filter_list', 'freq_range_t', 'gran', 'hamlib_port_deprecated_parm', 'hamlib_port_deprecated_parm_cm108', 'hamlib_port_deprecated_parm_gpio', 'hamlib_port_deprecated_parm_parallel', 'hamlib_port_deprecated_parm_serial', 'hamlib_port_deprecated_parm_usb', 'hamlib_port_deprecated_post_write_date', 'hamlib_port_deprecated_type', 'hamlib_port_parm', 'hamlib_port_parm_cm108', 'hamlib_port_parm_gpio', 'hamlib_port_parm_parallel', 'hamlib_port_parm_serial', 'hamlib_port_parm_usb', 'hamlib_port_post_write_date', 'hamlib_port_t', 'hamlib_port_t_deprecated', 'hamlib_port_type', 'hl_usleep', 'locator2longlat', 'longlat2locator', 'multicast_s', 'netrigctl_get_vfo_mode', 'qrb', 'rig_cache_deprecated', 'rig_callbacks', 'rig_caps', 'rig_set_debug', 'rig_spectrum_avg_mode', 'rig_spectrum_line', 'rig_spectrum_scope', 'rig_state', 'rig_state_deprecated', 'rig_stragclevel', 'rig_strcommstatus', 'rig_strfunc', 'rig_strlevel', 'rig_strmtype', 'rig_strparm', 'rig_strptrshift', 'rig_strrmode', 'rig_strrmodes', 'rig_strscan', 'rig_strspectrummode', 'rig_strstatus', 'rig_strvfo', 'rig_strvfop', 'rigerror', 'rigerror2', 'rot_caps', 'rot_state', 'toneArray', 'tuning_step_list', 'value_t', 'value_t_b'] + assert expected_callables == self.actual_callables + + def test_properties(self): + """Check that nothing was added or removed""" + expected_properties = ['AMP_FLAG_1', 'AMP_FLAG_2', 'AMP_LEVEL_FAULT', 'AMP_LEVEL_NH', 'AMP_LEVEL_NONE', 'AMP_LEVEL_PF', 'AMP_LEVEL_PWR', 'AMP_LEVEL_PWR_FWD', 'AMP_LEVEL_PWR_INPUT', 'AMP_LEVEL_PWR_PEAK', 'AMP_LEVEL_PWR_REFLECTED', 'AMP_LEVEL_SWR', 'AMP_MODEL_DUMMY', 'AMP_MODEL_ELECRAFT_KPA1500', 'AMP_MODEL_EXPERT_FA', 'AMP_MODEL_GEMINI_DX1200', 'AMP_MODEL_NETAMPCTL', 'AMP_MODEL_NONE', 'AMP_RESET_AMP', 'AMP_RESET_FAULT', 'AMP_RESET_MEM', 'AMP_TYPE_OTHER', 'BUILTINFUNC', 'CTCSS_LIST_SIZE', 'DCS_LIST_SIZE', 'DEBUGMSGSAVE_SIZE', 'GPIO1', 'GPIO2', 'GPIO3', 'GPIO4', 'HAMLIB_CACHE_ALL', 'HAMLIB_CACHE_ALWAYS', 'HAMLIB_CACHE_FREQ', 'HAMLIB_CACHE_MODE', 'HAMLIB_CACHE_PTT', 'HAMLIB_CACHE_SPLIT', 'HAMLIB_CACHE_VFO', 'HAMLIB_CACHE_WIDTH', 'HAMLIB_CHANLSTSIZ', 'HAMLIB_CHECK_RIG_CAPS', 'HAMLIB_COOKIE_SIZE', 'HAMLIB_ELAPSED_GET', 'HAMLIB_ELAPSED_INVALIDATE', 'HAMLIB_ELAPSED_SET', 'HAMLIB_FIFO_SIZE', 'HAMLIB_FILPATHLEN', 'HAMLIB_FLTLSTSIZ', 'HAMLIB_FRQRANGESIZ', 'HAMLIB_MAXCHANDESC', 'HAMLIB_MAXDBLSTSIZ', 'HAMLIB_MAX_AGC_LEVELS', 'HAMLIB_MAX_CAL_LENGTH', 'HAMLIB_MAX_MODES', 'HAMLIB_MAX_ROTORS', 'HAMLIB_MAX_RSCANS', 'HAMLIB_MAX_SNAPSHOT_PACKET_SIZE', 'HAMLIB_MAX_SPECTRUM_AVG_MODES', 'HAMLIB_MAX_SPECTRUM_DATA', 'HAMLIB_MAX_SPECTRUM_MODES', 'HAMLIB_MAX_SPECTRUM_SCOPES', 'HAMLIB_MAX_SPECTRUM_SPANS', 'HAMLIB_MAX_VFOS', 'HAMLIB_MAX_VFO_OPS', 'HAMLIB_RIGNAMSIZ', 'HAMLIB_RIGVERSIZ', 'HAMLIB_SECRET_LENGTH', 'HAMLIB_SETTINGS_FILE', 'HAMLIB_TSLSTSIZ', 'MAX_MODELS_PER_BACKEND', 'NETAMPCTL_RET', 'NETRIGCTL_RET', 'NETROTCTL_RET', 'RIG_AGC_AUTO', 'RIG_AGC_FAST', 'RIG_AGC_LAST', 'RIG_AGC_LONG', 'RIG_AGC_MEDIUM', 'RIG_AGC_NONE', 'RIG_AGC_OFF', 'RIG_AGC_ON', 'RIG_AGC_SLOW', 'RIG_AGC_SUPERFAST', 'RIG_AGC_USER', 'RIG_ANN_CW', 'RIG_ANN_ENG', 'RIG_ANN_FREQ', 'RIG_ANN_JAP', 'RIG_ANN_NONE', 'RIG_ANN_OFF', 'RIG_ANN_RXMODE', 'RIG_ANT_MAX', 'RIG_ANT_NONE', 'RIG_BANDSELECT_10M', 'RIG_BANDSELECT_12M', 'RIG_BANDSELECT_13CM', 'RIG_BANDSELECT_15M', 'RIG_BANDSELECT_160M', 'RIG_BANDSELECT_17M', 'RIG_BANDSELECT_1_25M', 'RIG_BANDSELECT_20M', 'RIG_BANDSELECT_2200M', 'RIG_BANDSELECT_23CM', 'RIG_BANDSELECT_2M', 'RIG_BANDSELECT_30M', 'RIG_BANDSELECT_33CM', 'RIG_BANDSELECT_3CM', 'RIG_BANDSELECT_40M', 'RIG_BANDSELECT_4M', 'RIG_BANDSELECT_5CM', 'RIG_BANDSELECT_600M', 'RIG_BANDSELECT_60M', 'RIG_BANDSELECT_6M', 'RIG_BANDSELECT_70CM', 'RIG_BANDSELECT_80M', 'RIG_BANDSELECT_9CM', 'RIG_BANDSELECT_AIR', 'RIG_BANDSELECT_GEN', 'RIG_BANDSELECT_MW', 'RIG_BANDSELECT_UNUSED', 'RIG_BANDSELECT_WFM', 'RIG_BAND_10M', 'RIG_BAND_12M', 'RIG_BAND_144MHZ', 'RIG_BAND_15M', 'RIG_BAND_160M', 'RIG_BAND_17M', 'RIG_BAND_20M', 'RIG_BAND_30M', 'RIG_BAND_40M', 'RIG_BAND_430MHZ', 'RIG_BAND_60M', 'RIG_BAND_6M', 'RIG_BAND_80M', 'RIG_BAND_AIR', 'RIG_BAND_GEN', 'RIG_BAND_MW', 'RIG_BAND_UNUSED', 'RIG_BUSBUSY', 'RIG_BUSERROR', 'RIG_CAPS_HAS_GET_LEVEL', 'RIG_CAPS_HAS_SET_LEVEL', 'RIG_CAPS_MFG_NAME_CPTR', 'RIG_CAPS_MODEL_NAME_CPTR', 'RIG_CAPS_PORT_TYPE', 'RIG_CAPS_PTT_TYPE', 'RIG_CAPS_RIG_MODEL', 'RIG_CAPS_STATUS_CPTR', 'RIG_CAPS_TARGETABLE_VFO', 'RIG_CAPS_VERSION_CPTR', 'RIG_CHFLAG_DATA', 'RIG_CHFLAG_NONE', 'RIG_CHFLAG_PSKIP', 'RIG_CHFLAG_SKIP', 'RIG_CLIENT_GPREDICT', 'RIG_CLIENT_UNKNOWN', 'RIG_CLIENT_WSJTX', 'RIG_COMM_STATUS_CONNECTING', 'RIG_COMM_STATUS_DISCONNECTED', 'RIG_COMM_STATUS_ERROR', 'RIG_COMM_STATUS_OK', 'RIG_COMM_STATUS_TERMINATED', 'RIG_COMM_STATUS_WARNING', 'RIG_CONF_BINARY', 'RIG_CONF_BUTTON', 'RIG_CONF_CHECKBUTTON', 'RIG_CONF_COMBO', 'RIG_CONF_END', 'RIG_CONF_INT', 'RIG_CONF_NUMERIC', 'RIG_CONF_STRING', 'RIG_COOKIE_GET', 'RIG_COOKIE_RELEASE', 'RIG_COOKIE_RENEW', 'RIG_DCD_CM108', 'RIG_DCD_GPIO', 'RIG_DCD_GPION', 'RIG_DCD_NONE', 'RIG_DCD_OFF', 'RIG_DCD_ON', 'RIG_DCD_PARALLEL', 'RIG_DCD_RIG', 'RIG_DCD_SERIAL_CAR', 'RIG_DCD_SERIAL_CTS', 'RIG_DCD_SERIAL_DSR', 'RIG_DEBUG_BUG', 'RIG_DEBUG_CACHE', 'RIG_DEBUG_ERR', 'RIG_DEBUG_NONE', 'RIG_DEBUG_TRACE', 'RIG_DEBUG_VERBOSE', 'RIG_DEBUG_WARN', 'RIG_EACCESS', 'RIG_EARG', 'RIG_ECONF', 'RIG_EDEPRECATED', 'RIG_EDOM', 'RIG_EEND', 'RIG_EINTERNAL', 'RIG_EINVAL', 'RIG_EIO', 'RIG_ELIMIT', 'RIG_ENAVAIL', 'RIG_ENIMPL', 'RIG_ENOMEM', 'RIG_ENTARGET', 'RIG_EPOWER', 'RIG_EPROTO', 'RIG_ERJCTED', 'RIG_ESECURITY', 'RIG_ETIMEOUT', 'RIG_ETRUNC', 'RIG_EVFO', 'RIG_FLAG_APRS', 'RIG_FLAG_COMPUTER', 'RIG_FLAG_DXCLUSTER', 'RIG_FLAG_HANDHELD', 'RIG_FLAG_MOBILE', 'RIG_FLAG_RECEIVER', 'RIG_FLAG_SCANNER', 'RIG_FLAG_TNC', 'RIG_FLAG_TRANSMITTER', 'RIG_FLAG_TRUNKING', 'RIG_FLAG_TUNER', 'RIG_FLT_ANY', 'RIG_FUNCTION_CLEANUP', 'RIG_FUNCTION_CLOSE', 'RIG_FUNCTION_DECODE_EVENT', 'RIG_FUNCTION_GET_ANT', 'RIG_FUNCTION_GET_CHANNEL', 'RIG_FUNCTION_GET_CHAN_ALL_CB', 'RIG_FUNCTION_GET_CONF', 'RIG_FUNCTION_GET_CONF2', 'RIG_FUNCTION_GET_CTCSS_SQL', 'RIG_FUNCTION_GET_CTCSS_TONE', 'RIG_FUNCTION_GET_DCD', 'RIG_FUNCTION_GET_DCS_CODE', 'RIG_FUNCTION_GET_DCS_SQL', 'RIG_FUNCTION_GET_EXT_FUNC', 'RIG_FUNCTION_GET_EXT_LEVEL', 'RIG_FUNCTION_GET_EXT_PARM', 'RIG_FUNCTION_GET_FREQ', 'RIG_FUNCTION_GET_FUNC', 'RIG_FUNCTION_GET_INFO', 'RIG_FUNCTION_GET_LEVEL', 'RIG_FUNCTION_GET_MEM', 'RIG_FUNCTION_GET_MEM_ALL_CB', 'RIG_FUNCTION_GET_MODE', 'RIG_FUNCTION_GET_PARM', 'RIG_FUNCTION_GET_POWERSTAT', 'RIG_FUNCTION_GET_PTT', 'RIG_FUNCTION_GET_RIT', 'RIG_FUNCTION_GET_RPTR_OFFS', 'RIG_FUNCTION_GET_RPTR_SHIFT', 'RIG_FUNCTION_GET_SPLIT_FREQ', 'RIG_FUNCTION_GET_SPLIT_FREQ_MODE', 'RIG_FUNCTION_GET_SPLIT_VFO', 'RIG_FUNCTION_GET_TONE', 'RIG_FUNCTION_GET_TONE_SQL', 'RIG_FUNCTION_GET_TRN', 'RIG_FUNCTION_GET_TS', 'RIG_FUNCTION_GET_VFO', 'RIG_FUNCTION_GET_XIT', 'RIG_FUNCTION_INIT', 'RIG_FUNCTION_IS_ASYNC_FRAME', 'RIG_FUNCTION_MW2POWER', 'RIG_FUNCTION_OPEN', 'RIG_FUNCTION_POWER2MW', 'RIG_FUNCTION_PROCESS_ASYNC_FRAME', 'RIG_FUNCTION_READ_FRAME_DIRECT', 'RIG_FUNCTION_RESET', 'RIG_FUNCTION_SCAN', 'RIG_FUNCTION_SEND_DTMF', 'RIG_FUNCTION_SEND_MORSE', 'RIG_FUNCTION_SEND_VOICE_MEM', 'RIG_FUNCTION_SET_ANT', 'RIG_FUNCTION_SET_BANK', 'RIG_FUNCTION_SET_CHANNEL', 'RIG_FUNCTION_SET_CHAN_ALL_CB', 'RIG_FUNCTION_SET_CONF', 'RIG_FUNCTION_SET_CTCSS_SQL', 'RIG_FUNCTION_SET_CTCSS_TONE', 'RIG_FUNCTION_SET_DCS_CODE', 'RIG_FUNCTION_SET_DCS_SQL', 'RIG_FUNCTION_SET_EXT_FUNC', 'RIG_FUNCTION_SET_EXT_LEVEL', 'RIG_FUNCTION_SET_EXT_PARM', 'RIG_FUNCTION_SET_FREQ', 'RIG_FUNCTION_SET_FUNC', 'RIG_FUNCTION_SET_LEVEL', 'RIG_FUNCTION_SET_MEM', 'RIG_FUNCTION_SET_MEM_ALL_CB', 'RIG_FUNCTION_SET_MODE', 'RIG_FUNCTION_SET_PARM', 'RIG_FUNCTION_SET_POWERSTAT', 'RIG_FUNCTION_SET_PTT', 'RIG_FUNCTION_SET_RIT', 'RIG_FUNCTION_SET_RPTR_OFFS', 'RIG_FUNCTION_SET_RPTR_SHIFT', 'RIG_FUNCTION_SET_SPLIT_FREQ', 'RIG_FUNCTION_SET_SPLIT_FREQ_MODE', 'RIG_FUNCTION_SET_SPLIT_MODE', 'RIG_FUNCTION_SET_SPLIT_VFO', 'RIG_FUNCTION_SET_TONE', 'RIG_FUNCTION_SET_TONE_SQL', 'RIG_FUNCTION_SET_TRN', 'RIG_FUNCTION_SET_TS', 'RIG_FUNCTION_SET_VFO', 'RIG_FUNCTION_SET_VFO_OPT', 'RIG_FUNCTION_SET_XIT', 'RIG_FUNCTION_STOP_MORSE', 'RIG_FUNCTION_STOP_VOICE_MEM', 'RIG_FUNCTION_VFO_OP', 'RIG_FUNCTION_WAIT_MORSE', 'RIG_FUNC_ABM', 'RIG_FUNC_AFC', 'RIG_FUNC_AFLT', 'RIG_FUNC_AIP', 'RIG_FUNC_ANF', 'RIG_FUNC_ANL', 'RIG_FUNC_APF', 'RIG_FUNC_ARO', 'RIG_FUNC_BC', 'RIG_FUNC_BC2', 'RIG_FUNC_BIT49', 'RIG_FUNC_BIT50', 'RIG_FUNC_BIT51', 'RIG_FUNC_BIT52', 'RIG_FUNC_BIT53', 'RIG_FUNC_BIT54', 'RIG_FUNC_BIT55', 'RIG_FUNC_BIT56', 'RIG_FUNC_BIT57', 'RIG_FUNC_BIT58', 'RIG_FUNC_BIT59', 'RIG_FUNC_BIT60', 'RIG_FUNC_BIT61', 'RIG_FUNC_BIT62', 'RIG_FUNC_BIT63', 'RIG_FUNC_COMP', 'RIG_FUNC_CSQL', 'RIG_FUNC_DIVERSITY', 'RIG_FUNC_DSQL', 'RIG_FUNC_DUAL_WATCH', 'RIG_FUNC_FAGC', 'RIG_FUNC_FBKIN', 'RIG_FUNC_LOCK', 'RIG_FUNC_MBC', 'RIG_FUNC_MN', 'RIG_FUNC_MON', 'RIG_FUNC_MUTE', 'RIG_FUNC_NB', 'RIG_FUNC_NB2', 'RIG_FUNC_NONE', 'RIG_FUNC_NR', 'RIG_FUNC_OVF_STATUS', 'RIG_FUNC_RESUME', 'RIG_FUNC_REV', 'RIG_FUNC_RF', 'RIG_FUNC_RIT', 'RIG_FUNC_SATMODE', 'RIG_FUNC_SBKIN', 'RIG_FUNC_SCEN', 'RIG_FUNC_SCOPE', 'RIG_FUNC_SEND_MORSE', 'RIG_FUNC_SEND_VOICE_MEM', 'RIG_FUNC_SLICE', 'RIG_FUNC_SPECTRUM', 'RIG_FUNC_SPECTRUM_HOLD', 'RIG_FUNC_SQL', 'RIG_FUNC_SYNC', 'RIG_FUNC_TBURST', 'RIG_FUNC_TONE', 'RIG_FUNC_TRANSCEIVE', 'RIG_FUNC_TSQL', 'RIG_FUNC_TUNER', 'RIG_FUNC_VOX', 'RIG_FUNC_VSC', 'RIG_FUNC_XIT', 'RIG_HANDSHAKE_HARDWARE', 'RIG_HANDSHAKE_NONE', 'RIG_HANDSHAKE_XONXOFF', 'RIG_KEYERTYPE_BUG', 'RIG_KEYERTYPE_PADDLE', 'RIG_KEYERTYPE_STRAIGHT', 'RIG_KEYERTYPE_UNKNOWN', 'RIG_LEVEL_53', 'RIG_LEVEL_54', 'RIG_LEVEL_55', 'RIG_LEVEL_56', 'RIG_LEVEL_57', 'RIG_LEVEL_58', 'RIG_LEVEL_59', 'RIG_LEVEL_60', 'RIG_LEVEL_61', 'RIG_LEVEL_62', 'RIG_LEVEL_63', 'RIG_LEVEL_AF', 'RIG_LEVEL_AGC', 'RIG_LEVEL_AGC_TIME', 'RIG_LEVEL_ALC', 'RIG_LEVEL_ANTIVOX', 'RIG_LEVEL_APF', 'RIG_LEVEL_ATT', 'RIG_LEVEL_BALANCE', 'RIG_LEVEL_BAND_SELECT', 'RIG_LEVEL_BKINDL', 'RIG_LEVEL_BKIN_DLYMS', 'RIG_LEVEL_COMP', 'RIG_LEVEL_COMP_METER', 'RIG_LEVEL_CWPITCH', 'RIG_LEVEL_FLOAT_LIST', 'RIG_LEVEL_ID_METER', 'RIG_LEVEL_IF', 'RIG_LEVEL_KEYSPD', 'RIG_LEVEL_METER', 'RIG_LEVEL_MICGAIN', 'RIG_LEVEL_MONITOR_GAIN', 'RIG_LEVEL_NB', 'RIG_LEVEL_NONE', 'RIG_LEVEL_NOTCHF', 'RIG_LEVEL_NOTCHF_RAW', 'RIG_LEVEL_NR', 'RIG_LEVEL_PBT_IN', 'RIG_LEVEL_PBT_OUT', 'RIG_LEVEL_PREAMP', 'RIG_LEVEL_RAWSTR', 'RIG_LEVEL_READONLY_LIST', 'RIG_LEVEL_RF', 'RIG_LEVEL_RFPOWER', 'RIG_LEVEL_RFPOWER_METER', 'RIG_LEVEL_RFPOWER_METER_WATTS', 'RIG_LEVEL_SLOPE_HIGH', 'RIG_LEVEL_SLOPE_LOW', 'RIG_LEVEL_SPECTRUM_ATT', 'RIG_LEVEL_SPECTRUM_AVG', 'RIG_LEVEL_SPECTRUM_EDGE_HIGH', 'RIG_LEVEL_SPECTRUM_EDGE_LOW', 'RIG_LEVEL_SPECTRUM_MODE', 'RIG_LEVEL_SPECTRUM_REF', 'RIG_LEVEL_SPECTRUM_SPAN', 'RIG_LEVEL_SPECTRUM_SPEED', 'RIG_LEVEL_SQL', 'RIG_LEVEL_STRENGTH', 'RIG_LEVEL_SWR', 'RIG_LEVEL_TEMP_METER', 'RIG_LEVEL_USB_AF', 'RIG_LEVEL_USB_AF_INPUT', 'RIG_LEVEL_VD_METER', 'RIG_LEVEL_VOXDELAY', 'RIG_LEVEL_VOXGAIN', 'RIG_MEM_CAPS_ALL', 'RIG_METER_ALC', 'RIG_METER_COMP', 'RIG_METER_DB', 'RIG_METER_IC', 'RIG_METER_NONE', 'RIG_METER_PO', 'RIG_METER_SWR', 'RIG_METER_TEMP', 'RIG_METER_VDD', 'RIG_MODEL_505DSP', 'RIG_MODEL_ACLOG', 'RIG_MODEL_ADT_200A', 'RIG_MODEL_AR16', 'RIG_MODEL_AR2500', 'RIG_MODEL_AR2700', 'RIG_MODEL_AR3000', 'RIG_MODEL_AR3000A', 'RIG_MODEL_AR3030', 'RIG_MODEL_AR5000', 'RIG_MODEL_AR5000A', 'RIG_MODEL_AR7030', 'RIG_MODEL_AR7030P', 'RIG_MODEL_AR8000', 'RIG_MODEL_AR8200', 'RIG_MODEL_AR8600', 'RIG_MODEL_ARMSTRONG', 'RIG_MODEL_ATD578UVIII', 'RIG_MODEL_BARRETT_2050', 'RIG_MODEL_BARRETT_4050', 'RIG_MODEL_BARRETT_4100', 'RIG_MODEL_BARRETT_950', 'RIG_MODEL_BC235', 'RIG_MODEL_BC245', 'RIG_MODEL_BC250', 'RIG_MODEL_BC780', 'RIG_MODEL_BC785', 'RIG_MODEL_BC786', 'RIG_MODEL_BC895', 'RIG_MODEL_BC898', 'RIG_MODEL_BCD396T', 'RIG_MODEL_BCD996T', 'RIG_MODEL_BCT8', 'RIG_MODEL_CMH530', 'RIG_MODEL_CODAN_2110', 'RIG_MODEL_CODAN_ENVOY', 'RIG_MODEL_CODAN_NGT', 'RIG_MODEL_CTX10', 'RIG_MODEL_DDS60', 'RIG_MODEL_DELTAII', 'RIG_MODEL_DKR8', 'RIG_MODEL_DKR8A', 'RIG_MODEL_DKR8B', 'RIG_MODEL_DORJI_DRA818U', 'RIG_MODEL_DORJI_DRA818V', 'RIG_MODEL_DRT1', 'RIG_MODEL_DSP10', 'RIG_MODEL_DTTSP', 'RIG_MODEL_DTTSP_UDP', 'RIG_MODEL_DUMMY', 'RIG_MODEL_DUMMY_NOVFO', 'RIG_MODEL_DWT', 'RIG_MODEL_DX77', 'RIG_MODEL_DXSR8', 'RIG_MODEL_EB200', 'RIG_MODEL_EK070', 'RIG_MODEL_EK890', 'RIG_MODEL_EK891', 'RIG_MODEL_EK895', 'RIG_MODEL_EK89X', 'RIG_MODEL_EKD500', 'RIG_MODEL_ELAD_FDM_DUO', 'RIG_MODEL_ELEKTOR304', 'RIG_MODEL_ELEKTOR507', 'RIG_MODEL_ESM500', 'RIG_MODEL_ESMC', 'RIG_MODEL_F6K', 'RIG_MODEL_FASDR', 'RIG_MODEL_FIFISDR', 'RIG_MODEL_FLRIG', 'RIG_MODEL_FRG100', 'RIG_MODEL_FRG8800', 'RIG_MODEL_FRG9600', 'RIG_MODEL_FT100', 'RIG_MODEL_FT1000', 'RIG_MODEL_FT1000D', 'RIG_MODEL_FT1000MP', 'RIG_MODEL_FT1000MPMKV', 'RIG_MODEL_FT1000MPMKVFLD', 'RIG_MODEL_FT2000', 'RIG_MODEL_FT450', 'RIG_MODEL_FT450D', 'RIG_MODEL_FT575', 'RIG_MODEL_FT600', 'RIG_MODEL_FT650', 'RIG_MODEL_FT710', 'RIG_MODEL_FT736R', 'RIG_MODEL_FT747', 'RIG_MODEL_FT757', 'RIG_MODEL_FT757GXII', 'RIG_MODEL_FT767', 'RIG_MODEL_FT817', 'RIG_MODEL_FT818', 'RIG_MODEL_FT820', 'RIG_MODEL_FT840', 'RIG_MODEL_FT847', 'RIG_MODEL_FT847UNI', 'RIG_MODEL_FT857', 'RIG_MODEL_FT890', 'RIG_MODEL_FT891', 'RIG_MODEL_FT897', 'RIG_MODEL_FT897D', 'RIG_MODEL_FT900', 'RIG_MODEL_FT9000', 'RIG_MODEL_FT9000OLD', 'RIG_MODEL_FT920', 'RIG_MODEL_FT950', 'RIG_MODEL_FT980', 'RIG_MODEL_FT990', 'RIG_MODEL_FT990UNI', 'RIG_MODEL_FT991', 'RIG_MODEL_FTDX10', 'RIG_MODEL_FTDX101D', 'RIG_MODEL_FTDX101MP', 'RIG_MODEL_FTDX1200', 'RIG_MODEL_FTDX3000', 'RIG_MODEL_FTDX5000', 'RIG_MODEL_FUNCUBEDONGLE', 'RIG_MODEL_FUNCUBEDONGLEPLUS', 'RIG_MODEL_FX4', 'RIG_MODEL_G303', 'RIG_MODEL_G305', 'RIG_MODEL_G313', 'RIG_MODEL_G315', 'RIG_MODEL_G90', 'RIG_MODEL_GNURADIO', 'RIG_MODEL_GRAUDIO', 'RIG_MODEL_GRAUDIOIQ', 'RIG_MODEL_GS100', 'RIG_MODEL_HF1000', 'RIG_MODEL_HF1000A', 'RIG_MODEL_HF150', 'RIG_MODEL_HF225', 'RIG_MODEL_HF235', 'RIG_MODEL_HF250', 'RIG_MODEL_HIQSDR', 'RIG_MODEL_HPSDR', 'RIG_MODEL_IC1271', 'RIG_MODEL_IC1275', 'RIG_MODEL_IC271', 'RIG_MODEL_IC2730', 'RIG_MODEL_IC275', 'RIG_MODEL_IC375', 'RIG_MODEL_IC471', 'RIG_MODEL_IC475', 'RIG_MODEL_IC575', 'RIG_MODEL_IC7000', 'RIG_MODEL_IC703', 'RIG_MODEL_IC705', 'RIG_MODEL_IC706', 'RIG_MODEL_IC706MKII', 'RIG_MODEL_IC706MKIIG', 'RIG_MODEL_IC707', 'RIG_MODEL_IC7100', 'RIG_MODEL_IC718', 'RIG_MODEL_IC7200', 'RIG_MODEL_IC725', 'RIG_MODEL_IC726', 'RIG_MODEL_IC728', 'RIG_MODEL_IC729', 'RIG_MODEL_IC7300', 'RIG_MODEL_IC731', 'RIG_MODEL_IC735', 'RIG_MODEL_IC736', 'RIG_MODEL_IC737', 'RIG_MODEL_IC738', 'RIG_MODEL_IC7410', 'RIG_MODEL_IC746', 'RIG_MODEL_IC746PRO', 'RIG_MODEL_IC751', 'RIG_MODEL_IC751A', 'RIG_MODEL_IC756', 'RIG_MODEL_IC756PRO', 'RIG_MODEL_IC756PROII', 'RIG_MODEL_IC756PROIII', 'RIG_MODEL_IC7600', 'RIG_MODEL_IC761', 'RIG_MODEL_IC7610', 'RIG_MODEL_IC765', 'RIG_MODEL_IC7700', 'RIG_MODEL_IC775', 'RIG_MODEL_IC7760', 'RIG_MODEL_IC78', 'RIG_MODEL_IC7800', 'RIG_MODEL_IC781', 'RIG_MODEL_IC785x', 'RIG_MODEL_IC820', 'RIG_MODEL_IC821H', 'RIG_MODEL_IC905', 'RIG_MODEL_IC910', 'RIG_MODEL_IC9100', 'RIG_MODEL_IC92D', 'RIG_MODEL_IC970', 'RIG_MODEL_IC9700', 'RIG_MODEL_ICF8101', 'RIG_MODEL_ICID1', 'RIG_MODEL_ICR10', 'RIG_MODEL_ICR20', 'RIG_MODEL_ICR30', 'RIG_MODEL_ICR6', 'RIG_MODEL_ICR7000', 'RIG_MODEL_ICR71', 'RIG_MODEL_ICR7100', 'RIG_MODEL_ICR72', 'RIG_MODEL_ICR75', 'RIG_MODEL_ICR8500', 'RIG_MODEL_ICR8600', 'RIG_MODEL_ICR9000', 'RIG_MODEL_ICR9500', 'RIG_MODEL_ICRX7', 'RIG_MODEL_IC_M700PRO', 'RIG_MODEL_IC_M710', 'RIG_MODEL_IC_M802', 'RIG_MODEL_IC_M803', 'RIG_MODEL_ID31', 'RIG_MODEL_ID4100', 'RIG_MODEL_ID51', 'RIG_MODEL_ID5100', 'RIG_MODEL_JST145', 'RIG_MODEL_JST245', 'RIG_MODEL_K2', 'RIG_MODEL_K3', 'RIG_MODEL_K3S', 'RIG_MODEL_K4', 'RIG_MODEL_KX2', 'RIG_MODEL_KX3', 'RIG_MODEL_LAB599_TX500', 'RIG_MODEL_MALACHITE', 'RIG_MODEL_MC4020', 'RIG_MODEL_MCHFQRP', 'RIG_MODEL_MDS4710', 'RIG_MODEL_MDS9710', 'RIG_MODEL_MICOM2', 'RIG_MODEL_MICROTUNE_4702', 'RIG_MODEL_MICROTUNE_4707', 'RIG_MODEL_MICROTUNE_4937', 'RIG_MODEL_MINISCOUT', 'RIG_MODEL_MINIVNA', 'RIG_MODEL_NETRIGCTL', 'RIG_MODEL_NONE', 'RIG_MODEL_NRD345', 'RIG_MODEL_NRD525', 'RIG_MODEL_NRD535', 'RIG_MODEL_NRD545', 'RIG_MODEL_OMNIVI', 'RIG_MODEL_OMNIVIP', 'RIG_MODEL_OS456', 'RIG_MODEL_OS535', 'RIG_MODEL_PARAGON2', 'RIG_MODEL_PCR100', 'RIG_MODEL_PCR1000', 'RIG_MODEL_PCR1500', 'RIG_MODEL_PCR2500', 'RIG_MODEL_PERSEUS', 'RIG_MODEL_PMR171', 'RIG_MODEL_PMSDR', 'RIG_MODEL_POWERSDR', 'RIG_MODEL_PRM8060', 'RIG_MODEL_PRM8070', 'RIG_MODEL_PRO2052', 'RIG_MODEL_PT8000A', 'RIG_MODEL_Q900', 'RIG_MODEL_QRPLABS', 'RIG_MODEL_QRPLABS_QMX', 'RIG_MODEL_QUISK', 'RIG_MODEL_R5000', 'RIG_MODEL_RA3702', 'RIG_MODEL_RA3710', 'RIG_MODEL_RA3720', 'RIG_MODEL_RA3790', 'RIG_MODEL_RA6790', 'RIG_MODEL_RS2005', 'RIG_MODEL_RS2006', 'RIG_MODEL_RS2035', 'RIG_MODEL_RS2041', 'RIG_MODEL_RS2042', 'RIG_MODEL_RS64', 'RIG_MODEL_RSHFIQ', 'RIG_MODEL_RX320', 'RIG_MODEL_RX331', 'RIG_MODEL_RX340', 'RIG_MODEL_RX350', 'RIG_MODEL_SDR1000', 'RIG_MODEL_SDR1000RFE', 'RIG_MODEL_SDRCONSOLE', 'RIG_MODEL_SDRSHARP', 'RIG_MODEL_SDRUNO', 'RIG_MODEL_SDU5000', 'RIG_MODEL_SDU5500', 'RIG_MODEL_SI570AVRUSB', 'RIG_MODEL_SI570PEABERRY1', 'RIG_MODEL_SI570PEABERRY2', 'RIG_MODEL_SI570PICUSB', 'RIG_MODEL_SMARTSDR_A', 'RIG_MODEL_SMARTSDR_B', 'RIG_MODEL_SMARTSDR_C', 'RIG_MODEL_SMARTSDR_D', 'RIG_MODEL_SMARTSDR_E', 'RIG_MODEL_SMARTSDR_F', 'RIG_MODEL_SMARTSDR_G', 'RIG_MODEL_SMARTSDR_H', 'RIG_MODEL_SR2200', 'RIG_MODEL_TCI1X', 'RIG_MODEL_THD72A', 'RIG_MODEL_THD74', 'RIG_MODEL_THD7A', 'RIG_MODEL_THD7AG', 'RIG_MODEL_THETIS', 'RIG_MODEL_THF6A', 'RIG_MODEL_THF7E', 'RIG_MODEL_THG71', 'RIG_MODEL_TMD700', 'RIG_MODEL_TMD710', 'RIG_MODEL_TMV7', 'RIG_MODEL_TMV71', 'RIG_MODEL_TRANSFOX', 'RIG_MODEL_TRC80', 'RIG_MODEL_TRP7000', 'RIG_MODEL_TRP8000', 'RIG_MODEL_TRP8255', 'RIG_MODEL_TRP9000', 'RIG_MODEL_TRUSDX', 'RIG_MODEL_TRXMANAGER_RIG', 'RIG_MODEL_TS140S', 'RIG_MODEL_TS2000', 'RIG_MODEL_TS440', 'RIG_MODEL_TS450S', 'RIG_MODEL_TS480', 'RIG_MODEL_TS50', 'RIG_MODEL_TS570D', 'RIG_MODEL_TS570S', 'RIG_MODEL_TS590S', 'RIG_MODEL_TS590SG', 'RIG_MODEL_TS680S', 'RIG_MODEL_TS690S', 'RIG_MODEL_TS711', 'RIG_MODEL_TS790', 'RIG_MODEL_TS811', 'RIG_MODEL_TS850', 'RIG_MODEL_TS870S', 'RIG_MODEL_TS890S', 'RIG_MODEL_TS930', 'RIG_MODEL_TS940', 'RIG_MODEL_TS950S', 'RIG_MODEL_TS950SDX', 'RIG_MODEL_TS990S', 'RIG_MODEL_TT516', 'RIG_MODEL_TT526', 'RIG_MODEL_TT538', 'RIG_MODEL_TT550', 'RIG_MODEL_TT565', 'RIG_MODEL_TT585', 'RIG_MODEL_TT588', 'RIG_MODEL_TT599', 'RIG_MODEL_USRP', 'RIG_MODEL_USRP0', 'RIG_MODEL_USRP_G', 'RIG_MODEL_V4L', 'RIG_MODEL_V4L2', 'RIG_MODEL_VR5000', 'RIG_MODEL_VX1700', 'RIG_MODEL_WJ8711', 'RIG_MODEL_WJ8888', 'RIG_MODEL_WR1000', 'RIG_MODEL_WR1500', 'RIG_MODEL_WR1550', 'RIG_MODEL_WR3100', 'RIG_MODEL_WR3150', 'RIG_MODEL_WR3500', 'RIG_MODEL_WR3700', 'RIG_MODEL_X108G', 'RIG_MODEL_X5105', 'RIG_MODEL_X6100', 'RIG_MODEL_X6200', 'RIG_MODEL_XG3', 'RIG_MODEL_XK2100', 'RIG_MODEL_XK852', 'RIG_MODEL_XPLORER', 'RIG_MODE_ALL', 'RIG_MODE_AM', 'RIG_MODE_AMN', 'RIG_MODE_AMS', 'RIG_MODE_BIT46', 'RIG_MODE_BIT47', 'RIG_MODE_BIT48', 'RIG_MODE_BIT49', 'RIG_MODE_BIT50', 'RIG_MODE_BIT51', 'RIG_MODE_BIT52', 'RIG_MODE_BIT53', 'RIG_MODE_BIT54', 'RIG_MODE_BIT55', 'RIG_MODE_BIT56', 'RIG_MODE_BIT57', 'RIG_MODE_BIT58', 'RIG_MODE_BIT59', 'RIG_MODE_BIT60', 'RIG_MODE_BIT61', 'RIG_MODE_BIT62', 'RIG_MODE_C4FM', 'RIG_MODE_CW', 'RIG_MODE_CWN', 'RIG_MODE_CWR', 'RIG_MODE_DCR', 'RIG_MODE_DD', 'RIG_MODE_DPMR', 'RIG_MODE_DSB', 'RIG_MODE_DSTAR', 'RIG_MODE_ECSS', 'RIG_MODE_ECSSLSB', 'RIG_MODE_ECSSUSB', 'RIG_MODE_FAX', 'RIG_MODE_FM', 'RIG_MODE_FMN', 'RIG_MODE_IQ', 'RIG_MODE_ISBLSB', 'RIG_MODE_ISBUSB', 'RIG_MODE_LSB', 'RIG_MODE_LSBD1', 'RIG_MODE_LSBD2', 'RIG_MODE_LSBD3', 'RIG_MODE_NONE', 'RIG_MODE_NXDNVN', 'RIG_MODE_NXDN_N', 'RIG_MODE_P25', 'RIG_MODE_PKTAM', 'RIG_MODE_PKTFM', 'RIG_MODE_PKTFMN', 'RIG_MODE_PKTLSB', 'RIG_MODE_PKTSSB', 'RIG_MODE_PKTUSB', 'RIG_MODE_PSK', 'RIG_MODE_PSKR', 'RIG_MODE_RTTY', 'RIG_MODE_RTTYR', 'RIG_MODE_SAH', 'RIG_MODE_SAL', 'RIG_MODE_SAM', 'RIG_MODE_SPEC', 'RIG_MODE_SSB', 'RIG_MODE_TESTS_MAX', 'RIG_MODE_USB', 'RIG_MODE_USBD1', 'RIG_MODE_USBD2', 'RIG_MODE_USBD3', 'RIG_MODE_WFM', 'RIG_MTYPE_BAND', 'RIG_MTYPE_CALL', 'RIG_MTYPE_EDGE', 'RIG_MTYPE_MEM', 'RIG_MTYPE_MEMOPAD', 'RIG_MTYPE_MORSE', 'RIG_MTYPE_NONE', 'RIG_MTYPE_PRIO', 'RIG_MTYPE_SAT', 'RIG_MTYPE_SPLIT', 'RIG_MTYPE_VOICE', 'RIG_MULTICAST_POLL', 'RIG_MULTICAST_SPECTRUM', 'RIG_MULTICAST_TRANSCEIVE', 'RIG_OK', 'RIG_OP_BAND_DOWN', 'RIG_OP_BAND_UP', 'RIG_OP_CPY', 'RIG_OP_DOWN', 'RIG_OP_FROM_VFO', 'RIG_OP_LEFT', 'RIG_OP_MCL', 'RIG_OP_NONE', 'RIG_OP_RIGHT', 'RIG_OP_TOGGLE', 'RIG_OP_TO_VFO', 'RIG_OP_TUNE', 'RIG_OP_UP', 'RIG_OP_XCHG', 'RIG_PARITY_EVEN', 'RIG_PARITY_MARK', 'RIG_PARITY_NONE', 'RIG_PARITY_ODD', 'RIG_PARITY_SPACE', 'RIG_PARM_AFIF', 'RIG_PARM_AFIF_ACC', 'RIG_PARM_AFIF_LAN', 'RIG_PARM_AFIF_WLAN', 'RIG_PARM_ANN', 'RIG_PARM_APO', 'RIG_PARM_BACKLIGHT', 'RIG_PARM_BANDSELECT', 'RIG_PARM_BAT', 'RIG_PARM_BEEP', 'RIG_PARM_KEYERTYPE', 'RIG_PARM_KEYLIGHT', 'RIG_PARM_NONE', 'RIG_PARM_SCREENSAVER', 'RIG_PARM_TIME', 'RIG_PORT_CM108', 'RIG_PORT_DEVICE', 'RIG_PORT_DTMF', 'RIG_PORT_GPIO', 'RIG_PORT_GPION', 'RIG_PORT_NETWORK', 'RIG_PORT_NONE', 'RIG_PORT_PACKET', 'RIG_PORT_PARALLEL', 'RIG_PORT_RPC', 'RIG_PORT_SERIAL', 'RIG_PORT_UDP_NETWORK', 'RIG_PORT_ULTRA', 'RIG_PORT_USB', 'RIG_POWER_OFF', 'RIG_POWER_ON', 'RIG_POWER_OPERATE', 'RIG_POWER_STANDBY', 'RIG_POWER_UNKNOWN', 'RIG_PTRX_AMPPORT', 'RIG_PTRX_AMPSTATE', 'RIG_PTRX_CACHE', 'RIG_PTRX_DCDPORT', 'RIG_PTRX_MAXIMUM', 'RIG_PTRX_NONE', 'RIG_PTRX_PTTPORT', 'RIG_PTRX_RIGPORT', 'RIG_PTRX_ROTPORT', 'RIG_PTRX_ROTPORT2', 'RIG_PTRX_ROTSTATE', 'RIG_PTRX_STATE', 'RIG_PTT_CM108', 'RIG_PTT_GPIO', 'RIG_PTT_GPION', 'RIG_PTT_NONE', 'RIG_PTT_OFF', 'RIG_PTT_ON', 'RIG_PTT_ON_DATA', 'RIG_PTT_ON_MIC', 'RIG_PTT_PARALLEL', 'RIG_PTT_RIG', 'RIG_PTT_RIG_MICDATA', 'RIG_PTT_SERIAL_DTR', 'RIG_PTT_SERIAL_RTS', 'RIG_RESET_MASTER', 'RIG_RESET_MCALL', 'RIG_RESET_NONE', 'RIG_RESET_SOFT', 'RIG_RESET_VFO', 'RIG_RPT_SHIFT_MINUS', 'RIG_RPT_SHIFT_NONE', 'RIG_RPT_SHIFT_PLUS', 'RIG_SCAN_DELTA', 'RIG_SCAN_MEM', 'RIG_SCAN_NONE', 'RIG_SCAN_PLT', 'RIG_SCAN_PRIO', 'RIG_SCAN_PROG', 'RIG_SCAN_SLCT', 'RIG_SCAN_STOP', 'RIG_SCAN_VFO', 'RIG_SETTING_MAX', 'RIG_SIGNAL_OFF', 'RIG_SIGNAL_ON', 'RIG_SIGNAL_UNSET', 'RIG_SPECTRUM_MODE_CENTER', 'RIG_SPECTRUM_MODE_CENTER_SCROLL', 'RIG_SPECTRUM_MODE_FIXED', 'RIG_SPECTRUM_MODE_FIXED_SCROLL', 'RIG_SPECTRUM_MODE_NONE', 'RIG_SPLIT_OFF', 'RIG_SPLIT_ON', 'RIG_STATUS_ALPHA', 'RIG_STATUS_BETA', 'RIG_STATUS_BUGGY', 'RIG_STATUS_STABLE', 'RIG_STATUS_UNTESTED', 'RIG_TARGETABLE_ALL', 'RIG_TARGETABLE_ANT', 'RIG_TARGETABLE_BAND', 'RIG_TARGETABLE_BANK', 'RIG_TARGETABLE_COMMON', 'RIG_TARGETABLE_FREQ', 'RIG_TARGETABLE_FUNC', 'RIG_TARGETABLE_LEVEL', 'RIG_TARGETABLE_MEM', 'RIG_TARGETABLE_MODE', 'RIG_TARGETABLE_NONE', 'RIG_TARGETABLE_PTT', 'RIG_TARGETABLE_PURE', 'RIG_TARGETABLE_RITXIT', 'RIG_TARGETABLE_ROOFING', 'RIG_TARGETABLE_SPECTRUM', 'RIG_TARGETABLE_TONE', 'RIG_TRN_OFF', 'RIG_TRN_POLL', 'RIG_TRN_RIG', 'RIG_TS_ANY', 'RIG_TYPE_OTHER', 'RIG_VFO_A', 'RIG_VFO_ALL', 'RIG_VFO_B', 'RIG_VFO_C', 'RIG_VFO_CURR', 'RIG_VFO_MAIN', 'RIG_VFO_MAIN_A', 'RIG_VFO_MAIN_B', 'RIG_VFO_MAIN_C', 'RIG_VFO_MEM', 'RIG_VFO_NONE', 'RIG_VFO_OTHER', 'RIG_VFO_RX', 'RIG_VFO_SUB', 'RIG_VFO_SUB_A', 'RIG_VFO_SUB_B', 'RIG_VFO_SUB_C', 'RIG_VFO_TX', 'RIG_VFO_TX_FLAG', 'RIG_VFO_VFO', 'ROT_FLAG_AZIMUTH', 'ROT_FLAG_ELEVATION', 'ROT_FUNC_BIT63', 'ROT_FUNC_NONE', 'ROT_LEVEL_63', 'ROT_LEVEL_FLOAT_LIST', 'ROT_LEVEL_NONE', 'ROT_LEVEL_READONLY_LIST', 'ROT_LEVEL_SPEED', 'ROT_MODEL_ANDROIDSENSOR', 'ROT_MODEL_APEX_SHARED_LOOP', 'ROT_MODEL_ARRAYSOLUTIONS_SAL_12_20_30', 'ROT_MODEL_CNCTRK', 'ROT_MODEL_DCU', 'ROT_MODEL_DUMMY', 'ROT_MODEL_EASYCOMM1', 'ROT_MODEL_EASYCOMM2', 'ROT_MODEL_EASYCOMM3', 'ROT_MODEL_ERC', 'ROT_MODEL_ETHER6', 'ROT_MODEL_F1TETRACKER', 'ROT_MODEL_FLIR', 'ROT_MODEL_FODTRACK', 'ROT_MODEL_GRBLTRK_NET', 'ROT_MODEL_GRBLTRK_SER', 'ROT_MODEL_GS23', 'ROT_MODEL_GS232', 'ROT_MODEL_GS232A', 'ROT_MODEL_GS232A_AZ', 'ROT_MODEL_GS232A_EL', 'ROT_MODEL_GS232B', 'ROT_MODEL_GS232B_AZ', 'ROT_MODEL_GS232B_EL', 'ROT_MODEL_GS232_GENERIC', 'ROT_MODEL_GS23_AZ', 'ROT_MODEL_HD1780', 'ROT_MODEL_IF100', 'ROT_MODEL_INDI', 'ROT_MODEL_IOPTRON', 'ROT_MODEL_LVB', 'ROT_MODEL_MEADE', 'ROT_MODEL_NETROTCTL', 'ROT_MODEL_NEXSTAR', 'ROT_MODEL_NONE', 'ROT_MODEL_PCROTOR', 'ROT_MODEL_PROSISTEL_COMBI_TRACK_AZEL', 'ROT_MODEL_PROSISTEL_D_AZ', 'ROT_MODEL_PROSISTEL_D_EL', 'ROT_MODEL_PROSISTEL_D_EL_CBOXAZ', 'ROT_MODEL_PSTROTATOR', 'ROT_MODEL_RADANT', 'ROT_MODEL_RC2800', 'ROT_MODEL_RC2800_EARLY_AZ', 'ROT_MODEL_RC2800_EARLY_AZEL', 'ROT_MODEL_RCI_AZ', 'ROT_MODEL_RCI_AZEL', 'ROT_MODEL_ROTORCARD', 'ROT_MODEL_ROTOREZ', 'ROT_MODEL_RT21', 'ROT_MODEL_SAEBRTRACK', 'ROT_MODEL_SARTEK1', 'ROT_MODEL_SATEL', 'ROT_MODEL_SATROTCTL', 'ROT_MODEL_SKYWATCHER', 'ROT_MODEL_SPID_MD01_ROT2PROG', 'ROT_MODEL_SPID_ROT1PROG', 'ROT_MODEL_SPID_ROT2PROG', 'ROT_MODEL_ST2', 'ROT_MODEL_TS7400', 'ROT_MODEL_YRC1', 'ROT_MOVE_CCW', 'ROT_MOVE_CW', 'ROT_MOVE_DOWN', 'ROT_MOVE_DOWN_CCW', 'ROT_MOVE_DOWN_CW', 'ROT_MOVE_DOWN_LEFT', 'ROT_MOVE_DOWN_RIGHT', 'ROT_MOVE_LEFT', 'ROT_MOVE_RIGHT', 'ROT_MOVE_UP', 'ROT_MOVE_UP_CCW', 'ROT_MOVE_UP_CW', 'ROT_MOVE_UP_LEFT', 'ROT_MOVE_UP_RIGHT', 'ROT_PARM_FLOAT_LIST', 'ROT_PARM_NONE', 'ROT_PARM_READONLY_LIST', 'ROT_RESET_ALL', 'ROT_SPEED_NOCHANGE', 'ROT_STATUS_BUSY', 'ROT_STATUS_LIMIT_DOWN', 'ROT_STATUS_LIMIT_LEFT', 'ROT_STATUS_LIMIT_RIGHT', 'ROT_STATUS_LIMIT_UP', 'ROT_STATUS_MOVING', 'ROT_STATUS_MOVING_AZ', 'ROT_STATUS_MOVING_DOWN', 'ROT_STATUS_MOVING_EL', 'ROT_STATUS_MOVING_LEFT', 'ROT_STATUS_MOVING_RIGHT', 'ROT_STATUS_MOVING_UP', 'ROT_STATUS_NONE', 'ROT_STATUS_OVERLAP_DOWN', 'ROT_STATUS_OVERLAP_LEFT', 'ROT_STATUS_OVERLAP_RIGHT', 'ROT_STATUS_OVERLAP_UP', 'ROT_TYPE_OTHER', 'TWIDDLE_OFF', 'TWIDDLE_ON', 'VFO_DUAL', 'cvar', 'hamlib_copyright', 'hamlib_version'] + assert expected_properties == self.actual_properties + + @classmethod + def generate_data(cls, the_object): + """Extract callables and properties from the given object""" + callables = [] + properties = [] + for method_or_property in dir(the_object): + if not method_or_property.startswith("_"): + if callable(getattr(the_object, method_or_property)): + callables.append(method_or_property) + else: + properties.append(method_or_property) + + return callables, properties diff --git a/bindings/python/test_amp.py b/bindings/python/test_amp.py new file mode 100755 index 000000000..f3d915071 --- /dev/null +++ b/bindings/python/test_amp.py @@ -0,0 +1,118 @@ +#! /bin/env pytest +"""Tests of the Python bindings for Hamlib + +Running this script directly will use the installed bindings. +For an in-tree run use "make check", or set PYTHONPATH to point to +the directories containing Hamlib.py and _Hamlib.so. +""" +import Hamlib + +Hamlib.rig_set_debug(Hamlib.RIG_DEBUG_NONE) + +AMP_MODEL = Hamlib.AMP_MODEL_DUMMY + +class TestClass: + """Container class for tests""" + + def test_without_open(self): + """Call all the methods that do not depend on open()""" + amp = Hamlib.Amp(AMP_MODEL) + assert amp is not None + assert amp.do_exception == 0 + assert amp.error_status == Hamlib.RIG_OK + + assert isinstance(amp.caps.model_name, str) + assert isinstance(amp.caps.mfg_name, str) + assert isinstance(amp.caps.version, str) + assert isinstance(amp.caps.copyright, str) + + assert amp.set_conf("", "") is None + assert amp.get_conf("") == "" + assert amp.get_conf(0) == "" + conf = amp.get_conf("mcfg") + assert isinstance(conf, str) + assert amp.set_conf("mcfg", "foo") is None + conf = amp.get_conf("mcfg") + assert conf == "" # FIXME: should return "foo" + + assert amp.token_lookup("") is None + + + def test_with_open(self): + """Call all the methods that depend on open()""" + amp = Hamlib.Amp(AMP_MODEL) + assert amp is not None + + assert amp.state.comm_state == 0 + assert amp.open() is None + assert amp.state.comm_state == 1 + info = amp.get_info() + assert isinstance(info, str) + assert amp.reset(Hamlib.AMP_RESET_FAULT) is None + assert amp.set_freq(0) is None + assert amp.set_freq(123.45) is None + assert amp.get_freq() == 123.45 + assert amp.get_level(Hamlib.AMP_LEVEL_NONE) is None + level = amp.get_level(Hamlib.AMP_LEVEL_SWR) + assert isinstance(level, float) + level = amp.get_level(Hamlib.AMP_LEVEL_PWR_REFLECTED) + assert isinstance(level, int) + level = amp.get_level(Hamlib.AMP_LEVEL_FAULT) + assert isinstance(level, str) + level = amp.get_level(123456) + assert level is None + assert amp.set_powerstat(Hamlib.RIG_POWER_ON) is None + assert amp.get_powerstat() == Hamlib.RIG_POWER_ON + assert amp.close() is None + + + def test_all_methods(self): + """Just call all the methods""" + amp = Hamlib.Amp(AMP_MODEL) + assert amp is not None + + # the tests that do not depend on open() + assert amp.set_conf("", "") is None + assert amp.get_conf("") == "" + assert amp.get_conf(0) == "" + conf = amp.get_conf("mcfg") + assert isinstance(conf, str) + assert amp.set_conf("mcfg", "foo") is None + conf = amp.get_conf("mcfg") + assert conf == "" # FIXME: should return "foo" + assert amp.token_lookup("") is None + + # the tests that depend on open() + assert amp.state.comm_state == 0 + assert amp.open() is None + assert amp.state.comm_state == 1 + info = amp.get_info() + assert isinstance(info, str) + assert amp.reset(Hamlib.AMP_RESET_FAULT) is None + assert amp.set_freq(0) is None + assert amp.set_freq(123.45) is None + assert amp.get_freq() == 123.45 + assert amp.get_level(Hamlib.AMP_LEVEL_NONE) is None + level = amp.get_level(Hamlib.AMP_LEVEL_SWR) + assert isinstance(level, float) + level = amp.get_level(Hamlib.AMP_LEVEL_PWR_REFLECTED) + assert isinstance(level, int) + level = amp.get_level(Hamlib.AMP_LEVEL_FAULT) + assert isinstance(level, str) + level = amp.get_level(123456) + assert level is None + assert amp.set_powerstat(Hamlib.RIG_POWER_ON) is None + assert amp.get_powerstat() == Hamlib.RIG_POWER_ON + assert amp.close() is None + assert amp.state.comm_state == 0 + info = amp.get_info() + assert info is None + + + def test_object_creation(self): + """Create all objects available""" + amp = Hamlib.Rig(AMP_MODEL) + assert amp is not None + + assert isinstance(amp.caps, Hamlib.rig_caps) + assert isinstance(amp.state, Hamlib.rig_state) diff --git a/bindings/python/test_rig.py b/bindings/python/test_rig.py new file mode 100755 index 000000000..4790dc92e --- /dev/null +++ b/bindings/python/test_rig.py @@ -0,0 +1,216 @@ +#! /bin/env pytest +"""Tests of the Python bindings for Hamlib + +Running this script directly will use the installed bindings. +For an in-tree run use "make check", or set PYTHONPATH to point to +the directories containing Hamlib.py and _Hamlib.so. +""" +from pytest import raises + +import Hamlib + +Hamlib.rig_set_debug(Hamlib.RIG_DEBUG_NONE) + +RIG_MODEL = Hamlib.RIG_MODEL_DUMMY + +class TestClass: + """Container class for tests""" + + def test_without_open(self): + """Call all the methods that do not depend on open()""" + rig = Hamlib.Rig(RIG_MODEL) + assert rig is not None + assert rig.do_exception == 0 + assert rig.error_status == Hamlib.RIG_OK + + assert isinstance(rig.caps.model_name, str) + assert isinstance(rig.caps.mfg_name, str) + assert isinstance(rig.caps.version, str) + assert isinstance(rig.caps.copyright, str) + + assert rig.set_conf("", "") is None + assert rig.get_conf("") == "" + assert rig.get_conf(0) == "" + conf = rig.get_conf("mcfg") + assert isinstance(conf, str) + assert rig.set_conf("mcfg", "foo") is None + conf = rig.get_conf("mcfg") + assert conf == "foo" + + assert rig.token_lookup("") is None + + + def test_with_open(self): + """Call all the methods that depend on open()""" + rig = Hamlib.Rig(RIG_MODEL) + assert rig is not None + + assert rig.state.comm_state == 0 + assert rig.open() is None + assert rig.state.comm_state == 1 + info = rig.get_info() + assert isinstance(info, str) + assert rig.close() is None + assert rig.state.comm_state == 0 + info = rig.get_info() + assert info is None + + + def test_misc(self): + """Just call all the methods""" + rig = Hamlib.Rig(RIG_MODEL) + assert rig is not None + + assert rig.close() is None + assert rig.ext_token_lookup("") is None + option = Hamlib.value_t() + ant = 0 # FIXME should use a RIG_ANT_* constant but it isn't available in the bindings + assert isinstance(rig.get_ant(option, ant), list) + assert isinstance(rig.get_ant(option, ant, Hamlib.RIG_VFO_CURR), list) + assert option.i == 0 + assert rig.get_chan_all() is None + channel = 0 + readonly = 0 + assert isinstance(rig.get_channel(channel), Hamlib.channel) + assert isinstance(rig.get_channel(channel, Hamlib.RIG_VFO_CURR), Hamlib.channel) + assert isinstance(rig.get_channel(channel, Hamlib.RIG_VFO_CURR, readonly), Hamlib.channel) + assert isinstance(rig.get_conf(""), str) + assert isinstance(rig.get_ctcss_sql(), int) + assert isinstance(rig.get_ctcss_sql(0), int) + assert isinstance(rig.get_ctcss_tone(), int) + assert isinstance(rig.get_ctcss_tone(0), int) + assert isinstance(rig.get_dcd(), int) + assert isinstance(rig.get_dcd(Hamlib.RIG_VFO_CURR,), int) + assert isinstance(rig.get_dcs_code(), int) + assert isinstance(rig.get_dcs_code(0), int) + assert isinstance(rig.get_dcs_sql(), int) + assert isinstance(rig.get_dcs_sql(0), int) + assert isinstance(rig.get_ext_func(0), int) + assert isinstance(rig.get_ext_func(0, 0), int) + assert rig.get_ext_level(0) is None + assert rig.get_ext_level(0, 0) is None + assert rig.get_ext_parm(0) is None + assert isinstance(rig.get_freq(), float) + assert isinstance(rig.get_freq(Hamlib.RIG_VFO_CURR), float) + assert isinstance(rig.get_func(0), int) + assert isinstance(rig.get_func(0, 0), int) + assert rig.get_info() is None + assert rig.get_level(0) is None + assert isinstance(rig.get_level_f(0), float) + assert isinstance(rig.get_level_i(0), int) + assert isinstance(rig.get_mem(), int) + assert isinstance(rig.get_mem(Hamlib.RIG_VFO_CURR), int) + assert isinstance(rig.get_mode(), list) + assert isinstance(rig.get_mode(Hamlib.RIG_VFO_CURR), list) + assert rig.get_parm(0) is None + assert isinstance(rig.get_parm_f(0), float) + assert isinstance(rig.get_parm_i(0), int) + assert isinstance(rig.get_powerstat(), int) + assert isinstance(rig.get_ptt(), int) + assert isinstance(rig.get_ptt(Hamlib.RIG_VFO_CURR), int) + assert isinstance(rig.get_rit(), int) + assert isinstance(rig.get_rit(Hamlib.RIG_VFO_CURR), int) + assert isinstance(rig.get_rptr_offs(), int) + assert isinstance(rig.get_rptr_offs(Hamlib.RIG_VFO_CURR), int) + assert isinstance(rig.get_rptr_shift(), int) + assert isinstance(rig.get_rptr_shift(Hamlib.RIG_VFO_CURR), int) + assert isinstance(rig.get_split_freq(), float) + assert isinstance(rig.get_split_freq(Hamlib.RIG_VFO_CURR), float) + assert isinstance(rig.get_split_mode(), list) + assert isinstance(rig.get_split_mode(Hamlib.RIG_VFO_CURR), list) + with raises(TypeError): + assert rig.get_split_vfo() is None # FIXME + assert rig.get_split_vfo(Hamlib.RIG_VFO_CURR) is None # FIXME + assert isinstance(rig.get_trn(), int) # deprecated + assert isinstance(rig.get_ts(), int) + assert isinstance(rig.get_ts(Hamlib.RIG_VFO_CURR), int) + assert isinstance(rig.get_vfo(), int) + with raises(TypeError): + assert rig.get_vfo_info(Hamlib.RIG_VFO_CURR) is None # FIXME + assert rig.get_vfo_info(Hamlib.RIG_VFO_CURR, 2) is None # FIXME + assert rig.get_vfo_info(Hamlib.RIG_VFO_CURR, 2, 3) is None # FIXME + assert isinstance(rig.get_xit(), int) + assert isinstance(rig.get_xit(Hamlib.RIG_VFO_CURR), int) + # assert rig_has_get_func(0) FIXME not defined + assert rig.has_get_level(0) is None # FIXME should return setting_t + # assert rig_has_get_parm(0) FIXME not defined + assert rig.has_scan(0) is None # FIXME should return scan_t + assert rig.has_set_func(0) is None # FIXME should return setting_t + # assert rig_has_set_level(0) FIXME not defined + assert rig.has_set_parm(0) is None # FIXME should return setting_t + assert rig.has_vfo_op(0) is None # FIXME should return vfo_op_t + assert rig.lookup_mem_caps() is None + assert rig.lookup_mem_caps(0) is None + assert isinstance(rig.mem_count(), int) + assert rig.open() is None + assert rig.passband_narrow(0) is None + assert rig.passband_normal(0) is None + assert rig.passband_wide(0) is None + assert isinstance(rig.recv_dtmf(), str) + assert isinstance(rig.recv_dtmf(0), str) + assert rig.reset(Hamlib.RIG_RESET_NONE) is None + assert rig.scan(0, 0) is None + assert rig.scan(0, 0, 0) is None + assert rig.send_dtmf(0, "") is None + assert rig.send_morse(0, "") is None + option = Hamlib.value_t() + option.i = 0 # FIXME should use a RIG_ANT_* constant but it isn't available in the bindings + assert rig.set_ant(Hamlib.RIG_VFO_CURR, option) is None + assert rig.set_ant(Hamlib.RIG_VFO_CURR, option, 0) is None + assert rig.set_bank(0, 0) is None + channel = Hamlib.channel() + channel = Hamlib.channel(0) + channel = Hamlib.channel(0, 0) + assert rig.set_channel(channel) is None + assert rig.set_conf("", "") is None + assert rig.set_ctcss_sql(0, 0) is None + assert rig.set_ctcss_tone(0, 0) is None + assert rig.set_dcs_code(0, 0) is None + assert rig.set_dcs_sql(0) is None + assert rig.set_dcs_sql(0, 0) is None + assert rig.set_ext_func(0, 0) is None + assert rig.set_ext_func(0, 0, 0) is None + level = 0 + value = Hamlib.value_t() + assert rig.set_ext_level(level, value) is None + assert rig.set_ext_level(level, value, Hamlib.RIG_VFO_CURR) is None + value = Hamlib.value_t() + assert rig.set_ext_parm(0, value) is None + assert rig.set_freq(0, 0) is None + assert rig.set_func(0, 0, 0) is None + assert rig.set_level(0, 0, 0) is None + assert rig.set_mem(0, 0) is None + assert rig.set_mode(0) is None + assert rig.set_mode(0, 0) is None + assert rig.set_mode(0, 0, 0) is None + assert rig.set_parm(0, 0) is None + assert rig.set_powerstat(0) is None + assert rig.set_ptt(0, 0) is None + assert rig.set_rit(0, 0) is None + assert rig.set_rptr_offs(0, 0) is None + assert rig.set_rptr_shift(0, 0) is None + assert rig.set_split_freq(0, 0) is None + assert rig.set_split_freq_mode(0, 0) is None + assert rig.set_split_freq_mode(0, 0, 0) is None + assert rig.set_split_freq_mode(0, 0, 0, 0) is None + assert rig.set_split_mode(0) is None + assert rig.set_split_mode(0, 0) is None + assert rig.set_split_mode(0, 0, 0) is None + assert rig.set_split_vfo(0, 0) is None + assert rig.set_split_vfo(0, 0, 0) is None + assert rig.set_trn(0) is None # deprecated + assert rig.set_ts(0, 0) is None + assert rig.set_vfo(0) is None + assert rig.set_vfo_opt(0) is None + assert rig.set_xit(0, 0) is None + assert rig.token_lookup("") is None + assert rig.vfo_op(0, 0) is None + + + def test_object_creation(self): + """Create all objects available""" + rig = Hamlib.Rig(RIG_MODEL) + assert rig is not None + + assert isinstance(rig.caps, Hamlib.rig_caps) + assert isinstance(rig.state, Hamlib.rig_state) diff --git a/bindings/python/test_rot.py b/bindings/python/test_rot.py new file mode 100644 index 000000000..f51ea46ce --- /dev/null +++ b/bindings/python/test_rot.py @@ -0,0 +1,115 @@ +#! /bin/env pytest +"""Tests of the Python bindings for Hamlib + +Running this script directly will use the installed bindings. +For an in-tree run use "make check", or set PYTHONPATH to point to +the directories containing Hamlib.py and _Hamlib.so. +""" +from pytest import raises + +import Hamlib + +Hamlib.rig_set_debug(Hamlib.RIG_DEBUG_NONE) + +ROT_MODEL = Hamlib.ROT_MODEL_DUMMY + +class TestClass: + """Container class for tests""" + + TOK_EL_ROT_MAGICLEVEL = 1 + TOK_EL_ROT_MAGICFUNC = 2 + # TOK_EL_ROT_MAGICOP = 3 # handled by get_ext_level/set_ext_level + TOK_EP_ROT_MAGICPARM = 4 + # TOK_EL_ROT_MAGICCOMBO = 5 # handled by get_ext_level/set_ext_level + TOK_EL_ROT_MAGICEXTFUNC = 6 + + def test_without_open(self): + """Call all the methods that do not depend on open()""" + rot = Hamlib.Rot(ROT_MODEL) + assert rot is not None + assert rot.do_exception == 0 + assert rot.error_status == Hamlib.RIG_OK + + assert isinstance(rot.caps.model_name, str) + assert isinstance(rot.caps.mfg_name, str) + assert isinstance(rot.caps.version, str) + assert isinstance(rot.caps.copyright, str) + + assert rot.set_conf("", "") is None + assert rot.get_conf("") == "" + assert rot.get_conf(0) == "" + conf = rot.get_conf("mcfg") + assert isinstance(conf, str) + assert rot.set_conf("mcfg", "foo") is None + conf = rot.get_conf("mcfg") + assert conf == "foo" + + assert rot.token_lookup("") is None + + + def test_with_open(self): + """Call all the methods that depend on open()""" + rot = Hamlib.Rot(ROT_MODEL) + assert rot is not None + + assert rot.state.comm_state == 0 + assert rot.open() is None + assert rot.state.comm_state == 1 + info = rot.get_info() + assert isinstance(info, str) + assert rot.set_position(1.23, 4.56) is None + # When the Dummy rotator simulates movement, this test is too fast + # to see a changed position + assert rot.get_position() == [0.0, 0.0] # should be [1.23, 4.56] + assert rot.move(0, 0) is None + speed = 4 + assert rot.move(Hamlib.ROT_MOVE_UP, speed) is None + assert rot.move(Hamlib.ROT_MOVE_LEFT, speed) is None + # When the Dummy rotator simulates movement, this test is too fast + # to see a changed position + assert rot.get_position() == [0.0, 0.0] # should be [-90.0, 90.0] + value = Hamlib.value_t() + value.i = 3 + assert rot.set_level(Hamlib.ROT_LEVEL_SPEED, value) is None + assert rot.get_level(Hamlib.ROT_LEVEL_NONE) is None + assert rot.get_level(Hamlib.ROT_LEVEL_SPEED) == 3 + with raises(AttributeError): + assert rot.status() is None + assert rot.stop() is None + assert rot.park() is None + assert rot.reset(Hamlib.ROT_RESET_ALL) is None + + assert rot.set_ext_func(self.TOK_EL_ROT_MAGICEXTFUNC, 5) is None + assert rot.get_ext_func(self.TOK_EL_ROT_MAGICEXTFUNC) == 5 + + value.i = 6 + assert rot.set_ext_level(self.TOK_EL_ROT_MAGICLEVEL, value) is None + assert rot.get_ext_level(self.TOK_EL_ROT_MAGICLEVEL) == 6 + + value.i = 7 + assert rot.set_ext_parm(self.TOK_EP_ROT_MAGICPARM, value) is None + assert rot.get_ext_parm(self.TOK_EP_ROT_MAGICPARM) == 7 + + # Dummy rotator doesn't support functions + status = 0 + assert rot.set_func(Hamlib.ROT_FUNC_NONE, status) is None + assert rot.get_func(Hamlib.ROT_FUNC_NONE) is None + + # Dummy rotator doesn't support parameters + value.i = 5 + assert rot.set_parm(Hamlib.ROT_PARM_NONE, value) is None + assert rot.get_parm(Hamlib.ROT_PARM_NONE) is None + + assert rot.close() is None + assert rot.state.comm_state == 0 + info = rot.get_info() + assert info is None + + + def test_object_creation(self): + """Create all objects available""" + rot = Hamlib.Rig(ROT_MODEL) + assert rot is not None + + assert isinstance(rot.caps, Hamlib.rig_caps) + assert isinstance(rot.state, Hamlib.rig_state) diff --git a/bindings/python/test_startup.py b/bindings/python/test_startup.py new file mode 100755 index 000000000..e4cfaf8c0 --- /dev/null +++ b/bindings/python/test_startup.py @@ -0,0 +1,131 @@ +#! /bin/env pytest +"""Tests of the Python bindings for Hamlib + +Running this script directly will use the installed bindings. +For an in-tree run use "make check", or set PYTHONPATH to point to +the directories containing Hamlib.py and _Hamlib.so. +""" +from pytest import approx + +import Hamlib + +class TestClass: + """Container class for tests""" + + def test_startup(self): + """Simple script to test the Hamlib.py module with Python3.""" + + assert isinstance(Hamlib.cvar.hamlib_version, str) + + Hamlib.rig_set_debug(Hamlib.RIG_DEBUG_NONE) + + model = Hamlib.RIG_MODEL_DUMMY + my_rig = Hamlib.Rig(model) + assert my_rig is not None + assert my_rig.set_conf("rig_pathname", "/dev/Rig") is None + assert my_rig.set_conf("retry", "5") is None + + assert my_rig.open() is None + + assert my_rig.get_conf("rig_pathname") == "/dev/Rig" + assert my_rig.get_conf("retry") == '5' + + assert my_rig.error_status == Hamlib.RIG_OK + assert Hamlib.rigerror(my_rig.error_status) == "Command completed successfully\n" + + assert my_rig.set_freq(Hamlib.RIG_VFO_B, 5700000000) is None + assert my_rig.set_vfo(Hamlib.RIG_VFO_B) is None + assert my_rig.get_freq() == 5700000000 + + assert my_rig.set_freq(Hamlib.RIG_VFO_A, 145550000) is None + + (mode, width) = my_rig.get_mode(Hamlib.RIG_VFO_A) + assert Hamlib.rig_strrmode(mode) == 'FM' + assert width == 15000 + + assert my_rig.set_mode(Hamlib.RIG_MODE_CW) is None + (mode, width) = my_rig.get_mode() + assert Hamlib.rig_strrmode(mode) == 'CW' + assert width == 0 + + assert isinstance(my_rig.caps.copyright, str) + assert isinstance(my_rig.caps.model_name, str) + assert isinstance(my_rig.caps.mfg_name, str) + assert isinstance(my_rig.caps.version, str) + assert isinstance(Hamlib.rig_strstatus(my_rig.caps.status), str) + assert isinstance(my_rig.get_info(), str) + + assert my_rig.set_level("VOXDELAY", 1) is None + assert my_rig.get_level_i("VOXDELAY") == 1 + + assert my_rig.set_level(Hamlib.RIG_LEVEL_VOXDELAY, 5) is None + assert my_rig.get_level_i(Hamlib.RIG_LEVEL_VOXDELAY) == 5 + + af = 12.34 + assert my_rig.set_level("AF", af) is None + assert my_rig.error_status == Hamlib.RIG_OK + assert my_rig.get_level_f(Hamlib.RIG_LEVEL_AF) == approx(12.34) + assert my_rig.error_status == Hamlib.RIG_OK + + assert my_rig.get_level_f(Hamlib.RIG_LEVEL_RFPOWER_METER) + assert my_rig.get_level_f(Hamlib.RIG_LEVEL_RFPOWER_METER_WATTS) + assert my_rig.get_level_i(Hamlib.RIG_LEVEL_STRENGTH) + + chan = Hamlib.channel(Hamlib.RIG_VFO_B) + assert chan.vfo == Hamlib.RIG_VFO_B + assert my_rig.get_channel(chan, 1) is None + assert my_rig.error_status == Hamlib.RIG_OK + + assert Hamlib.rig_strvfo(chan.vfo) == 'MainB' + assert chan.freq == 5700000000 + assert my_rig.caps.attenuator == [10, 20, 30, 0, 0, 0, 0, 0] + # Can't seem to get get_vfo_info to work + #(freq, width, mode, split) = my_rig.get_vfo_info(Hamlib.RIG_VFO_A,freq,width,mode,split) + #print("Rig vfo_info:\t\tfreq=%s, mode=%s, width=%s, split=%s" % (freq, mode, width, split)) + + assert my_rig.send_morse(Hamlib.RIG_VFO_A, "73") is None + assert my_rig.close() is None + + # Some static functions + + err, lon1, lat1 = Hamlib.locator2longlat("IN98XC") + assert err == Hamlib.RIG_OK + assert lon1 == approx(-0.0417, abs=1.0e-04) # FIXME why need to override tolerance? + assert lat1 == approx(48.1042) + assert Hamlib.longlat2locator(lon1, lat1, 3) == [Hamlib.RIG_OK, 'IN98XC'] + + err, lon2, lat2 = Hamlib.locator2longlat("DM33DX") + assert err == Hamlib.RIG_OK + assert lon2 == approx(-113.7083) + assert lat2 == approx(33.9792) + assert Hamlib.longlat2locator(lon2, lat2, 3) == [Hamlib.RIG_OK, 'DM33DX'] + + err, dist, az = Hamlib.qrb(lon1, lat1, lon2, lat2) + assert err == Hamlib.RIG_OK + assert dist == approx(8765.814) + assert az == approx(309.0) + longpath = Hamlib.distance_long_path(dist) + assert longpath == approx(31266.186) + + # dec2dms expects values from 180 to -180 + # sw is 1 when deg is negative (west or south) as 0 cannot be signed + err, deg1, mins1, sec1, sw1 = Hamlib.dec2dms(lon1) + assert err == Hamlib.RIG_OK + assert deg1 == 0 + assert mins1 == 2 + assert sec1 == 29 + assert sw1 == 1 + + err, deg2, mins2, sec2, sw2 = Hamlib.dec2dms(lat1) + assert err == Hamlib.RIG_OK + assert deg2 == 48 + assert mins2 == 6 + assert sec2 == 15 + assert sw2 == 0 + + lon3 = Hamlib.dms2dec(deg1, mins1, sec1, sw1) + lat3 = Hamlib.dms2dec(deg2, mins2, sec2, sw2) + assert lon3 == approx(-0.0414, abs=1.0e-04) # FIXME why need to override tolerance? + assert lat3 == approx(48.1042) + + assert my_rig.set_vfo_opt(0) is None diff --git a/bindings/rig.swg b/bindings/rig.swg index a101d1575..e0f3373e6 100644 --- a/bindings/rig.swg +++ b/bindings/rig.swg @@ -18,6 +18,17 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ +%{ +#include +%} + +%immutable rig_caps::model_name; +%immutable rig_caps::mfg_name; +%immutable rig_caps::version; +%immutable rig_caps::copyright; + +%include +%include %inline %{ @@ -453,7 +464,7 @@ typedef channel_t * const_channel_t_p; METHOD1VGET(get_rit, shortfreq_t) METHOD1VGET(get_xit, shortfreq_t) METHOD1VGET(get_ts, shortfreq_t) - extern void get_ant(ant_t *ant_rx, ant_t *ant_tx, ant_t *ant_curr, value_t * OUTPUT, ant_t ant, vfo_t vfo = RIG_VFO_CURR); + extern void get_ant(ant_t *OUTPUT, ant_t *OUTPUT, ant_t *OUTPUT, value_t *OUTPUT, ant_t ant, vfo_t vfo = RIG_VFO_CURR); extern void get_vfo_info (int *satmode, split_t *split, pbwidth_t *width, rmode_t *mode, freq_t *freq, vfo_t vfo = RIG_VFO_CURR); METHOD1VGET(get_mem, int) METHOD1GET(get_powerstat, powerstat_t) @@ -478,10 +489,6 @@ typedef channel_t * const_channel_t_p; /* TODO: handle ext_level's */ } - void chan_clear(struct channel *chans, int nb_chans = 1) { - memset(chans, 0, sizeof(struct channel)*nb_chans); - } - void get_chan_all(struct channel *chans) { self->error_status = rig_get_chan_all(self->rig, RIG_VFO_NONE, chans); /* TODO: handle ext_level's */ diff --git a/bindings/rotator.swg b/bindings/rotator.swg index 2e527fcef..7cff51045 100644 --- a/bindings/rotator.swg +++ b/bindings/rotator.swg @@ -18,6 +18,18 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ +%{ +#include +%} + +%include macros.swg +%immutable rot_caps::model_name; +%immutable rot_caps::mfg_name; +%immutable rot_caps::version; +%immutable rot_caps::copyright; + +%include +%include %inline %{ @@ -78,6 +90,16 @@ typedef struct Rot { ROTMETHOD0(open) ROTMETHOD0(close) + ROTMETHOD2(set_ext_func, hamlib_token_t, int) + ROTMETHOD2(set_ext_level, hamlib_token_t, value_t) + ROTMETHOD2(set_ext_parm, hamlib_token_t, value_t) + + ROTMETHOD2(set_func, setting_t, int) + + ROTMETHOD2(set_level, setting_t, value_t) + + ROTMETHOD2(set_parm, setting_t, value_t) + ROTMETHOD2(set_position, azimuth_t, elevation_t) extern void get_position(azimuth_t *OUTPUT, elevation_t *OUTPUT); ROTMETHOD0(stop) @@ -97,6 +119,15 @@ typedef struct Rot { ROTMETHOD2(set_conf, hamlib_token_t, const_char_string) + ROT_GET_LONG(func) + ROT_GET_LONG(ext_func) + + ROT_GET_VALUE_T(level) + ROT_GET_VALUE_T(ext_level) + + ROT_GET_VALUE_T(parm) + ROT_GET_VALUE_T(ext_parm) + const char *get_conf(hamlib_token_t tok) { static char s[128] = ""; self->error_status = rot_get_conf(self->rot, tok, s); diff --git a/configure.ac b/configure.ac index 4ef857e1d..0c3ec35b8 100644 --- a/configure.ac +++ b/configure.ac @@ -615,9 +615,32 @@ AS_IF([test x"${cf_with_python_binding}" = "xyes"],[ AM_CONDITIONAL([ENABLE_PYTHON], [test x"${cf_with_python_binding}" = "xyes"]) -dnl Determine whether to install pytest.py or py3test.py to $(docdir)/examples -AM_PYTHON_CHECK_VERSION([${PYTHON}], [3.0], [pyver_3="yes"], [pyver_3="no"]) -AM_CONDITIONAL([PYVER_3], [test x"${pyver_3}" = x"yes"]) +AC_ARG_ENABLE([pytest], + [AS_HELP_STRING([--enable-pytest], + [execute Python tests @<:@default=check@:>@]) + ], + [enable_pytest=check] +) + +AS_IF([test "x$enable_pytest" != "xno"], + [AC_PATH_PROG([PYTEST], [pytest])] +) + +AS_IF([test "x$PYTEST" = "x"], + [have_pytest=no], + [have_pytest=yes] +) + +AS_IF([test "x$enable_pytest" = "xyes"], + [AS_IF([test "x$have_pytest" = "xno"], + [AC_MSG_ERROR([pytest requested but not found])] + )] +) + +# "check" is replaced by the rsult ("yes" or "no") +enable_pytest=$have_pytest + +AC_SUBST([enable_pytest]) # Tcl binding AC_MSG_CHECKING([Whether to build Tcl bindings]) @@ -980,5 +1003,6 @@ echo \ Enable USB backends ${cf_with_libusb} Enable shared libs ${enable_shared} Enable static libs ${enable_static} + Enable Python tests ${enable_pytest} -----------------------------------------------------------------------"