cqta 1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cqta might be problematic. Click here for more details.
- cqta-1.2/LICENSE +21 -0
- cqta-1.2/MANIFEST.in +1 -0
- cqta-1.2/PKG-INFO +92 -0
- cqta-1.2/PROTOCOL.md +242 -0
- cqta-1.2/README.md +72 -0
- cqta-1.2/cqta/__init__.py +3 -0
- cqta-1.2/cqta/__main__.py +27 -0
- cqta-1.2/cqta/app.py +2056 -0
- cqta-1.2/cqta/cqta.png +0 -0
- cqta-1.2/cqta/protocol.py +165 -0
- cqta-1.2/cqta/session.py +133 -0
- cqta-1.2/cqta.egg-info/PKG-INFO +92 -0
- cqta-1.2/cqta.egg-info/SOURCES.txt +21 -0
- cqta-1.2/cqta.egg-info/dependency_links.txt +1 -0
- cqta-1.2/cqta.egg-info/entry_points.txt +2 -0
- cqta-1.2/cqta.egg-info/requires.txt +1 -0
- cqta-1.2/cqta.egg-info/top_level.txt +1 -0
- cqta-1.2/pyproject.toml +38 -0
- cqta-1.2/setup.cfg +4 -0
- cqta-1.2/tests/test_app_ui.py +1871 -0
- cqta-1.2/tests/test_build.py +121 -0
- cqta-1.2/tests/test_entrypoint.py +55 -0
- cqta-1.2/tests/test_protocol.py +148 -0
cqta-1.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Federico Santandrea
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cqta-1.2/MANIFEST.in
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include PROTOCOL.md
|
cqta-1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cqta
|
|
3
|
+
Version: 1.2
|
|
4
|
+
Summary: A Qt application for exchanging text over amateur radio links
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/sntfrc/cqta
|
|
7
|
+
Classifier: Environment :: X11 Applications :: Qt
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: PySide6>=6.8
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# CQta - Ham radio QSOs over IP radio links
|
|
22
|
+
|
|
23
|
+
CQta is a protocol and application for having ham radio QSOs over
|
|
24
|
+
IP radio links, such as KISS TNCs, Meshtastic tunnels, HSMM Wi-Fi, etc.
|
|
25
|
+
It assumes that the network interface already carries an IP
|
|
26
|
+
tunnel over the radio link, but also contains conveniences to start
|
|
27
|
+
KISS and Meshtastic tunnels directly from the application interface.
|
|
28
|
+
CQta itself sends UDP datagrams to an interface's broadcast address
|
|
29
|
+
on port **37373**.
|
|
30
|
+
|
|
31
|
+
It has been created mainly as a reference companion app to showcase
|
|
32
|
+
pytap3/kisstun functionality, but I hope the protocol can be useful
|
|
33
|
+
on his own anyway, and the app can be fun to use on-air.
|
|
34
|
+
|
|
35
|
+
The protocol uses a compact, human-readable text format. It
|
|
36
|
+
supports directed messages, broadcasts, per-direction numbering for each
|
|
37
|
+
`FROM>TO` pair, missing-message requests, and empty keepalive messages. See
|
|
38
|
+
[PROTOCOL.md](./PROTOCOL.md) for the wire format and current draft rules.
|
|
39
|
+
|
|
40
|
+
## Run the application
|
|
41
|
+
|
|
42
|
+
The cross-platform Qt application uses PySide6. With Python 3.10 or later,
|
|
43
|
+
install CQta as a package:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
python -m pip install cqta
|
|
47
|
+
cqta
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
To run directly from a source checkout, use `python -m pip install .` and
|
|
51
|
+
`python -m cqta`.
|
|
52
|
+
|
|
53
|
+
Self-contained Linux AppImage, macOS DMG, and Windows executable builds are
|
|
54
|
+
available, and they bundle ``kisstun`` and ``meshtastic`` for convenience as
|
|
55
|
+
they are the two most typical use cases. Check [BUILD.md](./BUILD.md) for
|
|
56
|
+
details on how to invoke them from shell on various platforms if you need.
|
|
57
|
+
|
|
58
|
+
Choose the IPv4 network interface that is tunneled over the radio link and
|
|
59
|
+
enter your callsign. The default callsign is the reserved placeholder
|
|
60
|
+
`N0CALL`, which puts the application in listen-only mode: it will receive
|
|
61
|
+
traffic but never transmit. Enter your own callsign to enable sending.
|
|
62
|
+
|
|
63
|
+
The **Radio interface** list also offers **Start kisstun (pytap3) tunnel...**
|
|
64
|
+
and **Start Meshtastic tunnel...**. Choose one to configure and start the
|
|
65
|
+
bundled tunnel command; its output appears in a console window. If you use
|
|
66
|
+
the Dire Wolf integration, make sure that `direwolf` executable is on PATH,
|
|
67
|
+
and that the current directory contains the prepared `direwolf.conf` file.
|
|
68
|
+
|
|
69
|
+
The application listens for IP broadcasts on UDP port 37373.
|
|
70
|
+
Outgoing traffic is sent to the selected interface's broadcast address.
|
|
71
|
+
|
|
72
|
+
The application keeps QSO state in memory for the current run. It does not
|
|
73
|
+
persist message history or restore conversations after restart.
|
|
74
|
+
|
|
75
|
+
## Quick example
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
W1ABC>N0XYZ 0:Hello, N0XYZ. This is W1ABC.
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The message is sent as a UDP datagram to the interface broadcast address and
|
|
82
|
+
port 37373. The header identifies the sender, intended recipient, and
|
|
83
|
+
sender-local message number; the text after the colon is the message content.
|
|
84
|
+
Double-click a station to open a QSO tab. All valid packets heard update
|
|
85
|
+
the station list, including messages addressed to other stations. Enable
|
|
86
|
+
**Monitor** to also show all received
|
|
87
|
+
non-empty messages in the broadcast transcript, including messages directed
|
|
88
|
+
to others.
|
|
89
|
+
The **Beacon** option is enabled by default and sends an empty `QST` packet
|
|
90
|
+
immediately when manually checked, then at the selected interval, so other
|
|
91
|
+
stations can update Last Heard. Its remaining seconds (or `TX` when a beacon
|
|
92
|
+
is sent) appear on the right side of the status bar.
|
cqta-1.2/PROTOCOL.md
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# CQta protocol
|
|
2
|
+
|
|
3
|
+
CQta carries text messages over UDP on a network interface that is assumed to
|
|
4
|
+
be a tunnel over a radio link. Every CQta datagram is sent to that interface's
|
|
5
|
+
broadcast address on UDP port **37373**. The address and port are transport
|
|
6
|
+
details; the CQta header identifies the intended recipient.
|
|
7
|
+
|
|
8
|
+
This document describes the initial draft. It is not yet a versioned or
|
|
9
|
+
finalized standard.
|
|
10
|
+
|
|
11
|
+
## Wire format
|
|
12
|
+
|
|
13
|
+
Each UDP payload has this form:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
SOURCE>DESTINATION NUMBER{REQUESTS}[NO-CONTENT]:TEXT
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The optional `{REQUESTS}` and `[NO-CONTENT]` sections may appear in either
|
|
20
|
+
order. Omit a section when it has no entries. For example:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
W1ABC>N0XYZ 12{3-7,9}[5-6]:The requested text is being resent.
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The header is ASCII. `TEXT` is UTF-8; the first colon separates the header
|
|
27
|
+
from the text, and any further colons belong to the text. An empty `TEXT` is
|
|
28
|
+
valid. A datagram is one complete CQta packet: the format does not define
|
|
29
|
+
fragmentation or continuation across datagrams.
|
|
30
|
+
|
|
31
|
+
### Header fields
|
|
32
|
+
|
|
33
|
+
- **SOURCE** is the transmitting station's callsign.
|
|
34
|
+
- **DESTINATION** is the intended station's callsign, or the special value
|
|
35
|
+
`QST` for a message to all stations.
|
|
36
|
+
- **NUMBER** is the decimal number of this packet in the source's outgoing
|
|
37
|
+
stream to that destination. It is a non-negative integer. A `QST` call and
|
|
38
|
+
the opening packet of a directed QSO use number `0`. Repeated `QST` calls
|
|
39
|
+
use `0`. The first packet numbered `0` received from a peer establishes
|
|
40
|
+
that peer's direction in the QSO; once packets have already been received
|
|
41
|
+
from that peer, a subsequent packet numbered `0` signals that the peer
|
|
42
|
+
wants to restart the QSO. On restart, both stations should discard their
|
|
43
|
+
sequence and retransmission state for that directed QSO. A station
|
|
44
|
+
initiating a restart resets its local state before sending its new packet
|
|
45
|
+
`0`. After the opening packet, each new packet in that direction increments
|
|
46
|
+
the number by one. Do not wrap the number during a QSO.
|
|
47
|
+
- **REQUESTS**, in braces, lists packet numbers from the destination's
|
|
48
|
+
outgoing stream that the source has not received and is asking to have
|
|
49
|
+
retransmitted.
|
|
50
|
+
- **NO-CONTENT**, in brackets, lists packet numbers from the source's own
|
|
51
|
+
outgoing stream that had no text content (keepalives). It tells the
|
|
52
|
+
destination that those packets have no text to retransmit.
|
|
53
|
+
- **TEXT** is the message content. It may be empty, including for keepalives.
|
|
54
|
+
|
|
55
|
+
For a directed QSO, a sequence belongs to the ordered `SOURCE>DESTINATION`
|
|
56
|
+
pair. For example, `W1ABC>N0XYZ` and `W1ABC>K2DEF` have separate sequences,
|
|
57
|
+
and `W1ABC>N0XYZ` and `N0XYZ>W1ABC` also have separate sequences. This allows
|
|
58
|
+
a station to conduct QSOs with multiple stations in parallel, with each
|
|
59
|
+
direction numbered independently. `QST` calls are not part of a directed
|
|
60
|
+
QSO's sequence: each call uses number `0`, even when the station makes
|
|
61
|
+
repeated calls.
|
|
62
|
+
|
|
63
|
+
An empty `SOURCE>QST 0:` packet is a **beacon**. It is not a user-visible
|
|
64
|
+
broadcast message and does not belong to a sequence; receivers may use it to
|
|
65
|
+
update the sender's last-heard time. Beacons may be repeated with packet
|
|
66
|
+
number `0`, like other QST calls, and must not be shown as transcript messages.
|
|
67
|
+
|
|
68
|
+
### Number lists
|
|
69
|
+
|
|
70
|
+
Each list contains comma-separated decimal numbers and inclusive ranges.
|
|
71
|
+
For example, `3-7,9` means `3, 4, 5, 6, 7, 9`. Numbers and ranges in a list
|
|
72
|
+
must be in ascending order, with no duplicates or overlap. Whitespace is not
|
|
73
|
+
allowed inside the header. Empty lists and descending ranges are invalid.
|
|
74
|
+
|
|
75
|
+
Requests refer to the destination's sequence in this directed QSO;
|
|
76
|
+
no-content confirmations refer to the source's sequence in this QSO. The same
|
|
77
|
+
number may appear in both lists because it identifies different packets in
|
|
78
|
+
each direction. `QST` calls are not part of a directed QSO's sequence and
|
|
79
|
+
must not include either list.
|
|
80
|
+
|
|
81
|
+
## Examples
|
|
82
|
+
|
|
83
|
+
### CQ call and start a directed QSO
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
W1ABC>QST 0:CQ
|
|
87
|
+
W1ABC>N0XYZ 0:Hello, N0XYZ. This is W1ABC.
|
|
88
|
+
N0XYZ>W1ABC 0:Hello, W1ABC. I hear you.
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Every CQ call uses number `0`, including repeated calls. When N0XYZ answers
|
|
92
|
+
and the directed QSO begins, each station starts its own direction at `0`.
|
|
93
|
+
Subsequent new packets in each direction are numbered `1`, `2`, and so on.
|
|
94
|
+
The first received `0` in each direction establishes that direction; a later
|
|
95
|
+
received `0` from that peer restarts the QSO and clears both directions'
|
|
96
|
+
tracked sequence state. Because packet `0` is also the restart signal, a
|
|
97
|
+
delayed or retransmitted opening packet cannot be distinguished from an
|
|
98
|
+
intentional restart. Implementations should avoid retransmitting an opening
|
|
99
|
+
packet after the QSO is established.
|
|
100
|
+
|
|
101
|
+
Empty broadcast messages can be used as beacons to signal presence.
|
|
102
|
+
|
|
103
|
+
### Restart an active QSO
|
|
104
|
+
|
|
105
|
+
If W1ABC sends packet `0` to N0XYZ after N0XYZ has already received packets
|
|
106
|
+
from W1ABC in their current QSO, that packet signals a restart. N0XYZ clears
|
|
107
|
+
the tracked outgoing packets, missing packet numbers, sequence counters, and
|
|
108
|
+
empty-packet confirmations for the `W1ABC>N0XYZ` and `N0XYZ>W1ABC` directions,
|
|
109
|
+
then processes the received packet `0` as the opening packet of the new QSO.
|
|
110
|
+
The first `0` received from W1ABC when a QSO is initially established is not
|
|
111
|
+
a restart.
|
|
112
|
+
|
|
113
|
+
This restart signal is intentionally lightweight, but it means delayed or
|
|
114
|
+
retransmitted packet `0` can restart an active QSO. The protocol has no session
|
|
115
|
+
identifier with which to distinguish those cases.
|
|
116
|
+
|
|
117
|
+
### Request missing packets
|
|
118
|
+
|
|
119
|
+
If W1ABC has received N0XYZ packets `0`, `1`, `2`, `8`, and `10`, it can ask
|
|
120
|
+
N0XYZ to resend the gaps it has observed:
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
W1ABC>N0XYZ 4{3-7,9}:Please resend those packets.
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The request list can accompany any new packet from W1ABC, including a
|
|
127
|
+
keepalive. A receiver may wait and piggyback a request on its next outgoing
|
|
128
|
+
packet; it does not have to send a request immediately when it notices a gap.
|
|
129
|
+
It may also send a request sooner, for example in an empty keepalive. The
|
|
130
|
+
receiver should continue including unresolved missing numbers in later
|
|
131
|
+
packets until they are received or confirmed empty. A sender may retransmit
|
|
132
|
+
them but is not required to. A receiver must not infer that packets after its
|
|
133
|
+
highest received number are missing.
|
|
134
|
+
|
|
135
|
+
To retransmit a packet, the sender sends its original packet number and
|
|
136
|
+
original text again; it does not allocate a new number. The retransmission
|
|
137
|
+
may include current request or no-content lists. Receivers should treat a
|
|
138
|
+
repeated nonzero number within the same directed QSO as a duplicate rather
|
|
139
|
+
than displaying the same text twice. Because a received `0` after QSO
|
|
140
|
+
establishment means restart, implementations should not retransmit packet
|
|
141
|
+
`0` once the QSO is underway. This duplicate rule does not apply to `QST`
|
|
142
|
+
calls: each `SOURCE>QST 0` is a new call, even if its text matches an earlier
|
|
143
|
+
call.
|
|
144
|
+
|
|
145
|
+
### Close a QSO with QRT
|
|
146
|
+
|
|
147
|
+
Applications may treat a directed message whose text is exactly `QRT` as the
|
|
148
|
+
remote station closing the QSO. The comparison is exact: `QRT `, ` QRT`, and
|
|
149
|
+
`QRT.` are ordinary message text. A receiving application may close the
|
|
150
|
+
conversation UI and stop keepalives without sending a separate close packet.
|
|
151
|
+
Closing a QSO locally should send a numbered `QRT` message, then discard the
|
|
152
|
+
local sequence and retransmission state for that peer.
|
|
153
|
+
|
|
154
|
+
If the remote station misses that `QRT` and later sends another packet, the
|
|
155
|
+
local application may treat it as a new QSO. It starts tracking the remote
|
|
156
|
+
direction at the first packet number it observes and does not request earlier
|
|
157
|
+
numbers from before this QSO was reopened. Gaps after that first observed
|
|
158
|
+
packet are still tracked normally. The two directions are independent, so the
|
|
159
|
+
local station's new outgoing sequence starts at `0`.
|
|
160
|
+
|
|
161
|
+
### Confirm empty keepalives
|
|
162
|
+
|
|
163
|
+
If N0XYZ's packets `3` through `6` and `8` were empty keepalives, it can tell
|
|
164
|
+
W1ABC those packets have no text to recover:
|
|
165
|
+
|
|
166
|
+
```text
|
|
167
|
+
N0XYZ>W1ABC 11[3-6,8]:
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
An empty packet still consumes a number in its sender's stream. A receiver
|
|
171
|
+
that gets a no-content confirmation should stop requesting those numbers.
|
|
172
|
+
Keepalives do not have a separate wire representation; they are ordinary
|
|
173
|
+
numbered packets with empty text.
|
|
174
|
+
|
|
175
|
+
### Mix requests and confirmations
|
|
176
|
+
|
|
177
|
+
```text
|
|
178
|
+
N0XYZ>W1ABC 11{2,5-6}[3-4]:
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
This requests W1ABC packets `2`, `5`, and `6`, and confirms that N0XYZ
|
|
182
|
+
packets `3` and `4` were empty. The two lists refer to opposite streams.
|
|
183
|
+
|
|
184
|
+
### Broadcast
|
|
185
|
+
|
|
186
|
+
```text
|
|
187
|
+
W1ABC>QST 0:CQ
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`QST` means all stations. Each CQ call uses number `0` and is not included in
|
|
191
|
+
a directed QSO's packet sequence. Text beginning with `CQ` is otherwise
|
|
192
|
+
ordinary content. The CQta GUI highlights a received `CQ` message and lets
|
|
193
|
+
the user open a QSO with its sender by double-clicking it.
|
|
194
|
+
|
|
195
|
+
## Keepalive timing
|
|
196
|
+
|
|
197
|
+
When a directed QSO is idle, a station should send an empty keepalive
|
|
198
|
+
approximately every 25 to 35 seconds, choosing the interval randomly in that
|
|
199
|
+
range. A keepalive increments that direction's QSO sequence just like a
|
|
200
|
+
text-bearing packet. Keepalives help expose gaps in the sequence, but do not
|
|
201
|
+
guarantee delivery or prove that a peer is reachable. This timing rule does
|
|
202
|
+
not apply to `QST` calls.
|
|
203
|
+
|
|
204
|
+
## Datagram size
|
|
205
|
+
|
|
206
|
+
Implementations should avoid IP fragmentation. The maximum CQta payload
|
|
207
|
+
depends on the path MTU and IP version: subtract the IP and UDP headers from
|
|
208
|
+
the MTU to obtain the maximum UDP payload, and keep the CQta header and text
|
|
209
|
+
within that budget. For example, with a 256-byte IP MTU, the theoretical UDP
|
|
210
|
+
payload limit is 228 bytes for IPv4 with a 20-byte IP header, or 208 bytes for
|
|
211
|
+
IPv6 with a 40-byte IP header. Tunnels and IP options can reduce those limits;
|
|
212
|
+
use a smaller configured limit when necessary.
|
|
213
|
+
|
|
214
|
+
This draft does not define message splitting, truncation, or a mechanism to
|
|
215
|
+
negotiate a payload limit. Applications should reject or clearly report
|
|
216
|
+
messages that exceed their configured datagram budget rather than silently
|
|
217
|
+
truncating them.
|
|
218
|
+
|
|
219
|
+
The current CQta GUI implementation limits each decoded number list and the
|
|
220
|
+
number of inferred missing packet IDs to 4096. This is an application resource
|
|
221
|
+
limit, not a limit on the wire format; a later implementation could track
|
|
222
|
+
large ranges without expanding every ID in memory.
|
|
223
|
+
|
|
224
|
+
## Limitations and possible improvements
|
|
225
|
+
|
|
226
|
+
- **Conversation identity:** Numbering is scoped to the ordered
|
|
227
|
+
`SOURCE>DESTINATION` pair, so separate partners can have concurrent QSOs.
|
|
228
|
+
Packet `0` can restart a QSO with the same partner, but the protocol cannot
|
|
229
|
+
distinguish that intent from a delayed or retransmitted opening packet. A
|
|
230
|
+
future revision could add a compact session identifier (or sender epoch) to
|
|
231
|
+
disambiguate restarts and stale packets.
|
|
232
|
+
- **Retransmission policy:** Requests are advisory and have no expiry,
|
|
233
|
+
acknowledgement, or retry limit. A receiver can piggyback requests on its
|
|
234
|
+
next ordinary message or keepalive, or send them sooner; implementations
|
|
235
|
+
need local policy for how long to retain packets and how frequently to
|
|
236
|
+
repeat requests.
|
|
237
|
+
- **Unbounded gap lists:** A long outage can make a missing-packet list large.
|
|
238
|
+
A future revision could cap each list or define a compact cumulative
|
|
239
|
+
acknowledgement scheme.
|
|
240
|
+
- **No security or delivery guarantee:** This format provides no
|
|
241
|
+
authentication, encryption, or guaranteed delivery. Those concerns depend
|
|
242
|
+
on the underlying link and are not solved by the message syntax.
|
cqta-1.2/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# CQta - Ham radio QSOs over IP radio links
|
|
2
|
+
|
|
3
|
+
CQta is a protocol and application for having ham radio QSOs over
|
|
4
|
+
IP radio links, such as KISS TNCs, Meshtastic tunnels, HSMM Wi-Fi, etc.
|
|
5
|
+
It assumes that the network interface already carries an IP
|
|
6
|
+
tunnel over the radio link, but also contains conveniences to start
|
|
7
|
+
KISS and Meshtastic tunnels directly from the application interface.
|
|
8
|
+
CQta itself sends UDP datagrams to an interface's broadcast address
|
|
9
|
+
on port **37373**.
|
|
10
|
+
|
|
11
|
+
It has been created mainly as a reference companion app to showcase
|
|
12
|
+
pytap3/kisstun functionality, but I hope the protocol can be useful
|
|
13
|
+
on his own anyway, and the app can be fun to use on-air.
|
|
14
|
+
|
|
15
|
+
The protocol uses a compact, human-readable text format. It
|
|
16
|
+
supports directed messages, broadcasts, per-direction numbering for each
|
|
17
|
+
`FROM>TO` pair, missing-message requests, and empty keepalive messages. See
|
|
18
|
+
[PROTOCOL.md](./PROTOCOL.md) for the wire format and current draft rules.
|
|
19
|
+
|
|
20
|
+
## Run the application
|
|
21
|
+
|
|
22
|
+
The cross-platform Qt application uses PySide6. With Python 3.10 or later,
|
|
23
|
+
install CQta as a package:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
python -m pip install cqta
|
|
27
|
+
cqta
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
To run directly from a source checkout, use `python -m pip install .` and
|
|
31
|
+
`python -m cqta`.
|
|
32
|
+
|
|
33
|
+
Self-contained Linux AppImage, macOS DMG, and Windows executable builds are
|
|
34
|
+
available, and they bundle ``kisstun`` and ``meshtastic`` for convenience as
|
|
35
|
+
they are the two most typical use cases. Check [BUILD.md](./BUILD.md) for
|
|
36
|
+
details on how to invoke them from shell on various platforms if you need.
|
|
37
|
+
|
|
38
|
+
Choose the IPv4 network interface that is tunneled over the radio link and
|
|
39
|
+
enter your callsign. The default callsign is the reserved placeholder
|
|
40
|
+
`N0CALL`, which puts the application in listen-only mode: it will receive
|
|
41
|
+
traffic but never transmit. Enter your own callsign to enable sending.
|
|
42
|
+
|
|
43
|
+
The **Radio interface** list also offers **Start kisstun (pytap3) tunnel...**
|
|
44
|
+
and **Start Meshtastic tunnel...**. Choose one to configure and start the
|
|
45
|
+
bundled tunnel command; its output appears in a console window. If you use
|
|
46
|
+
the Dire Wolf integration, make sure that `direwolf` executable is on PATH,
|
|
47
|
+
and that the current directory contains the prepared `direwolf.conf` file.
|
|
48
|
+
|
|
49
|
+
The application listens for IP broadcasts on UDP port 37373.
|
|
50
|
+
Outgoing traffic is sent to the selected interface's broadcast address.
|
|
51
|
+
|
|
52
|
+
The application keeps QSO state in memory for the current run. It does not
|
|
53
|
+
persist message history or restore conversations after restart.
|
|
54
|
+
|
|
55
|
+
## Quick example
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
W1ABC>N0XYZ 0:Hello, N0XYZ. This is W1ABC.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The message is sent as a UDP datagram to the interface broadcast address and
|
|
62
|
+
port 37373. The header identifies the sender, intended recipient, and
|
|
63
|
+
sender-local message number; the text after the colon is the message content.
|
|
64
|
+
Double-click a station to open a QSO tab. All valid packets heard update
|
|
65
|
+
the station list, including messages addressed to other stations. Enable
|
|
66
|
+
**Monitor** to also show all received
|
|
67
|
+
non-empty messages in the broadcast transcript, including messages directed
|
|
68
|
+
to others.
|
|
69
|
+
The **Beacon** option is enabled by default and sends an empty `QST` packet
|
|
70
|
+
immediately when manually checked, then at the selected interval, so other
|
|
71
|
+
stations can update Last Heard. Its remaining seconds (or `TX` when a beacon
|
|
72
|
+
is sent) appear on the right side of the status bar.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from cqta.app import main
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def run(argv: list[str] | None = None) -> int:
|
|
7
|
+
arguments = sys.argv[1:] if argv is None else argv
|
|
8
|
+
if arguments and arguments[0] == "kisstun":
|
|
9
|
+
from pytap3.kisstun import main as kisstun_main
|
|
10
|
+
|
|
11
|
+
kisstun_main(arguments[1:])
|
|
12
|
+
return 0
|
|
13
|
+
if arguments and arguments[0] == "meshtastic":
|
|
14
|
+
from meshtastic.__main__ import main as meshtastic_main
|
|
15
|
+
|
|
16
|
+
original_argv = sys.argv
|
|
17
|
+
try:
|
|
18
|
+
sys.argv = [original_argv[0], *arguments[1:]]
|
|
19
|
+
meshtastic_main()
|
|
20
|
+
finally:
|
|
21
|
+
sys.argv = original_argv
|
|
22
|
+
return 0
|
|
23
|
+
return main()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
raise SystemExit(run())
|