-
-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy patharq.h
More file actions
388 lines (335 loc) · 12 KB
/
Copy patharq.h
File metadata and controls
388 lines (335 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
/* HERMES Modem
*
* Copyright (C) 2025 Rhizomatica
* Author: Rafael Diniz <rafael@riseup.net>
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#ifndef ARQ_H_
#define ARQ_H_
#define CALLSIGN_MAX_SIZE 16
#define CALLSIGN_MAX_SECONDARY 4
#define ARQ_BANDWIDTH_NARROW_HZ 500
#define ARQ_BANDWIDTH_FULL_HZ 2300
#define ARQ_BANDWIDTH_TACTICAL_HZ 2750
#define RX 0
#define TX 1
#define HEADER_SIZE 1
#define PACKET_ARQ_CONTROL 0x00
#define PACKET_ARQ_DATA 0x01
#define PACKET_ARQ_CALL 0x02
#define PACKET_BROADCAST_CONTROL 0x03
#define PACKET_BROADCAST_PAYLOAD 0x04
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "arq_events.h"
/** @brief Runtime ARQ connection and mode state shared across modules. */
typedef struct
{
int TRX; // RX (0) or TX (1)
char my_call_sign[CALLSIGN_MAX_SIZE];
char secondary_calls[CALLSIGN_MAX_SECONDARY][CALLSIGN_MAX_SIZE];
int secondary_call_count;
char src_addr[CALLSIGN_MAX_SIZE], dst_addr[CALLSIGN_MAX_SIZE];
bool encryption;
int call_burst_size;
bool listen;
int bw; // configured local bandwidth in Hz
int session_bw; // negotiated session bandwidth in Hz (0 = not negotiated)
int retry_slots; // 0 = use compiled default
size_t frame_size;
int mode;
} arq_info;
/** @brief Action types emitted by ARQ for modem worker execution. */
typedef enum
{
ARQ_ACTION_NONE = 0,
ARQ_ACTION_TX_CONTROL = 1,
ARQ_ACTION_TX_PAYLOAD = 2,
ARQ_ACTION_MODE_SWITCH = 3,
ARQ_ACTION_TX_PATTERN = 4 /* emit a Welch-Costas MFSK pattern ACK — no
* coded frame; pattern_kind selects ACK vs
* ACK+TURN (break). Airtime ~0.64 s. */
} arq_action_type_t;
/** @brief Pattern-ACK symbol kind (ARQ_ACTION_TX_PATTERN). */
typedef enum
{
ARQ_PATTERN_ACK = 0, /* plain ACK: "got your frame, keep the floor" */
ARQ_PATTERN_BREAK = 1 /* ACK+TURN: "got it AND I have data" (HAS_DATA) */
} arq_pattern_kind_t;
/** @brief Single modem action item popped by modem TX worker. */
typedef struct
{
arq_action_type_t type;
int mode;
size_t frame_size; /* bytes per frame */
int frame_count; /* frames in this PTT burst (>= 1); the modem reads
* frame_count * frame_size bytes and modulates them
* behind a single preamble */
int pattern_kind; /* arq_pattern_kind_t, valid for ARQ_ACTION_TX_PATTERN */
} arq_action_t;
/** @brief Snapshot of current ARQ runtime state for telemetry/decision making. */
typedef struct
{
bool initialized;
bool connected;
/* True when a Welch-Costas pattern ACK could legitimately arrive: the
* answerer awaiting the caller's connect-confirm (ACCEPTING) or an active
* session (CONNECTED). The RX pattern detector runs its per-chunk
* correlation only when this is set — running it during CALLING/LISTENING/
* idle is pure overhead that slows the connect-critical DATAC16 decode. */
bool expect_pattern_ack;
int trx;
int tx_backlog_bytes;
int speed_level;
int payload_mode; /* my TX mode (ISS direction) */
int peer_tx_mode; /* RX decoder mode = peer's TX mode (IRS direction) */
int control_mode;
int preferred_rx_mode;
int preferred_tx_mode;
uint64_t tx_bytes;
uint64_t rx_bytes;
} arq_runtime_snapshot_t;
extern arq_info arq_conn;
/* Thread-safe accessors for arq_conn (see g_conn_lock in arq.c).
* None may be called while the caller already holds the connection lock. */
void arq_set_trx(int trx);
int arq_get_trx(void);
/* Configured (non-session) bandwidth in Hz, read under g_conn_lock. */
int arq_get_bw(void);
/* Copy the current callsign strings out under g_conn_lock. Each of my_call,
* src_addr, dst_addr may be NULL to skip; each receives up to bufsz bytes,
* always NUL-terminated. */
void arq_conn_get_calls(char *my_call, char *src_addr, char *dst_addr, size_t bufsz);
/* Wake the ARQ event loop so it re-reads time_now_ms(). Called by the
* -x sock virtual-clock transport after advancing signal time; harmless
* (a spurious wakeup) from any other context. */
void arq_notify_virtual_time(void);
/**
* @brief Initialize ARQ subsystem.
* @param frame_size Active modem frame size in bytes.
* @param mode Initial modem mode.
* @return 0 on success, non-zero on failure.
*/
int arq_init(size_t frame_size, int mode);
/**
* @brief Shut down ARQ workers and release ARQ resources.
*/
void arq_shutdown();
/**
* @brief Execute 1 Hz ARQ maintenance tick.
*/
void arq_tick_1hz(void);
/**
* @brief Post an FSM event to ARQ.
* @param event Event identifier from fsm.h.
*/
void arq_post_event(int event);
/**
* @brief Check whether ARQ link is connected.
* @return true when connected; otherwise false.
*/
bool arq_is_link_connected(void);
/**
* @brief Get the effective ARQ bandwidth cap in Hz used for mode gating.
* @return 500 for narrow-band mode, otherwise 2300.
*/
int arq_effective_bandwidth_hz(void);
/**
* @brief Get the configured ARQ bandwidth token to report to VARA clients.
* @return 500, 2300, or 2750 when configured; otherwise 2300.
*/
int arq_reported_bandwidth_hz(void);
/**
* @brief Check whether the current ARQ bandwidth cap allows a modem mode.
* @param mode FreeDV mode value.
* @return true if the mode is allowed under the active BW setting.
*/
bool arq_bandwidth_allows_mode(int mode);
/**
* @brief Queue outbound payload bytes for ARQ transmission.
* @param data Pointer to payload bytes.
* @param len Number of bytes to queue.
* @return Number of bytes queued, or negative on error.
*/
int arq_queue_data(const uint8_t *data, size_t len);
/**
* @brief Get pending outbound payload backlog.
* @return Backlog size in bytes.
*/
int arq_get_tx_backlog_bytes(void);
/**
* @brief Set the ARQ no-progress disconnect budget.
* @param seconds Budget in seconds. Values <= 0 reset to the compiled
* default (ARQ_NO_PROGRESS_TIMEOUT_S_DEFAULT).
*/
void arq_set_no_progress_timeout_s(int seconds);
/**
* @brief Set the absolute cap on how long an app DISCONNECT may stay deferred
* while draining the last TX bytes.
* @param seconds Window in seconds. Values <= 0 reset to the compiled
* default (ARQ_DISCONNECT_DRAIN_TIMEOUT_S_DEFAULT).
*/
void arq_set_disconnect_drain_timeout_s(int seconds);
/**
* @brief Get current ARQ speed level (gear).
* @return Speed level index.
*/
int arq_get_speed_level(void);
/**
* @brief Get active ARQ payload mode.
* @return FreeDV payload mode value.
*/
int arq_get_payload_mode(void);
/**
* @brief Get active ARQ control mode.
* @return FreeDV control mode value.
*/
int arq_get_control_mode(void);
/**
* @brief Get ARQ-preferred receive mode for modem.
* @return FreeDV mode value.
*/
int arq_get_preferred_rx_mode(void);
/**
* @brief Get ARQ-preferred transmit mode for modem.
* @return FreeDV mode value.
*/
int arq_get_preferred_tx_mode(void);
/**
* @brief Inform ARQ of modem mode/frame size actually active in modem.
* @param mode Active FreeDV mode.
* @param frame_size Active frame size in bytes.
*/
void arq_set_active_modem_mode(int mode, size_t frame_size);
/**
* @brief Handle incoming compressed CALL/ACCEPT frame.
* @param data Frame bytes.
* @param frame_size Frame length in bytes.
* @return true if frame was handled by ARQ connect path.
*/
bool arq_handle_incoming_connect_frame(uint8_t *data, size_t frame_size);
/**
* @brief Handle incoming compact CQ frame and emit host-side CQFRAME notification.
* @param data Incoming frame bytes.
* @param frame_size Frame length in bytes.
* @return true if frame was handled by CQ path.
*/
bool arq_handle_incoming_cq_frame(uint8_t *data, size_t frame_size);
/**
* @brief Emit VARA-style async status for an outgoing CQFRAME transmission.
*
* VARA clients such as varim treat CQFRAME as a pending operation and expect
* PENDING/CANCELPENDING around the actual send lifecycle.
*/
void arq_notify_cq_tx_started(void);
void arq_notify_cq_tx_complete(void);
/**
* @brief Handle incoming regular ARQ control/data frame.
* @param data Frame bytes.
* @param frame_size Frame length in bytes.
* @param rx_snr Local receive SNR at decode time (dB); 0.0 = unknown.
*/
void arq_handle_incoming_frame(uint8_t *data, size_t frame_size, float rx_snr);
/**
* @brief Post a synthesized pattern-ACK event (from the RX pattern detector).
*
* A Welch-Costas pattern ACK carries no coded header, so the modem RX worker
* synthesizes the FSM event directly. In stop-and-wait only one frame is
* outstanding, so an ACK unambiguously acks it.
*
* @param is_break true = ACK+TURN (HAS_DATA piggyback), false = plain ACK.
*/
void arq_post_pattern_ack(bool is_break);
/**
* @brief Feed decoder/link metrics into ARQ adaptation.
* @param sync Decoder sync flag.
* @param snr Estimated SNR.
* @param rx_status Decoder RX status flags.
* @param frame_decoded True when a frame decoded this cycle.
*/
void arq_update_link_metrics(int sync, float snr, int rx_status, bool frame_decoded);
/**
* @brief Try to dequeue next modem action without blocking.
* @param action Output action item.
* @return true if an action was dequeued.
*/
bool arq_try_dequeue_action(arq_action_t *action);
/**
* @brief Wait for next modem action.
* @param action Output action item.
* @param timeout_ms Wait timeout in milliseconds.
* @return true if an action was dequeued.
*/
bool arq_wait_dequeue_action(arq_action_t *action, int timeout_ms);
/**
* @brief Copy ARQ runtime snapshot.
* @param snapshot Output snapshot pointer.
* @return true when snapshot contains initialized state.
*/
bool arq_get_runtime_snapshot(arq_runtime_snapshot_t *snapshot);
/**
* @brief Submit parsed control command from TCP bridge to ARQ.
* @param cmd Command message.
* @return 0 on success, negative on queue/error.
*/
int arq_submit_tcp_cmd(const arq_cmd_msg_t *cmd);
/**
* @brief Submit payload bytes from TCP bridge to ARQ.
* @param data Payload bytes.
* @param len Payload length in bytes.
* @return 0 on success, negative on queue/error.
*/
int arq_submit_tcp_payload(const uint8_t *data, size_t len);
/**
* @brief Clear legacy ARQ connection state and buffers.
*/
void clear_connection_data();
/**
* @brief Reset external arq_info structure.
* @param arq_conn Pointer to structure to reset.
*/
void reset_arq_info(arq_info *arq_conn);
/**
* @brief Set runtime retry slot counts.
*
* Updates CALL, ACCEPT, and DATA retry counters used by the ARQ FSM.
* DISCONNECT retries are left at the compiled default.
* Pass 0 to restore all counters to compiled defaults.
*
* @param slots Number of retry slots, or 0 to restore defaults.
*/
void arq_set_retry_slots(int slots);
/* Set only the DATA-frame retry slots (leaves CALL/ACCEPT/DISCONNECT alone).
* Used by the startup config so a large data_retry_slots does not also inflate
* connection-setup (CALL/ACCEPT) retries. */
void arq_set_data_retry_slots(int slots);
/* Setters for the newly-atomic ARQ timing/ladder tunables. */
void arq_set_channel_guard_ms(int ms);
void arq_set_iss_post_ack_guard_ms(int ms);
void arq_set_ladder_up_successes(int n);
void arq_set_retry_downgrade_threshold(int n);
void arq_set_mode_hold_after_downgrade_s(int s);
void arq_set_peer_payload_hold_s(int s);
/**
* @brief Trigger outgoing call attempt using current ARQ addresses.
*/
void call_remote();
/**
* @brief Trigger callee-side accept flow in compatibility path.
*/
void callee_accept_connection();
#endif