Metadata-Version: 2.4
Name: ducobelt
Version: 0.1.0
Summary: Lowlevel tool to interact with Rhizomatica's mercury modem.
Author-email: "Dr. Andreas Krüger, DJ3EI" <dj3ei@famsik.de>
Project-URL: Homepage, https://codeberg.org/omwb/ducobelt
Project-URL: Repository, https://codeberg.org/omwb/ducobelt
Project-URL: Issues, https://codeberg.org/omwb/ducobelt/issues
Keywords: ham,amateur radio
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Communications :: Ham Radio
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# DuCoBelt: Dual Conveyor Belt

## What does it do, in a nutshell

This program, called "ducobelt", makes it more convenient to operate the
[mercury](https://github.com/Rhizomatica/mercury) modem, in
particular, its
[TNC](https://github.com/Rhizomatica/mercury/blob/mercuryv2/docs/TNC.md) network
API.

- You can type TNC commands that are to be sent to mercury's command port
  (TCP port 8300).
    - The line endings you type are normalized to `\r` (aka `CR`) as wanted by
      mercury.
    - The characters you type are normalized to upper case as required by mercury.
- You can see what mercury answers via that command port.
    - These answers are typically given in lines terminated with `\r`.
      If those are displayed as is, each line fully or partially replaces
      the previous one. So ducobelt replaces `\r` line ends coming from mercury
      with `\r\n` (aka `CR LF`) line endings that will display nicely.

So this is a bit like [netcat](https://packages.debian.org/trixie/netcat-openbsd),
[ncat](https://nmap.org/ncat/), or [socat](https://packages.debian.org/trixie/socat),
but with some convenience conversations.

## Why the name?

At the end of the day, `ducobelt` just transfers data.
What `mercury` supplies via the ARQ socket is transferred to `np`
and, simultaneously, what you provide via your keyboard
is transferred to that socket, in a parallel **du**al **con**veyer **belt**.

## Alternative

There is an alternative solution by the makers of `mercury`, namely
[mercury-client](https://github.com/Rhizomatica/mercury-client).
That software can be used for first experiments quite as well as `ducobelt`.

The main differences (from my point of view as the `ducobelt` author):

- You need to compile `mercury-client`, while `ducobelt` works out of the box
  with Python.
- `ducobelt` presently will not work on Windows and may or may not work on Mac,
  while `mercury-client` serves all three platforms.
- `mercury-client` gives you a GUI, while `ducobelt` can be run "headless",
  that is, only needs text terminals.
- `mercury-client` tries to offer a unified GUI, while `docubelt` follows the
  time-honored approach "pull what you need out of a well-stocked toolbox".
- Other than `mercury-client`, `ducobelt` leaves the data port (usually 8301) alone.
  This may change in later versions, but I plan to always provide an option to
  not touch that port. On the other hand, it is unclear what to do in
  `mercury-client` to free that port for use by other software.
- The `ducobelt` approach means: You *need* the toolbox. E.g., for text chat,
  you'll need to use some network-enabled program to connect to the data port,
  e.g., `nc` (from one of the `netcat` packages).
- On the other hand, it is more straightforward to rig up fun experiments with
  `ducobelt`. E.g., we played around with `sz` and `rz` (from the `lrzsz` package)
  to send files via the venerable ZMODEM protocol (see below). This is probably
  not the most efficient choice on top of Mercury, but totally can be made to work.

## How to set this up

- Install Linux (this will presently not run unter Windows, it _might_ run on Mac)
- Install Python (version 3.12 or later)
- Create a virtual environment
    - either [barefeet](https://docs.python.org/3/library/venv.html#creating-virtual-environments)
    - or install [uv](https://github.com/astral-sh/uv) (which also allows to install Python 
      3.12 if you are stuck with an older version) and
      [use](https://docs.astral.sh/uv/pip/environments/) that.
- Activate that virtual env and install `ducobelt` with `pip` (either
  plain `pip install ducobelt` or `uv pip install ducobelt`).
- Install [mercury](https://github.com/Rhizomatica/mercury)
  (either plain headless `mercury` or GUI `mercury-ui`, both do)
- Install some `netcat` version; if in doubt, the `netcat-openbsd` flavor is fine.

## How to run: The four-terminal setup

I suggest using four different terminals.

(Personally, I like to use `tmux` to manage many terminals in the same
terminal window.)

What is the plan? If you follow the instructions below, these are the terminals
you'll end up with:

- **Terminal 1** runs the `ducobelt` binary. You type your
  [TNC commands](https://github.com/Rhizomatica/mercury/blob/mercuryv2/docs/TNC.md)
  commands into this termina.
  They'll be slightly changed, to upper-cased and `\r` line endings, and sent to port 8300. 
- **Terminal 2** echoes the replies that come back from the `mercury` or `mercury-ui` binary
  over the network (from port 8300).
- **Terminal 3** The `mercury` or `mercury-ui` binary itself runs here.
  You'll see the log lines scroll by.
- **Terminal 4** In this terminal, you can type chat messages to your QSO partner
  and see what they type back, and/or trigger sending or receipt of (text or
  binary) data.

The most complicated thing here is: How does the `ducobelt` programm get its output
into terminal 2? Presently, that program is hard-wired to write this output
into some file `np` in its current directory. So you need some way to display
whatever is writen to `np` in terminal 2.

There is more than one way to do this.

My original way was:

- I create a "named pipe" via `mknod np p` in the directory 
  in which I intend to run the `docubelt` binary later.
- In terminal 2, I start `cat np` .

Nowadays,

- I run `ps ax | grep 'ps ax'` in terminal 2 to find out which terminal,
  in the sense of `/dev/pts/N` or similar, is connected with terminal 2.
- In the directory in which I intend to run the `ducobelt` binary,
  I run `ln -s /dev/pts/N np` (while replacing `N` with the number I found).
- In that same directory (possibly in terminal 1), I test this with
  `echo hello > np` which should cause "hello" to appear in terminal 2.

After that is accomplished, start either `mercury` or `mercury-ui` 
in terminal 3.  It is your choice which of the two you use; if in doubt,
the `mercury-ui` is recommended, it allows looking at the waterfall display
and makes it easier to set audio volume.

**Afterwards**, start `ducobelt` in terminal 1.  You'll have  to activate
the Python virtual environment into which you installed `ducobelt`in that terminal
https://docs.python.org/3/library/venv.html#how-venvs-work in order to do that.

If all runs well, the text `BUFFER 0` should appear in terminal 2, and,
every so often, `IAMALIVE`.

You can now type things like `mycall ab0xyz` (replace with whatever your real
amateur radio call is) into terminal 1 and should see an `OK` appear in
terminal 2.

If you type something like `connect ab0xyz xy0abc` to (attempt to) connect
with XY0ABC (or whatever callsign you actually type), `ducobelt` will cause
`mercury` (or `mercury-ui`) to key your trx  and send pertinent data.

For the things you can type, refer to mercury's
[TNC](https://github.com/Rhizomatica/mercury/blob/mercuryv2/docs/TNC.md)
documentation. The manual page of `mercury` also contains this information.

The setup of the fourth terminal is only needed when a connection with
another station has actually been established.

To chat with that station, I use `nc` (the binary from the `netcat` package)
to connect to the data port 8301 of `mercury` or `mercury-ui`:

```commandline
nc -v -v localhost 8301
```

If I want to transfer binary data, the QSO partner needs to be ready to
receive such data. Any data piped into the tcp/ip socket 8301 on my machine
will come out of the tcp/ip socket 8301 on the other machine, be it text or
binary.

On my machine, I can pipe some file `binary.data` into the socket with

```commandline
nc -v -v -N localhost 8301 < binary.data
```

But Mercury does not provide a way for the sending side to communicate to
the other side "a batch of data has now come to an end". In tests, we have
successfully used ZMODEM for that.  This is probably overkill, but it works.

Details:

- We used `sz` and `rz` from the `lrzsz` package on both Linux and Mac.
- On Linux, `--tcp-client 127.0.0.1 8301` connect either of the two with the data port.
- On Mac, `sz` / `rz` didn't provide that switch. So we pulled `socat` with `EXEC:`
  out of our toolbox, which worked nicely.
- `--disable-timeouts` was essential.
- `-b` worked (needed on the `sz` side), quite as we expected.
- We used `-L 42` (on the `sz` side), but could probably have used
  much larger windows.
- `-v -v` keeps you more entertained and informed.

## Mercury hints

I have found the `alsa` sound access hard to use, but the `pulse`
interface works nicely on my Debian Trixie machine (that actually
uses pipewire audio).

For first experiments, I put a `mercury.ini` into the directory in which I
intend to start the `marcury-ui` (or plain `mercury`) binary.

Those first experiments do not actually use my transceiver, but just listen
to the noise that comes out of my laptop's speakers resp. feed such
noise into my laptop's microphone. For this, this is my "acoustic couple"
`mercury.ini`:

```
[main]
ui_enabled = true
ui_port = 10000
ui_protocol = ws
waterfall_enabled = true
radio_model = 6
radio_device = ""
input_device = "alsa_input.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__Mic1__source"
output_device = "alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__Speaker__sink"
capture_channel = stereo
sound_system = pulse
arq_tcp_base_port = 8300
broadcast_tcp_port = 8100
verbose = true
freedv_verbosity = 1
hamlib_log_level = 3
radio_serial_speed = 0

[arq]
no_progress_timeout_s = 180
disconnect_drain_timeout_s = 30
data_retry_slots = 10
mode_hold_after_downgrade_s = 6
ladder_up_successes = 2
retry_downgrade_threshold = 2
channel_guard_ms = 700
iss_post_ack_guard_ms = 900
keepalive_interval_s = 20
keepalive_miss_limit = 5
peer_payload_hold_s = 15
startup_max_s = 10

[channel]
busy_detect = false
busy_threshold_db = 10
busy_hysteresis_db = 3
busy_on_debounce_ms = 300
busy_hang_ms = 1500

[audio]
tx_gain_db = -8.50

[tnc]
keepalive_s = 60
buffer_report_ms = 1000
```

You will probably want to set your own values for `input_device`, `output_device`,
and `tx_gain_db`.

## An actual first experiment that does not require a QSO partner

My actual first experiment was: With no trx connected,

- send `MYCALL` and then `CONNECT` commands, trying to connect
  to a fake call,
- record the noise with my smarthopne,
- switch to the fake call using `MYCALL`,
- let the smartphone play back the sound to the laptop's microphone.

Sample cuts of the four terminals:

Terminal 1:

```
$ ducobelt 
> mycall dj3ei
> connect dj3ei dl1ejw
> mycall dl1ejw
> listen on
> 
```

Terminal 2, the head is missing, this starts when
the smartphone sent the recording:

```
SN 1.6                                                                                             │andreas@uhu:~
BITRATE (3) 321 BPS                                                                                │$ nc -v -v localhost 8301
PENDING                                                                                            │nc: connect to localhost (::1) port 8301 (tcp) failed: Connection refused
PTT ON                                                                                             │Connection to localhost (127.0.0.1) 8301 port [tcp/*] succeeded!
PTT OFF                                                                                            │
SN 5.8                                                                                             │
BITRATE (15) 68 BPS                                                                                │
PTT ON                                                                                             │
PTT OFF                                                                                            │
SN 3.4                                                                                             │
BITRATE (15) 68 BPS                                                                                │
PTT ON                                                                                             │
PTT OFF                                                                                            │
PTT ON                                                                                             │
PTT OFF                                                                                            │
PTT ON                                                                                             │
IAMALIVE                                                                                           │
PTT OFF                                                                                            │
PTT ON                                                                                             │
PTT OFF                                                                                            │
CANCELPENDING                                                                                      │
IAMALIVE
```

And here is the last part of the log of `mercury-ui`, slightly earlier,
starting with the moment when `listen on` was typed into terminal 1
(and converted to upper case `LISTEN ON` by ducobelt).

```
18:11:28.896 [+119.274s] [INF] [tcp-ctl] Command received: LISTEN ON
18:11:39.865 [+130.243s] [INF] [arq] Incoming connection from DJ3EI on DL1EJW (pending)
18:11:40.608 [+130.986s] [INF] [radio] TX enabled (PTT ON)
18:11:44.318 [+134.696s] [INF] [radio] TX disabled (PTT OFF)
18:11:53.352 [+143.730s] [INF] [radio] TX enabled (PTT ON)
18:11:57.063 [+147.441s] [INF] [radio] TX disabled (PTT OFF)
18:12:06.096 [+156.474s] [INF] [radio] TX enabled (PTT ON)
18:12:09.806 [+160.185s] [INF] [radio] TX disabled (PTT OFF)
18:12:18.837 [+169.216s] [INF] [radio] TX enabled (PTT ON)
18:12:22.548 [+172.926s] [INF] [radio] TX disabled (PTT OFF)
18:12:31.548 [+181.926s] [INF] [arq] Incoming connection cancelled
```

The mercury program sends out four times its affirmative answer
to the offer that came in (by the smart phone audio) of an incoming connection
originating from DJ3EI.
