diff --git a/README.md b/README.md index 6419e8b..e3b57ac 100644 --- a/README.md +++ b/README.md @@ -53,33 +53,19 @@ host **ic-705** (ic-705.local or ic-705.localdomain). After it is connected and logged in: - Creates a virtual PulseAudio **sound card** (48kHz, s16le, mono). This can be - used to record/play audio from/to the server (the radio). You can also set - this sound card in [WSJT-X](https://physics.princeton.edu/pulsar/K1JT/wsjtx.html). - - If you want to listen to the audio coming from this sound card in real time, - then you can create a [PulseAudio loopback](https://github.com/alentoghostflame/Python-Pulseaudio-Loopback-Tool) - between the kappanhang sound card and your real sound card. You can also - create a loopback for your microphone using this tool, so you'll be able to - transmit your voice. + used to record/play audio from/to the server (the transceiver). You can also + set this sound card in [WSJT-X](https://physics.princeton.edu/pulsar/K1JT/wsjtx.html). - Starts a **TCP server** on port `4533` for exposing the **serial port**. +- Starts `rigctld` and connects it to kappanhang's TCP serial port server. This can be used for controlling the server (the transceiver) with [Hamlib](https://hamlib.github.io/) (`rigctld`). -- Runs the command `rigctld -m 3085 -r :4533` which starts `rigctld` and - connects it to kappanhang's TCP serial port server. You can specify a custom - command with the `-r` command line argument. Running any command can be - disabled with `-r -`. The command is only executed once, as the TCP serial - port server will stay on even if the RS-BA1 server disconnects. If the TCP - serial port client disconnects (rigctld hangs) then the command will be - automatically restarted. This can be disabled with the `-e`. - - 3085 is the model number of the Icom IC-705. `rigctld` will connect to - kappanhang's TCP serial port server, and waits connections on it's default - TCP port `4532`. To use this with for example [WSJT-X](https://physics.princeton.edu/pulsar/K1JT/wsjtx.html), open WSJT-X settings, go to the *Radio* tab, set the *rig type* to `Hamlib NET rigctl`, and the *Network server* to `localhost`. +### Notes on command line arguments + If the `-s` command line argument is specified, then kappanhang will create a **virtual serial port**, so other apps which don't support Hamlib can access the transceiver directly. Look at the app log to find out the name of the @@ -148,18 +134,12 @@ not available. ### Hotkeys - `q` (quit): closes the app -- `l` (listen): toggles audio stream playback to the default sound device - +- `l` (listen): toggles audio stream playback to the default sound device. This is useful for quickly listening into the audio stream coming from the server (the transceiver). - - Note that audio will be played to the previously created virtual sound card - regardless of this setting. - `space`: toggles PTT and audio stream recording from the default sound - device - - You can transmit your own voice using a mic attached to your computer for - example. + device. You can transmit your own voice using a mic attached to your + computer for example. Some basic CAT control hotkeys are also supported: diff --git a/args.go b/args.go index 1ab72e5..3a5f328 100644 --- a/args.go +++ b/args.go @@ -12,8 +12,9 @@ var verboseLog bool var connectAddress string var serialTCPPort uint16 var enableSerialDevice bool +var rigctldModel uint +var disableRigctld bool var runCmd string -var disableReRunCmd bool var runCmdOnSerialPortCreated string var statusLogInterval time.Duration @@ -21,11 +22,12 @@ func parseArgs() { h := getopt.BoolLong("help", 'h', "display help") v := getopt.BoolLong("verbose", 'v', "Enable verbose (debug) logging") a := getopt.StringLong("address", 'a', "IC-705", "Connect to address") - t := getopt.Uint16Long("serial-tcp-port", 'p', 4533, "Expose radio's serial port on this TCP port") + t := getopt.Uint16Long("serial-tcp-port", 't', 4533, "Expose radio's serial port on this TCP port") s := getopt.BoolLong("enable-serial-device", 's', "Expose radio's serial port as a virtual serial port") - r := getopt.StringLong("run", 'r', "rigctld -m 3085 -r :4533", "Exec cmd when connected, set to - to disable") - e := getopt.BoolLong("disable-rerun", 'e', "Disable re-execing the cmd on TCP serial port disconnect") - o := getopt.StringLong("run-serial", 'o', "socat /tmp/kappanhang-IC-705.pty /tmp/vmware.pty", "Exec cmd when virtual serial port is created, set to - to disable") + m := getopt.UintLong("rigctld-model", 'm', 3085, "rigctld model number") + r := getopt.BoolLong("disable-rigctld", 'r', "Disable starting rigctld") + e := getopt.StringLong("exec", 'e', "", "Exec cmd when connected") + o := getopt.StringLong("exec-serial", 'o', "socat /tmp/kappanhang-IC-705.pty /tmp/vmware.pty", "Exec cmd when virtual serial port is created, set to - to disable") i := getopt.Uint16Long("log-interval", 'i', 100, "Status bar/log interval in milliseconds") getopt.Parse() @@ -40,8 +42,9 @@ func parseArgs() { connectAddress = *a serialTCPPort = *t enableSerialDevice = *s - runCmd = *r - disableReRunCmd = *e + rigctldModel = *m + disableRigctld = *r + runCmd = *e runCmdOnSerialPortCreated = *o statusLogInterval = time.Duration(*i) * time.Millisecond } diff --git a/controlstream.go b/controlstream.go index 9b84d79..d5f6a10 100644 --- a/controlstream.go +++ b/controlstream.go @@ -259,9 +259,8 @@ func (s *controlStream) handleRead(r []byte) error { statusLog.startPeriodicPrint() startCmdIfNeeded() - if enableSerialDevice { - startSerialPortCmdIfNeeded() - } + startSerialPortCmdIfNeeded() + startRigctldCmdIfNeeded() } } return nil diff --git a/main.go b/main.go index f2e57d0..e3c3788 100644 --- a/main.go +++ b/main.go @@ -134,6 +134,7 @@ func main() { stopCmd() stopSerialPortCmd() + stopRigctldCmd() audio.deinit() serialTCPSrv.deinit() serialPort.deinit() diff --git a/runcmd.go b/runcmd.go index 62f6afe..5014a38 100644 --- a/runcmd.go +++ b/runcmd.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "os/exec" "strings" "time" @@ -8,8 +9,48 @@ import ( const startCmdDelay = time.Second +var startedRigctldCmd *exec.Cmd +var rigctldCmdStartTimer *time.Timer var startedCmd *exec.Cmd +var cmdStartTimer *time.Timer var serialPortStartedCmd *exec.Cmd +var serialPortCmdStartTimer *time.Timer + +func doStartRigctldCmd() { + startedRigctldCmd = exec.Command("rigctld", "-m", fmt.Sprint(rigctldModel), "-r", + fmt.Sprint(":", serialTCPPort)) + err := startedRigctldCmd.Start() + if err == nil { + log.Print("rigctld started: ", startedRigctldCmd) + } else { + log.Error("error starting rigctld: ", err) + startedCmd = nil + } + rigctldCmdStartTimer = nil +} + +func startRigctldCmdIfNeeded() { + if startedRigctldCmd != nil || disableRigctld { + return + } + + if rigctldCmdStartTimer != nil { + rigctldCmdStartTimer.Stop() + } + rigctldCmdStartTimer = time.AfterFunc(startCmdDelay, doStartRigctldCmd) +} + +func stopRigctldCmd() { + if startedRigctldCmd == nil { + return + } + + if err := startedRigctldCmd.Process.Kill(); err != nil { + log.Error("failed to stop rigctld: ", err) + } + _ = startedRigctldCmd.Wait() + startedRigctldCmd = nil +} func doStartCmd() { c := strings.Split(runCmd, " ") @@ -21,14 +62,29 @@ func doStartCmd() { log.Error("error starting ", runCmd, ": ", err) startedCmd = nil } + cmdStartTimer = nil } func startCmdIfNeeded() { - if startedCmd != nil || runCmd == "-" { + if startedCmd != nil || runCmd == "" { return } - time.AfterFunc(startCmdDelay, doStartCmd) + if cmdStartTimer != nil { + cmdStartTimer.Stop() + } + cmdStartTimer = time.AfterFunc(startCmdDelay, doStartCmd) +} + +func stopCmd() { + if startedCmd == nil { + return + } + + if err := startedCmd.Process.Kill(); err != nil { + log.Error("failed to stop cmd ", runCmd, ": ", err) + } + startedCmd = nil } func doSerialPortStartCmd() { @@ -41,30 +97,27 @@ func doSerialPortStartCmd() { log.Error("error starting ", runCmdOnSerialPortCreated, ": ", err) serialPortStartedCmd = nil } + serialPortCmdStartTimer = nil } func startSerialPortCmdIfNeeded() { - if serialPortStartedCmd != nil || runCmdOnSerialPortCreated == "-" { + if !enableSerialDevice || serialPortStartedCmd != nil || runCmdOnSerialPortCreated == "-" { return } - time.AfterFunc(startCmdDelay, doSerialPortStartCmd) -} - -func stopCmd() { - if startedCmd != nil { - if err := startedCmd.Process.Kill(); err != nil { - log.Error("failed to stop cmd ", runCmd, ": ", err) - } - startedCmd = nil + if serialPortCmdStartTimer != nil { + serialPortCmdStartTimer.Stop() } + serialPortCmdStartTimer = time.AfterFunc(startCmdDelay, doSerialPortStartCmd) } func stopSerialPortCmd() { - if serialPortStartedCmd != nil { - if err := serialPortStartedCmd.Process.Kill(); err != nil { - log.Error("failed to stop cmd ", runCmdOnSerialPortCreated, ": ", err) - } - serialPortStartedCmd = nil + if serialPortStartedCmd == nil { + return } + + if err := serialPortStartedCmd.Process.Kill(); err != nil { + log.Error("failed to stop cmd ", runCmdOnSerialPortCreated, ": ", err) + } + serialPortStartedCmd = nil } diff --git a/serialtcpsrv.go b/serialtcpsrv.go index 117f3f0..256fc14 100644 --- a/serialtcpsrv.go +++ b/serialtcpsrv.go @@ -104,10 +104,8 @@ func (s *serialTCPSrvStruct) loop() { s.disconnectClient() log.Print("client ", s.client.RemoteAddr().String(), " disconnected") - if !disableReRunCmd { - stopCmd() - startCmdIfNeeded() - } + stopRigctldCmd() + startRigctldCmdIfNeeded() } }