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 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,3 @@
1
+ """CQta protocol and application."""
2
+
3
+ __version__ = "1.2"
@@ -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())