[Hamlib-commits] Hamlib -- Ham radio control libraries branch master updated. 10303d58038e4b35c00d9
Library to control radio transceivers and receivers
Brought to you by:
n0nb
From: n0nb <n0...@us...> - 2025-08-09 23:43:08
|
This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "Hamlib -- Ham radio control libraries". The branch, master has been updated via 10303d58038e4b35c00d94c35c417ca7cc2fb73c (commit) via 81cb260670471689d7a3543153bb5c56cc26b5ce (commit) via e2214fd2f18f326c0325f2e7abb7c892d13885aa (commit) via 2890da9d79fdcdd070fbc924cd326a5d35d0b53d (commit) via 8e4bcbcede1c42a39b74371c5b337bac2c49ff16 (commit) via 9e1c7b5ec1f33dea1cf5a263c2ec8e12f791ac06 (commit) via 3bc1cc8744e973e314882f1c43a58478c1346e4c (commit) via 8324c85487c416025f6907a920f3528e36c07b8e (commit) from 2462ab0a135ca0274a069e27c319c90957d70ac1 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 10303d58038e4b35c00d94c35c417ca7cc2fb73c Author: Daniele Forsi IU5HKX <iu...@gm...> Date: Sat Aug 9 18:09:17 2025 +0200 Fix function signature of spectrum_callback() diff --git a/bindings/python/test_rig.py b/bindings/python/test_rig.py index 474fe1d1f..5c0c305ec 100755 --- a/bindings/python/test_rig.py +++ b/bindings/python/test_rig.py @@ -181,8 +181,8 @@ class TestClass: assert rig.set_pltune_callback(None) is None # spectrum event callback - def spectrum_callback(vfo, ptt, arg): - print("spectrum_callback", vfo, ptt, arg) + def spectrum_callback(rig_spectrum_line, arg): + print("spectrum_callback", rig_spectrum_line, arg) assert (1, 5000, 2345678901) == (vfo, arg) assert rig.set_spectrum_callback(spectrum_callback, 7890123456) is None commit 81cb260670471689d7a3543153bb5c56cc26b5ce Author: Daniele Forsi IU5HKX <iu...@gm...> Date: Sat Aug 9 14:54:31 2025 +0200 Fix ptt_callback() diff --git a/bindings/python/test_rig.py b/bindings/python/test_rig.py index 315b8c5d2..474fe1d1f 100755 --- a/bindings/python/test_rig.py +++ b/bindings/python/test_rig.py @@ -153,8 +153,7 @@ class TestClass: # PTT event callback def ptt_callback(vfo, ptt, arg): - print("ptt_callback", vfo, ptt, arg) - assert (1, 5000, 4567890123) == (vfo, arg) + assert (Hamlib.RIG_VFO_CURR, Hamlib.RIG_PTT_ON, 4567890123) == (vfo, ptt, arg) assert rig.set_ptt_callback(ptt_callback, 4567890123) is None assert rig.set_ptt(Hamlib.RIG_VFO_CURR, Hamlib.RIG_PTT_ON) is None commit e2214fd2f18f326c0325f2e7abb7c892d13885aa Author: Daniele Forsi IU5HKX <iu...@gm...> Date: Sat Aug 9 14:32:59 2025 +0200 Change .ptt_type to RIG_PTT_RIG for RIG_MODEL_DUMMY With the old value of RIG_PTT_NONE, dummy_get_ptt() and dummy_set_ptt() were never called because rig_get_ptt() and rig_set_ptt() do check .ptt_type and return -RIG_ENAVAIL in that case. Do not change .ptt_type for RIG_MODEL_DUMMY_NOVFO so that both cases can be tested if needed. diff --git a/rigs/dummy/dummy.c b/rigs/dummy/dummy.c index 3f54a655e..544de59cc 100644 --- a/rigs/dummy/dummy.c +++ b/rigs/dummy/dummy.c @@ -2460,7 +2460,7 @@ struct rig_caps dummy_caps = .status = RIG_STATUS_STABLE, .rig_type = RIG_TYPE_OTHER, .targetable_vfo = RIG_TARGETABLE_PTT | RIG_TARGETABLE_RITXIT | RIG_TARGETABLE_FREQ | RIG_TARGETABLE_MODE | RIG_TARGETABLE_SPECTRUM, - .ptt_type = RIG_PTT_NONE, + .ptt_type = RIG_PTT_RIG, .dcd_type = RIG_DCD_RIG, .port_type = RIG_PORT_NONE, .has_get_func = DUMMY_FUNC, commit 2890da9d79fdcdd070fbc924cd326a5d35d0b53d Author: Daniele Forsi IU5HKX <iu...@gm...> Date: Sat Aug 9 10:50:10 2025 +0200 Split the tests It makes it easier in future to conditionally run some tests(eg. to not test PTT for receiver-only rigs), or to run tests in isolation (after renaming them). It makes it easier to look at failing tests because pytest prints the code of the failing test up to the failure including any function that called the failed test (in this case would print everything from def test_with_open() up to the line with the failed assert. diff --git a/bindings/python/test_rig.py b/bindings/python/test_rig.py index e7966139b..315b8c5d2 100755 --- a/bindings/python/test_rig.py +++ b/bindings/python/test_rig.py @@ -41,23 +41,10 @@ class TestClass: assert rig.token_lookup("") is None - def test_with_open(self, model, rig_file, serial_speed): - """Call all the methods that depend on open()""" - rig = Hamlib.Rig(model) - assert rig is not None - - assert rig.state.comm_state == 0 - assert rig.state.comm_status == Hamlib.RIG_COMM_STATUS_DISCONNECTED - assert rig.set_conf("rig_pathname", rig_file) is None - assert rig.set_conf("serial_speed", str(serial_speed)) is None - assert rig.open() is None - assert rig.state.comm_state == 1 - assert rig.state.comm_status == Hamlib.RIG_COMM_STATUS_OK - info = rig.get_info() - assert isinstance(info, str) - - # Frequency + def do_test_frequency(self, rig): + """Frequency tests""" + # TODO use a frequency suitable for the VFO frequency = 5700000000 assert rig.set_freq(Hamlib.RIG_VFO_CURR, frequency) is None assert rig.get_freq() == 5700000000.0 @@ -66,7 +53,9 @@ class TestClass: assert isinstance(rig.get_freq(Hamlib.RIG_VFO_CURR), float) assert rig.get_freq(Hamlib.RIG_VFO_CURR) == 5700000000.5 - # VFO + + def do_test_vfo(self, rig): + """VFO tests""" assert rig.set_vfo(Hamlib.RIG_VFO_A) is None assert rig.get_vfo() == Hamlib.RIG_VFO_A @@ -78,7 +67,9 @@ class TestClass: assert rig.get_split_vfo() == [Hamlib.RIG_SPLIT_OFF, Hamlib.RIG_VFO_B] assert rig.get_split_vfo(Hamlib.RIG_VFO_CURR) == [Hamlib.RIG_SPLIT_OFF, Hamlib.RIG_VFO_B] - # RIT and XIT + + def do_test_rit_xit(self, rig): + """RIT and XIT tests""" assert rig.set_rit(Hamlib.RIG_VFO_CURR, 100) is None assert rig.get_rit() == 100 @@ -87,7 +78,9 @@ class TestClass: assert rig.get_xit() == 200 assert rig.get_xit(Hamlib.RIG_VFO_CURR) == 200 - # Antenna + + def do_test_antenna(self, rig): + """Antenna tests""" # FIXME should use a RIG_ANT_* constant but they aren't available in the bindings RIG_ANT_1 = 1<<0 @@ -101,7 +94,9 @@ class TestClass: assert rig.set_ant(RIG_ANT_1, option, Hamlib.RIG_VFO_CURR) is None assert rig.get_ant(RIG_ANT_CURR, Hamlib.RIG_VFO_A) == expected - # Squelch codes and tones + + def do_test_squelch(self, rig): + """Squelch codes and tones""" assert rig.set_ctcss_sql(Hamlib.RIG_VFO_CURR, 885) is None assert rig.get_ctcss_sql() == 885 @@ -116,16 +111,10 @@ class TestClass: assert rig.get_dcs_sql() == 134 assert rig.get_dcs_sql(Hamlib.RIG_VFO_CURR) == 134 - # Callbacks - self.do_test_callbacks(rig) - assert rig.close() is None - assert rig.state.comm_state == 0 - assert rig.state.comm_status == Hamlib.RIG_COMM_STATUS_DISCONNECTED - info = rig.get_info() - assert info is None + def do_test_callback(self, rig): + """Callback tests""" - def do_test_callbacks(self, rig): # Frequency event callback def freq_callback(vfo, freq, arg): assert (1, 144200000.5, 1234567890) == (vfo, freq, arg) @@ -202,6 +191,35 @@ class TestClass: assert rig.set_spectrum_callback(None) is None + def test_with_open(self, model, rig_file, serial_speed): + """Call all the methods that depend on open()""" + rig = Hamlib.Rig(model) + assert rig is not None + + assert rig.state.comm_state == 0 + assert rig.state.comm_status == Hamlib.RIG_COMM_STATUS_DISCONNECTED + assert rig.set_conf("rig_pathname", rig_file) is None + assert rig.set_conf("serial_speed", str(serial_speed)) is None + assert rig.open() is None + assert rig.state.comm_state == 1 + assert rig.state.comm_status == Hamlib.RIG_COMM_STATUS_OK + info = rig.get_info() + assert isinstance(info, str) + + self.do_test_frequency(rig) + self.do_test_vfo(rig) + self.do_test_rit_xit(rig) + self.do_test_antenna(rig) + self.do_test_squelch(rig) + self.do_test_callback(rig) + + assert rig.close() is None + assert rig.state.comm_state == 0 + assert rig.state.comm_status == Hamlib.RIG_COMM_STATUS_DISCONNECTED + info = rig.get_info() + assert info is None + + @pytest.mark.skipif('config.getoption("model") != Hamlib.RIG_MODEL_DUMMY') def test_misc(self, model): """Just call all the methods""" commit 8e4bcbcede1c42a39b74371c5b337bac2c49ff16 Author: Daniele Forsi IU5HKX <iu...@gm...> Date: Fri Aug 8 23:26:18 2025 +0200 Add another cross-reference in a Doxygen comment diff --git a/include/hamlib/rig.h b/include/hamlib/rig.h index a6f472bda..72eb3d105 100644 --- a/include/hamlib/rig.h +++ b/include/hamlib/rig.h @@ -2652,7 +2652,8 @@ typedef int (*spectrum_cb_t)(RIG *, * really appropriate in a GUI. * * \sa rig_set_dcd_callback(), rig_set_freq_callback(), rig_set_mode_callback(), - * rig_set_ptt_callback(), rig_set_spectrum_callback(), rig_set_vfo_callback() + * rig_set_pltune_callback(), rig_set_ptt_callback(), rig_set_spectrum_callback(), + * rig_set_vfo_callback() */ // Do NOT add/remove from this structure -- it will break DLL backwards compatibility struct rig_callbacks { commit 9e1c7b5ec1f33dea1cf5a263c2ec8e12f791ac06 Author: Daniele Forsi IU5HKX <iu...@gm...> Date: Fri Aug 8 23:36:38 2025 +0200 Allocate zereod memory for struct python_callbacks Otherwise Py_XDECREF() would randomly segfault trying to use uninitialized memory that by chance is not zeroed. diff --git a/bindings/ignore.swg b/bindings/ignore.swg index 4114aabc4..34f634034 100644 --- a/bindings/ignore.swg +++ b/bindings/ignore.swg @@ -68,7 +68,6 @@ %rename("%s", regexmatch$name="^RIG_VFO_") ""; %ignore RIG_EK; // an internal macro %ignore RIG_ELAD; // an internal macro -%rename("$ignore", regexmatch$name="python_callbacks$") ""; // internal structs and methods used by bindings // Rotators @@ -112,6 +111,8 @@ %ignore PRIfreq; %ignore SCNfreq; +%rename("$ignore", regexmatch$name="python_callbacks") ""; // internal structs and methods used by bindings + #ifdef SWIGLUA %ignore Rig::set_level(setting_t,int,vfo_t); %ignore Rig::set_ext_level(setting_t,value_t,vfo_t); diff --git a/bindings/macros.swg b/bindings/macros.swg index 888991858..29396a47a 100644 --- a/bindings/macros.swg +++ b/bindings/macros.swg @@ -122,13 +122,13 @@ void set_ ## event_name ## _callback(PyObject *cb, PyObject *arg=NULL) }; Py_XINCREF(cb); - Py_XDECREF(self->python_callbacks. ## event_name ## _event); + Py_XDECREF(self->python_callbacks-> ## event_name ## _event); Py_XINCREF(arg); - Py_XDECREF(self->python_callbacks. ## event_name ## _arg); + Py_XDECREF(self->python_callbacks-> ## event_name ## _arg); - self->python_callbacks. ## event_name ## _event = cb; - self->python_callbacks. ## event_name ## _arg = arg; + self->python_callbacks-> ## event_name ## _event = cb; + self->python_callbacks-> ## event_name ## _arg = arg; self->error_status = function_prefix ## set_ ## event_name ## _callback(class_pointer, callback, self); return; diff --git a/bindings/rig.swg b/bindings/rig.swg index 2be00320a..a062bcfef 100644 --- a/bindings/rig.swg +++ b/bindings/rig.swg @@ -45,16 +45,9 @@ #pragma SWIG nowarn=451 -typedef struct Rig { - RIG *rig; - struct rig_caps *caps; /* shortcut to RIG->caps */ - struct rig_state *state; /* shortcut to RIG->state */ - int error_status; - int do_exception; - // Handling of event callbacks #ifdef SWIGPYTHON // This mirrors "struct rig_callbacks" from rig.h using Python types - struct { + typedef struct { PyObject *freq_event; /*!< Frequency change event */ PyObject *freq_arg; /*!< Frequency change argument */ PyObject *mode_event; /*!< Mode change event */ @@ -70,7 +63,17 @@ typedef struct Rig { PyObject *spectrum_event; /*!< Spectrum line reception event */ PyObject *spectrum_arg; /*!< Spectrum line reception argument */ /* etc.. */ - } python_callbacks; + } python_callbacks_t; +#endif + +typedef struct Rig { + RIG *rig; + struct rig_caps *caps; /* shortcut to RIG->caps */ + struct rig_state *state; /* shortcut to RIG->state */ + int error_status; + int do_exception; +#ifdef SWIGPYTHON + python_callbacks_t *python_callbacks; #endif } Rig; @@ -87,10 +90,10 @@ int *rig_freq_cb_python(RIG *rig, vfo_t vfo, freq_t freq, rig_ptr_t arg) python_arguments = PyTuple_Pack(3, PyLong_FromLong(vfo), PyFloat_FromDouble(freq), - self->python_callbacks.freq_arg + self->python_callbacks->freq_arg ); - PyObject_CallObject(self->python_callbacks.freq_event, python_arguments); + PyObject_CallObject(self->python_callbacks->freq_event, python_arguments); Py_XDECREF(python_arguments); return RIG_OK; @@ -105,10 +108,10 @@ int *rig_mode_cb_python(RIG *rig, vfo_t vfo, rmode_t rmode, pbwidth_t pbwidth, r PyLong_FromLong(vfo), PyLong_FromLong(rmode), PyLong_FromLong(pbwidth), - self->python_callbacks.mode_arg + self->python_callbacks->mode_arg ); - PyObject_CallObject(self->python_callbacks.mode_event, python_arguments); + PyObject_CallObject(self->python_callbacks->mode_event, python_arguments); Py_XDECREF(python_arguments); @@ -120,11 +123,12 @@ int *rig_vfo_cb_python(RIG *rig, vfo_t vfo, rig_ptr_t arg) Rig *self = arg; PyObject *python_arguments; - python_arguments = PyTuple_Pack(1, - self->python_callbacks.vfo_arg + python_arguments = PyTuple_Pack(2, + PyLong_FromLong(vfo), + self->python_callbacks->vfo_arg ); - PyObject_CallObject(self->python_callbacks.vfo_event, python_arguments); + PyObject_CallObject(self->python_callbacks->vfo_event, python_arguments); Py_XDECREF(python_arguments); @@ -136,27 +140,31 @@ int *rig_ptt_cb_python(RIG *rig, vfo_t vfo, ptt_t ptt, rig_ptr_t arg) Rig *self = arg; PyObject *python_arguments; - python_arguments = PyTuple_Pack(1, - self->python_callbacks.ptt_arg + python_arguments = PyTuple_Pack(3, + PyLong_FromLong(vfo), + PyLong_FromLong(ptt), + self->python_callbacks->ptt_arg ); - PyObject_CallObject(self->python_callbacks.ptt_event, python_arguments); + PyObject_CallObject(self->python_callbacks->ptt_event, python_arguments); Py_XDECREF(python_arguments); return RIG_OK; } -int *rig_dcd_cb_python(RIG *rig, vfo_t vfo, dcd_t, rig_ptr_t arg) +int *rig_dcd_cb_python(RIG *rig, vfo_t vfo, dcd_t dcd, rig_ptr_t arg) { Rig *self = arg; PyObject *python_arguments; - python_arguments = PyTuple_Pack(1, - self->python_callbacks.dcd_arg + python_arguments = PyTuple_Pack(3, + PyLong_FromLong(vfo), + PyLong_FromLong(dcd), + self->python_callbacks->dcd_arg ); - PyObject_CallObject(self->python_callbacks.dcd_event, python_arguments); + PyObject_CallObject(self->python_callbacks->dcd_event, python_arguments); Py_XDECREF(python_arguments); @@ -169,10 +177,10 @@ int *rig_pltune_cb_python(RIG *rig, vfo_t vfo, freq_t *freq, rmode_t *rmode, pbw PyObject *python_arguments; python_arguments = PyTuple_Pack(1, - self->python_callbacks.pltune_arg + self->python_callbacks->pltune_arg ); - PyObject_CallObject(self->python_callbacks.pltune_event, python_arguments); + PyObject_CallObject(self->python_callbacks->pltune_event, python_arguments); Py_XDECREF(python_arguments); @@ -185,10 +193,10 @@ int *rig_spectrum_cb_python(RIG *rig, struct rig_spectrum_line *rig_spectrum_lin PyObject *python_arguments; python_arguments = PyTuple_Pack(1, - self->python_callbacks.spectrum_arg + self->python_callbacks->spectrum_arg ); - PyObject_CallObject(self->python_callbacks.spectrum_event, python_arguments); + PyObject_CallObject(self->python_callbacks->spectrum_event, python_arguments); Py_XDECREF(python_arguments); @@ -468,6 +476,13 @@ int *rig_spectrum_cb_python(RIG *rig, struct rig_spectrum_line *rig_spectrum_lin free(r); return NULL; } +#ifdef SWIGPYTHON + r->python_callbacks = calloc(1, sizeof(python_callbacks_t)); + if (!r->python_callbacks) { + free(r); + return NULL; + } +#endif /* install shortcuts */ r->caps = r->rig->caps; r->state = &r->rig->state; @@ -478,6 +493,9 @@ int *rig_spectrum_cb_python(RIG *rig, struct rig_spectrum_line *rig_spectrum_lin } ~Rig () { rig_cleanup(self->rig); +#ifdef SWIGPYTHON + free(self->python_callbacks); +#endif free(self); } commit 3bc1cc8744e973e314882f1c43a58478c1346e4c Author: Daniele Forsi IU5HKX <iu...@gm...> Date: Fri Aug 8 12:00:57 2025 +0200 Implement remaining event callbacks Implements: rig_set_dcd_callback(), rig_set_freq_callback(), rig_set_mode_callback(), rig_pltune_callback(), rig_set_ptt_callback(), rig_set_spectrum_callback(), rig_set_vfo_callback() diff --git a/bindings/python/test_Hamlib_Rig_class.py b/bindings/python/test_Hamlib_Rig_class.py index 3ebbc8c5b..3f60f0ce6 100755 --- a/bindings/python/test_Hamlib_Rig_class.py +++ b/bindings/python/test_Hamlib_Rig_class.py @@ -76,6 +76,7 @@ class TestClass: 'set_conf', 'set_ctcss_sql', 'set_ctcss_tone', +'set_dcd_callback', 'set_dcs_code', 'set_dcs_sql', 'set_ext_func', @@ -87,12 +88,16 @@ class TestClass: 'set_level', 'set_mem', 'set_mode', +'set_mode_callback', 'set_parm', +'set_pltune_callback', 'set_powerstat', 'set_ptt', +'set_ptt_callback', 'set_rit', 'set_rptr_offs', 'set_rptr_shift', +'set_spectrum_callback', 'set_split_freq', 'set_split_freq_mode', 'set_split_mode', @@ -100,6 +105,7 @@ class TestClass: 'set_trn', 'set_ts', 'set_vfo', +'set_vfo_callback', 'set_vfo_opt', 'set_xit', 'token_lookup', diff --git a/bindings/python/test_rig.py b/bindings/python/test_rig.py index 2b5dd863e..e7966139b 100755 --- a/bindings/python/test_rig.py +++ b/bindings/python/test_rig.py @@ -137,6 +137,70 @@ class TestClass: assert rig.set_freq(Hamlib.RIG_VFO_CURR, 144210000) is None # TODO assert that freq_callback() is called once + # Mode event callback + def mode_callback(vfo, mode, pbwidth, arg): + assert (1, 32, 5000, 2345678901) == (vfo, mode, pbwidth, arg) + + # FIXME should use a Hamlib.RIG_PASSBAND_* constant but they aren't available in the bindings + RIG_PASSBAND_NOCHANGE = -1 + assert rig.set_mode_callback(mode_callback, 2345678901) is None + assert rig.set_mode(Hamlib.RIG_MODE_FM, 5000) is None + # TODO assert that mode_callback() is called once + assert rig.set_mode_callback(None) is None + assert rig.set_mode(Hamlib.RIG_MODE_FM, 15000) is None + # TODO assert that mode_callback() is called once + + # VFO event callback + def vfo_callback(vfo, arg): + assert (1, 3456789012) == (vfo, arg) + + assert rig.set_vfo(Hamlib.RIG_VFO_B) is None + assert rig.set_vfo_callback(vfo_callback, 3456789012) is None + assert rig.set_vfo(Hamlib.RIG_VFO_A) is None + # TODO assert that vfo_callback() is called once + assert rig.set_vfo_callback(None) is None + assert rig.set_vfo(Hamlib.RIG_VFO_CURR) is None + # TODO assert that vfo_callback() is called once + + # PTT event callback + def ptt_callback(vfo, ptt, arg): + print("ptt_callback", vfo, ptt, arg) + assert (1, 5000, 4567890123) == (vfo, arg) + + assert rig.set_ptt_callback(ptt_callback, 4567890123) is None + assert rig.set_ptt(Hamlib.RIG_VFO_CURR, Hamlib.RIG_PTT_ON) is None + # TODO assert that ptt_callback() is called once + assert rig.set_ptt_callback(None) is None + assert rig.set_ptt(Hamlib.RIG_VFO_CURR, Hamlib.RIG_PTT_OFF) is None + # TODO assert that ptt_callback() is called once + + # DCD event callback + def dcd_callback(vfo, ptt, arg): + print("dcd_callback", vfo, dcd, arg) + assert (1, 5000, 2345678901) == (vfo, arg) + + assert rig.set_dcd_callback(dcd_callback, 5678901234) is None + # TODO simulate dcd events in dummy.c + assert rig.set_dcd_callback(None) is None + + # PLtune event callback + def pltune_callback(vfo, ptt, arg): + print("pltune_callback", vfo, ptt, arg) + assert (1, 5000, 2345678901) == (vfo, arg) + + assert rig.set_pltune_callback(pltune_callback, 6789012345) is None + # TODO simulate pltune events in dummy.c + assert rig.set_pltune_callback(None) is None + + # spectrum event callback + def spectrum_callback(vfo, ptt, arg): + print("spectrum_callback", vfo, ptt, arg) + assert (1, 5000, 2345678901) == (vfo, arg) + + assert rig.set_spectrum_callback(spectrum_callback, 7890123456) is None + # TODO simulate spectrum events in dummy.c + assert rig.set_spectrum_callback(None) is None + @pytest.mark.skipif('config.getoption("model") != Hamlib.RIG_MODEL_DUMMY') def test_misc(self, model): diff --git a/bindings/rig.swg b/bindings/rig.swg index 76a393e18..2be00320a 100644 --- a/bindings/rig.swg +++ b/bindings/rig.swg @@ -65,7 +65,7 @@ typedef struct Rig { PyObject *ptt_arg; /*!< PTT change argument */ PyObject *dcd_event; /*!< DCD change event */ PyObject *dcd_arg; /*!< DCD change argument */ - PyObject *pltune; /*!< Pipeline tuning module freq/mode/width callback */ + PyObject *pltune_event; /*!< Pipeline tuning module freq/mode/width callback NOTE: the C struct doesn't have the _event suffix*/ PyObject *pltune_arg; /*!< Pipeline tuning argument */ PyObject *spectrum_event; /*!< Spectrum line reception event */ PyObject *spectrum_arg; /*!< Spectrum line reception argument */ @@ -95,6 +95,105 @@ int *rig_freq_cb_python(RIG *rig, vfo_t vfo, freq_t freq, rig_ptr_t arg) return RIG_OK; } + +int *rig_mode_cb_python(RIG *rig, vfo_t vfo, rmode_t rmode, pbwidth_t pbwidth, rig_ptr_t arg) +{ + Rig *self = arg; + PyObject *python_arguments; + + python_arguments = PyTuple_Pack(4, + PyLong_FromLong(vfo), + PyLong_FromLong(rmode), + PyLong_FromLong(pbwidth), + self->python_callbacks.mode_arg + ); + + PyObject_CallObject(self->python_callbacks.mode_event, python_arguments); + + Py_XDECREF(python_arguments); + + return RIG_OK; +} + +int *rig_vfo_cb_python(RIG *rig, vfo_t vfo, rig_ptr_t arg) +{ + Rig *self = arg; + PyObject *python_arguments; + + python_arguments = PyTuple_Pack(1, + self->python_callbacks.vfo_arg + ); + + PyObject_CallObject(self->python_callbacks.vfo_event, python_arguments); + + Py_XDECREF(python_arguments); + + return RIG_OK; +} + +int *rig_ptt_cb_python(RIG *rig, vfo_t vfo, ptt_t ptt, rig_ptr_t arg) +{ + Rig *self = arg; + PyObject *python_arguments; + + python_arguments = PyTuple_Pack(1, + self->python_callbacks.ptt_arg + ); + + PyObject_CallObject(self->python_callbacks.ptt_event, python_arguments); + + Py_XDECREF(python_arguments); + + return RIG_OK; +} + +int *rig_dcd_cb_python(RIG *rig, vfo_t vfo, dcd_t, rig_ptr_t arg) +{ + Rig *self = arg; + PyObject *python_arguments; + + python_arguments = PyTuple_Pack(1, + self->python_callbacks.dcd_arg + ); + + PyObject_CallObject(self->python_callbacks.dcd_event, python_arguments); + + Py_XDECREF(python_arguments); + + return RIG_OK; +} + +int *rig_pltune_cb_python(RIG *rig, vfo_t vfo, freq_t *freq, rmode_t *rmode, pbwidth_t *pbwidth, rig_ptr_t arg) +{ + Rig *self = arg; + PyObject *python_arguments; + + python_arguments = PyTuple_Pack(1, + self->python_callbacks.pltune_arg + ); + + PyObject_CallObject(self->python_callbacks.pltune_event, python_arguments); + + Py_XDECREF(python_arguments); + + return RIG_OK; +} + +int *rig_spectrum_cb_python(RIG *rig, struct rig_spectrum_line *rig_spectrum_line, rig_ptr_t arg) +{ + Rig *self = arg; + PyObject *python_arguments; + + python_arguments = PyTuple_Pack(1, + self->python_callbacks.spectrum_arg + ); + + PyObject_CallObject(self->python_callbacks.spectrum_event, python_arguments); + + Py_XDECREF(python_arguments); + + return RIG_OK; +} #endif %} @@ -374,6 +473,7 @@ int *rig_freq_cb_python(RIG *rig, vfo_t vfo, freq_t freq, rig_ptr_t arg) r->state = &r->rig->state; r->do_exception = 0; /* default is disabled */ r->error_status = RIG_OK; + return r; } ~Rig () { @@ -525,6 +625,12 @@ int *rig_freq_cb_python(RIG *rig, vfo_t vfo, freq_t freq, rig_ptr_t arg) // Handling of event callbacks #ifdef SWIGPYTHON RIG_SET_CALLBACK(freq) + RIG_SET_CALLBACK(mode) + RIG_SET_CALLBACK(vfo) + RIG_SET_CALLBACK(ptt) + RIG_SET_CALLBACK(dcd) + RIG_SET_CALLBACK(pltune) + RIG_SET_CALLBACK(spectrum) #endif int mem_count(void) { diff --git a/rigs/dummy/dummy.c b/rigs/dummy/dummy.c index 7c0a2cb64..3f54a655e 100644 --- a/rigs/dummy/dummy.c +++ b/rigs/dummy/dummy.c @@ -638,6 +638,12 @@ static int dummy_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width) case RIG_VFO_C: priv->vfo_c.width = width; break; } + if (rig->callbacks.mode_event) + { + rig_debug(RIG_DEBUG_TRACE, "%s callbacks.mode_event(%p, %p)\n", __func__, rig->callbacks.mode_event, rig->callbacks.mode_arg); + rig->callbacks.mode_event(rig, vfo, mode, width, rig->callbacks.mode_arg); + } + RETURNFUNC(RIG_OK); } @@ -737,6 +743,12 @@ static int dummy_set_vfo(RIG *rig, vfo_t vfo) RETURNFUNC(-RIG_EINVAL); } + if (rig->callbacks.vfo_event) + { + rig_debug(RIG_DEBUG_TRACE, "%s callbacks.vfo_event(%p, %p)\n", __func__, rig->callbacks.vfo_event, rig->callbacks.vfo_arg); + rig->callbacks.vfo_event(rig, vfo, rig->callbacks.vfo_arg); + } + priv->last_vfo = priv->curr_vfo; priv->curr_vfo = vfo; STATE(rig)->current_vfo = vfo; @@ -762,6 +774,12 @@ static int dummy_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt) struct dummy_priv_data *priv = (struct dummy_priv_data *)STATE(rig)->priv; ENTERFUNC; + if (rig->callbacks.ptt_event) + { + rig_debug(RIG_DEBUG_TRACE, "%s callbacks.ptt_event(%p, %p)\n", __func__, rig->callbacks.ptt_event, rig->callbacks.ptt_arg); + rig->callbacks.ptt_event(rig, vfo, ptt, rig->callbacks.ptt_arg); + } + priv->ptt = ptt; RETURNFUNC(RIG_OK); commit 8324c85487c416025f6907a920f3528e36c07b8e Author: Daniele Forsi IU5HKX <iu...@gm...> Date: Fri Aug 8 08:45:45 2025 +0200 Implement the Python callback for freq_event Fixes #481. diff --git a/bindings/ignore.swg b/bindings/ignore.swg index d09b1a232..4114aabc4 100644 --- a/bindings/ignore.swg +++ b/bindings/ignore.swg @@ -68,6 +68,7 @@ %rename("%s", regexmatch$name="^RIG_VFO_") ""; %ignore RIG_EK; // an internal macro %ignore RIG_ELAD; // an internal macro +%rename("$ignore", regexmatch$name="python_callbacks$") ""; // internal structs and methods used by bindings // Rotators diff --git a/bindings/macros.swg b/bindings/macros.swg index 9f16b9f30..888991858 100644 --- a/bindings/macros.swg +++ b/bindings/macros.swg @@ -90,3 +90,61 @@ PyObject * ##function_name(hamlib_token_t token) %define ROT_GET_VALUE_T(function_name) GET_VALUE_T(rot_, get_ ##function_name, self->rot, ROT_) %enddef + +/* + * Macro SET_CALLBACK + * + * Creates a method that stores the Python arguments and sets the C callback. + * + * function_prefix: + * the first part of the function name (eg. one of: amp_ rig_ rot_) + * + * class_pointer: + * the pointer to the instance of the class (eg. one of self->amp self->rig self->rot) + * + * event_name: + * the name that creates a valid Hamlib function name when used together + * with the function_prefix and some predefined parts + * eg. SET_CALLBACK("rig_", "freq") creates rig_set_freq_callback() + */ +%define SET_CALLBACK(function_prefix, class_pointer, event_name) +void set_ ## event_name ## _callback(PyObject *cb, PyObject *arg=NULL) +{ + void *callback; + + if (cb == Py_None) { + callback = NULL; + } else if (!PyCallable_Check(cb)) { + SWIG_Python_RaiseOrModifyTypeError("The callback isn't a Python callable object"); + return; + } else { + callback = function_prefix ## event_name ## _cb_python; + }; + + Py_XINCREF(cb); + Py_XDECREF(self->python_callbacks. ## event_name ## _event); + + Py_XINCREF(arg); + Py_XDECREF(self->python_callbacks. ## event_name ## _arg); + + self->python_callbacks. ## event_name ## _event = cb; + self->python_callbacks. ## event_name ## _arg = arg; + self->error_status = function_prefix ## set_ ## event_name ## _callback(class_pointer, callback, self); + + return; +} +%enddef + +/* + * Macro RIG_SET_CALLBACK + * + * Creates a method that stores the Python arguments and sets the C callback. + * + * event_name: + * the name that creates a valid Hamlib function name when used together + * with the function_prefix and some predefined parts + * eg. RIG_SET_CALLBACK("freq") creates rig_set_freq_callback() + */ +%define RIG_SET_CALLBACK(event_name) + SET_CALLBACK(rig_, self->rig, event_name) +%enddef diff --git a/bindings/python/test_Hamlib_Rig_class.py b/bindings/python/test_Hamlib_Rig_class.py index f8f9898ce..3ebbc8c5b 100755 --- a/bindings/python/test_Hamlib_Rig_class.py +++ b/bindings/python/test_Hamlib_Rig_class.py @@ -82,6 +82,7 @@ class TestClass: 'set_ext_level', 'set_ext_parm', 'set_freq', +'set_freq_callback', 'set_func', 'set_level', 'set_mem', diff --git a/bindings/python/test_rig.py b/bindings/python/test_rig.py index 111311978..2b5dd863e 100755 --- a/bindings/python/test_rig.py +++ b/bindings/python/test_rig.py @@ -116,12 +116,27 @@ class TestClass: assert rig.get_dcs_sql() == 134 assert rig.get_dcs_sql(Hamlib.RIG_VFO_CURR) == 134 + # Callbacks + self.do_test_callbacks(rig) + assert rig.close() is None assert rig.state.comm_state == 0 assert rig.state.comm_status == Hamlib.RIG_COMM_STATUS_DISCONNECTED info = rig.get_info() assert info is None + def do_test_callbacks(self, rig): + # Frequency event callback + def freq_callback(vfo, freq, arg): + assert (1, 144200000.5, 1234567890) == (vfo, freq, arg) + + assert rig.set_freq_callback(freq_callback, 1234567890) is None + assert rig.set_freq(Hamlib.RIG_VFO_CURR, 144200000.5) is None + # TODO assert that freq_callback() is called once + assert rig.set_freq_callback(None) is None + assert rig.set_freq(Hamlib.RIG_VFO_CURR, 144210000) is None + # TODO assert that freq_callback() is called once + @pytest.mark.skipif('config.getoption("model") != Hamlib.RIG_MODEL_DUMMY') def test_misc(self, model): diff --git a/bindings/rig.swg b/bindings/rig.swg index 2245d0155..76a393e18 100644 --- a/bindings/rig.swg +++ b/bindings/rig.swg @@ -22,6 +22,7 @@ #include <hamlib/rig.h> %} +%include macros.swg %immutable rig_caps::model_name; %immutable rig_caps::mfg_name; %immutable rig_caps::version; @@ -50,12 +51,51 @@ typedef struct Rig { struct rig_state *state; /* shortcut to RIG->state */ int error_status; int do_exception; + // Handling of event callbacks +#ifdef SWIGPYTHON + // This mirrors "struct rig_callbacks" from rig.h using Python types + struct { + PyObject *freq_event; /*!< Frequency change event */ + PyObject *freq_arg; /*!< Frequency change argument */ + PyObject *mode_event; /*!< Mode change event */ + PyObject *mode_arg; /*!< Mode change argument */ + PyObject *vfo_event; /*!< VFO change event */ + PyObject *vfo_arg; /*!< VFO change argument */ + PyObject *ptt_event; /*!< PTT change event */ + PyObject *ptt_arg; /*!< PTT change argument */ + PyObject *dcd_event; /*!< DCD change event */ + PyObject *dcd_arg; /*!< DCD change argument */ + PyObject *pltune; /*!< Pipeline tuning module freq/mode/width callback */ + PyObject *pltune_arg; /*!< Pipeline tuning argument */ + PyObject *spectrum_event; /*!< Spectrum line reception event */ + PyObject *spectrum_arg; /*!< Spectrum line reception argument */ + /* etc.. */ + } python_callbacks; +#endif } Rig; typedef char * char_string; typedef channel_t * channel_t_p; typedef channel_t * const_channel_t_p; +#ifdef SWIGPYTHON +int *rig_freq_cb_python(RIG *rig, vfo_t vfo, freq_t freq, rig_ptr_t arg) +{ + Rig *self = arg; + PyObject *python_arguments; + + python_arguments = PyTuple_Pack(3, + PyLong_FromLong(vfo), + PyFloat_FromDouble(freq), + self->python_callbacks.freq_arg + ); + + PyObject_CallObject(self->python_callbacks.freq_event, python_arguments); + Py_XDECREF(python_arguments); + + return RIG_OK; +} +#endif %} %extend channel { @@ -482,6 +522,11 @@ typedef channel_t * const_channel_t_p; METHOD1GET(get_trn, int) METHOD1VGET(get_dcd, dcd_t) + // Handling of event callbacks +#ifdef SWIGPYTHON + RIG_SET_CALLBACK(freq) +#endif + int mem_count(void) { return rig_mem_count(self->rig); } @@ -648,5 +693,4 @@ struct channel *Rig_get_chan_all(Rig *self) return chans; } - %} diff --git a/rigs/dummy/dummy.c b/rigs/dummy/dummy.c index 020fdaeb2..7c0a2cb64 100644 --- a/rigs/dummy/dummy.c +++ b/rigs/dummy/dummy.c @@ -488,6 +488,12 @@ static int dummy_set_freq(RIG *rig, vfo_t vfo, freq_t freq) case RIG_VFO_C: priv->vfo_c.freq = freq; break; } + if (rig->callbacks.freq_event) + { + rig_debug(RIG_DEBUG_TRACE, "%s callbacks.freq_event(%p, %p)\n", __func__, rig->callbacks.freq_event, rig->callbacks.freq_arg); + rig->callbacks.freq_event(rig, vfo, freq, rig->callbacks.freq_arg); + } + if (!priv->split) { priv->curr->tx_freq = freq; ----------------------------------------------------------------------- Summary of changes: bindings/ignore.swg | 2 + bindings/macros.swg | 58 +++++++++++ bindings/python/test_Hamlib_Rig_class.py | 7 ++ bindings/python/test_rig.py | 136 +++++++++++++++++++++---- bindings/rig.swg | 170 ++++++++++++++++++++++++++++++- include/hamlib/rig.h | 3 +- rigs/dummy/dummy.c | 26 ++++- 7 files changed, 379 insertions(+), 23 deletions(-) hooks/post-receive -- Hamlib -- Ham radio control libraries |