mirror of
https://github.com/drowe67/codec2
synced 2026-08-14 19:31:34 -04:00
adding initial arq code
This commit is contained in:
parent
3741b0cab5
commit
c7c3f385a4
14 changed files with 2000 additions and 196 deletions
|
|
@ -1,6 +1,6 @@
|
|||
/* Audio subsystem
|
||||
*
|
||||
* Copyright (C) 2024 Rhizomatica
|
||||
* Copyright (C) 2024-2025 Rhizomatica
|
||||
* Author: Rafael Diniz <rafael@rhizomatica.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include "ring_buffer_posix.h"
|
||||
#include "shm_posix.h"
|
||||
#include "common_defines.h"
|
||||
#include "../datalink/defines.h"
|
||||
#include "os_interop.h"
|
||||
|
||||
#include "audioio.h"
|
||||
|
|
@ -32,13 +32,6 @@ cbuf_handle_t playback_buffer;
|
|||
|
||||
int audio_subsystem;
|
||||
|
||||
#if defined(_WIN32)
|
||||
HANDLE capture_prep_mutex;
|
||||
#else
|
||||
pthread_mutex_t capture_prep_mutex;
|
||||
#endif
|
||||
|
||||
|
||||
// tap to file FOR DEBUGGING PURPOSES //
|
||||
#define ENABLE_FLOAT64_TAP 0
|
||||
#define ENABLE_FLOAT64_TAP_BEFORE 0
|
||||
|
|
@ -128,9 +121,9 @@ void *radio_playback_thread(void *device_ptr)
|
|||
ffuint frame_size;
|
||||
ffuint msec_bytes;
|
||||
|
||||
uint8_t *buffer = (uint8_t *) malloc(AUDIO_PAYLOAD_BUFFER_SIZE * sizeof(double) * 2);
|
||||
uint8_t *buffer = (uint8_t *) malloc(SIGNAL_BUFFER_SIZE * sizeof(double) * 2);
|
||||
double *buffer_double = (double *) buffer;
|
||||
int32_t *buffer_internal_stereo = (int32_t *) malloc(AUDIO_PAYLOAD_BUFFER_SIZE * sizeof(int32_t) * 2); // a big enough buffer
|
||||
int32_t *buffer_internal_stereo = (int32_t *) malloc(SIGNAL_BUFFER_SIZE * sizeof(int32_t) * 2); // a big enough buffer
|
||||
|
||||
ffuint total_written = 0;
|
||||
int ch_layout = STEREO;
|
||||
|
|
@ -370,7 +363,7 @@ void *radio_capture_thread(void *device_ptr)
|
|||
frame_size = cfg->channels * (cfg->format & 0xff) / 8;
|
||||
msec_bytes = cfg->sample_rate * frame_size / 1000;
|
||||
|
||||
buffer_internal = (double *) malloc(AUDIO_PAYLOAD_BUFFER_SIZE * sizeof(double) * 2);
|
||||
buffer_internal = (double *) malloc(SIGNAL_BUFFER_SIZE * sizeof(double) * 2);
|
||||
|
||||
#if 0 // TODO: parametrize this
|
||||
if (radio_type == RADIO_SBITX)
|
||||
|
|
@ -567,7 +560,7 @@ int rx_transfer(double *buffer, size_t len)
|
|||
|
||||
|
||||
int audioio_init_internal(char *capture_dev, char *playback_dev, int audio_subsys, pthread_t *radio_capture,
|
||||
pthread_t *radio_playback, pthread_t *radio_capture_prep)
|
||||
pthread_t *radio_playback)
|
||||
{
|
||||
audio_subsystem = audio_subsys;
|
||||
|
||||
|
|
@ -576,13 +569,13 @@ int audioio_init_internal(char *capture_dev, char *playback_dev, int audio_subsy
|
|||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
uint8_t *buffer_cap = (uint8_t *)malloc(AUDIO_PAYLOAD_BUFFER_SIZE);
|
||||
uint8_t *buffer_play = (uint8_t *)malloc(AUDIO_PAYLOAD_BUFFER_SIZE);
|
||||
capture_buffer = circular_buf_init(buffer_cap, AUDIO_PAYLOAD_BUFFER_SIZE);
|
||||
playback_buffer = circular_buf_init(buffer_play, AUDIO_PAYLOAD_BUFFER_SIZE);
|
||||
uint8_t *buffer_cap = (uint8_t *)malloc(SIGNAL_BUFFER_SIZE);
|
||||
uint8_t *buffer_play = (uint8_t *)malloc(SIGNAL_BUFFER_SIZE);
|
||||
capture_buffer = circular_buf_init(buffer_cap, SIGNAL_BUFFER_SIZE);
|
||||
playback_buffer = circular_buf_init(buffer_play, SIGNAL_BUFFER_SIZE);
|
||||
#else
|
||||
capture_buffer = circular_buf_init_shm(AUDIO_PAYLOAD_BUFFER_SIZE, (char *) AUDIO_CAPT_PAYLOAD_NAME);
|
||||
playback_buffer = circular_buf_init_shm(AUDIO_PAYLOAD_BUFFER_SIZE, (char *) AUDIO_PLAY_PAYLOAD_NAME);
|
||||
capture_buffer = circular_buf_init_shm(SIGNAL_BUFFER_SIZE, (char *) SIGNAL_INPUT);
|
||||
playback_buffer = circular_buf_init_shm(SIGNAL_BUFFER_SIZE, (char *) SIGNAL_OUTPUT);
|
||||
#endif
|
||||
|
||||
clear_buffer(capture_buffer);
|
||||
|
|
@ -594,9 +587,8 @@ int audioio_init_internal(char *capture_dev, char *playback_dev, int audio_subsy
|
|||
return 0;
|
||||
}
|
||||
|
||||
int audioio_deinit(pthread_t *radio_capture, pthread_t *radio_playback, pthread_t *radio_capture_prep)
|
||||
int audioio_deinit(pthread_t *radio_capture, pthread_t *radio_playback)
|
||||
{
|
||||
pthread_join(*radio_capture_prep, NULL);
|
||||
pthread_join(*radio_capture, NULL);
|
||||
pthread_join(*radio_playback, NULL);
|
||||
|
||||
|
|
@ -610,10 +602,10 @@ int audioio_deinit(pthread_t *radio_capture, pthread_t *radio_playback, pthread_
|
|||
free(playback_buffer->buffer);
|
||||
circular_buf_free(playback_buffer);
|
||||
#else
|
||||
circular_buf_destroy_shm(capture_buffer, AUDIO_PAYLOAD_BUFFER_SIZE, (char *) AUDIO_CAPT_PAYLOAD_NAME);
|
||||
circular_buf_destroy_shm(capture_buffer, SIGNAL_BUFFER_SIZE, (char *) SIGNAL_INPUT);
|
||||
circular_buf_free_shm(capture_buffer);
|
||||
|
||||
circular_buf_destroy_shm(playback_buffer, AUDIO_PAYLOAD_BUFFER_SIZE, (char *) AUDIO_PLAY_PAYLOAD_NAME);
|
||||
circular_buf_destroy_shm(playback_buffer, SIGNAL_BUFFER_SIZE, (char *) SIGNAL_OUTPUT);
|
||||
circular_buf_free_shm(playback_buffer);
|
||||
#endif
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -34,17 +34,11 @@
|
|||
extern cbuf_handle_t capture_buffer;
|
||||
extern cbuf_handle_t playback_buffer;
|
||||
|
||||
#if defined(_WIN32)
|
||||
extern HANDLE capture_prep_mutex;
|
||||
#else
|
||||
extern pthread_mutex_t capture_prep_mutex;
|
||||
#endif
|
||||
|
||||
|
||||
int audioio_init_internal(char *capture_dev, char *playback_dev, int audio_subsys, pthread_t *radio_capture,
|
||||
pthread_t *radio_playback, pthread_t *radio_capture_prep);
|
||||
pthread_t *radio_playback);
|
||||
|
||||
int audioio_deinit(pthread_t *radio_capture, pthread_t *radio_playback, pthread_t *radio_capture_prep);
|
||||
int audioio_deinit(pthread_t *radio_capture, pthread_t *radio_playback);
|
||||
|
||||
int tx_transfer(double *buffer, size_t len);
|
||||
int rx_transfer(double *buffer, size_t len);
|
||||
|
|
|
|||
|
|
@ -1,157 +0,0 @@
|
|||
/*
|
||||
* Mercury: A configurable open-source software-defined modem.
|
||||
* Copyright (C) 2022-2024 Fadi Jerji
|
||||
* Author: Fadi Jerji
|
||||
* Email: fadi.jerji@ <gmail.com, caisresearch.com, ieee.org>
|
||||
* ORCID: 0000-0002-2076-5831
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, version 3 of the
|
||||
* License.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INC_COMMON_DEFINES_H_
|
||||
#define INC_COMMON_DEFINES_H_
|
||||
|
||||
#define VERSION__ "0.4.0"
|
||||
|
||||
#define BER_PLOT_baseband 0
|
||||
#define BER_PLOT_passband 1
|
||||
#define TX_RAND 2
|
||||
#define RX_RAND 3
|
||||
#define TX_TEST 4
|
||||
#define RX_TEST 5
|
||||
#define TX_SHM 6
|
||||
#define RX_SHM 7
|
||||
#define ARQ_MODE 8
|
||||
|
||||
#define NUMBER_OF_CONFIGS 17
|
||||
#define CONFIG_NONE -1
|
||||
#define CONFIG_0 0
|
||||
#define CONFIG_1 1
|
||||
#define CONFIG_2 2
|
||||
#define CONFIG_3 3
|
||||
#define CONFIG_4 4
|
||||
#define CONFIG_5 5
|
||||
#define CONFIG_6 6
|
||||
#define CONFIG_7 7
|
||||
#define CONFIG_8 8
|
||||
#define CONFIG_9 9
|
||||
#define CONFIG_10 10
|
||||
#define CONFIG_11 11
|
||||
#define CONFIG_12 12
|
||||
#define CONFIG_13 13
|
||||
#define CONFIG_14 14
|
||||
#define CONFIG_15 15
|
||||
#define CONFIG_16 16
|
||||
|
||||
|
||||
/*
|
||||
* Config CODE Mode EsN0(FER<0,1)
|
||||
0 BPSK 1/16 BPSK 1/16 -10
|
||||
1 BPSK 2/16 BPSK 2/16 -7,5
|
||||
2 BPSK 3/16 BPSK 3/16 -6
|
||||
3 BPSK 4/16 BPSK 4/16 -4,5
|
||||
4 BPSK 5/16 BPSK 5/16 -3,5
|
||||
5 BPSK 6/16 BPSK 6/16 -2,5
|
||||
6 BPSK 8/16 BPSK 8/16 -1,5
|
||||
7 QPSK 5/16 QPSK 5/16 -0,5
|
||||
8 QPSK 6/16 QPSK 6/16 0,5
|
||||
9 QPSK 8/16 QPSK 8/16 1,5
|
||||
10 8PSK 6/16 8PSK 6/16 3
|
||||
11 8PSK 8/16 8PSK 8/16 4
|
||||
12 QPSK 14/16 QPSK 14/16 6,5
|
||||
13 16QAM 8/16 16QAM 8/16 7,5
|
||||
14 8PSK 14/16 8PSK 14/16 9
|
||||
15 16QAM 14/16 16QAM 14/16 12,5
|
||||
16 32QAM 14/16 32QAM 14/16 13,5
|
||||
|
||||
|
||||
HIGH_DENSITY PILOTS
|
||||
|
||||
CONFIG_0 (71.3 bps).
|
||||
CONFIG_1 (156.1 bps).
|
||||
CONFIG_2 (241.0 bps).
|
||||
CONFIG_3 (325.8 bps).
|
||||
CONFIG_4 (410.6 bps).
|
||||
CONFIG_5 (495.5 bps).
|
||||
CONFIG_6 (665.2 bps).
|
||||
CONFIG_7 (762.6 bps).
|
||||
CONFIG_8 (920.2 bps).
|
||||
CONFIG_9 (1235.3 bps).
|
||||
CONFIG_10 (1353.7 bps).
|
||||
CONFIG_11 (1818.1 bps).
|
||||
CONFIG_12 (2261.4 bps).
|
||||
CONFIG_13 (2470.6 bps).
|
||||
CONFIG_14 (3389.7 bps).
|
||||
CONFIG_15 (4361.3 bps).
|
||||
CONFIG_16 (5664.7 bps).
|
||||
|
||||
|
||||
LOW DENSITY PILOTS
|
||||
|
||||
CONFIG_0 (84.2 bps).
|
||||
CONFIG_1 (184.5 bps).
|
||||
CONFIG_2 (284.8 bps).
|
||||
CONFIG_3 (385.0 bps).
|
||||
CONFIG_4 (485.3 bps).
|
||||
CONFIG_5 (585.6 bps).
|
||||
CONFIG_6 (786.1 bps).
|
||||
CONFIG_7 (889.7 bps).
|
||||
CONFIG_8 (1073.5 bps).
|
||||
CONFIG_9 (1441.2 bps).
|
||||
CONFIG_10 (1353.7 bps).
|
||||
CONFIG_11 (1818.1 bps).
|
||||
CONFIG_12 (2654.7 bps).
|
||||
CONFIG_13 (2882.4 bps).
|
||||
CONFIG_14 (3389.7 bps).
|
||||
CONFIG_15 (5088.2 bps).
|
||||
CONFIG_16 (5664.7 bps).
|
||||
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// messages definition
|
||||
#define FIRST_MESSAGE 0
|
||||
#define MIDDLE_MESSAGE 1
|
||||
#define FLUSH_MESSAGE 2
|
||||
#define SINGLE_MESSAGE 3
|
||||
#define NO_FILTER_MESSAGE 4
|
||||
|
||||
// supported radios
|
||||
#define RADIO_SBITX 0
|
||||
#define RADIO_STOCKHF 1
|
||||
|
||||
// {TX,RX}_SHM shared memory interface
|
||||
#define SHM_PAYLOAD_BUFFER_SIZE 131072
|
||||
#define SHM_PAYLOAD_NAME "/mercury-comm"
|
||||
|
||||
// audio buffers shared memory interface
|
||||
// 1536000 * 8
|
||||
#define AUDIO_PAYLOAD_BUFFER_SIZE 12288000
|
||||
#define AUDIO_CAPT_PAYLOAD_NAME "/audio-capt"
|
||||
#define AUDIO_PLAY_PAYLOAD_NAME "/audio-play"
|
||||
|
||||
|
||||
// Gear shifting modes
|
||||
#define NO_GEAR_SHIFT 0
|
||||
#define GEAR_SHIFT_ENABLED 1
|
||||
// #define NO_GEAR_SHIFT_LADDER 2
|
||||
// #define NO_GEAR_SHIFT_SNR 3
|
||||
|
||||
#define YES 1
|
||||
#define NO 0
|
||||
|
||||
#endif // INC_COMMON_DEFINES_H_
|
||||
|
|
@ -46,11 +46,28 @@ LDFLAGS=$(FFAUDIO_LINKFLAGS) -lm
|
|||
|
||||
all: ../modem
|
||||
|
||||
../modem: main.o ../freedv/libfreedvdata.a ../audioio/audioio.a ../common/os_interop.o ../common/ring_buffer_posix.o ../common/shm_posix.o
|
||||
$(CC) -o ../modem main.o ../freedv/libfreedvdata.a ../audioio/audioio.a ../common/os_interop.o ../common/ring_buffer_posix.o ../common/shm_posix.o $(LDFLAGS)
|
||||
../modem: main.o arq.o net.o fsm.o arith.o crc6.o ../freedv/libfreedvdata.a ../audioio/audioio.a \
|
||||
../common/os_interop.o ../common/ring_buffer_posix.o ../common/shm_posix.o
|
||||
$(CC) -o ../modem main.o arq.o net.o fsm.o arith.o crc6.o ../freedv/libfreedvdata.a ../audioio/audioio.a \
|
||||
../common/os_interop.o ../common/ring_buffer_posix.o ../common/shm_posix.o $(LDFLAGS)
|
||||
|
||||
main.o: main.c
|
||||
$(CC) $(CFLAGS) -c main.c
|
||||
|
||||
arq.o: arq.c
|
||||
$(CC) $(CFLAGS) -c arq.c
|
||||
|
||||
net.o: net.c
|
||||
$(CC) $(CFLAGS) -c net.c
|
||||
|
||||
fsm.o: fsm.c
|
||||
$(CC) $(CFLAGS) -c fsm.c
|
||||
|
||||
arith.o: arith.c
|
||||
$(CC) $(CFLAGS) -c arith.c
|
||||
|
||||
crc6.o: crc6.c
|
||||
$(CC) $(CFLAGS) -c crc6.c
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
|
|
|
|||
382
datalink/arith.c
Normal file
382
datalink/arith.c
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define NUM_SYMBOLS 39 // 37 symbols + 1 EOF + separator
|
||||
#define CODE_BITS 32
|
||||
#define MAX_CODE ((1ULL << CODE_BITS) - 1)
|
||||
#define HALF (1ULL << (CODE_BITS - 1))
|
||||
#define QUARTER (HALF >> 1)
|
||||
#define THREE_QUARTERS (HALF + QUARTER)
|
||||
#define BUFFER_SIZE 4096
|
||||
#define MAX_ENCODE_BITS (BUFFER_SIZE * 8)
|
||||
#define MAX_PENDING 1024
|
||||
|
||||
char symbols[NUM_SYMBOLS] = {
|
||||
'A','B','C','D','E','F','G','H','I','J','K','L','M',
|
||||
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
|
||||
'0','1','2','3','4','5','6','7','8','9','-', '|',
|
||||
'\0' // EOF symbol
|
||||
};
|
||||
|
||||
uint64_t cum_freq[NUM_SYMBOLS + 1];
|
||||
|
||||
void init_model() {
|
||||
for (int i = 0; i <= NUM_SYMBOLS; i++)
|
||||
cum_freq[i] = i;
|
||||
}
|
||||
|
||||
int find_index(char c) {
|
||||
for (int i = 0; i < NUM_SYMBOLS; i++)
|
||||
if (symbols[i] == c)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint8_t* buffer;
|
||||
int bitpos;
|
||||
int bytepos;
|
||||
int total_bits;
|
||||
} BitWriter;
|
||||
|
||||
void bw_init(BitWriter* bw, uint8_t* buf) {
|
||||
bw->buffer = buf;
|
||||
bw->bitpos = 0;
|
||||
bw->bytepos = 0;
|
||||
bw->total_bits = 0;
|
||||
memset(buf, 0, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void bw_write_bit(BitWriter* bw, int bit) {
|
||||
if (bw->bytepos * 8 + bw->bitpos >= MAX_ENCODE_BITS) {
|
||||
fprintf(stderr, "Fatal: bit output exceeded safe limit (%d bits)\n", MAX_ENCODE_BITS);
|
||||
exit(1);
|
||||
}
|
||||
if (bit)
|
||||
bw->buffer[bw->bytepos] |= (1 << (7 - bw->bitpos));
|
||||
if (++bw->bitpos == 8) {
|
||||
bw->bitpos = 0;
|
||||
bw->bytepos++;
|
||||
}
|
||||
bw->total_bits++;
|
||||
}
|
||||
|
||||
int bw_bytes(BitWriter* bw) {
|
||||
return bw->bytepos + ((bw->bitpos != 0)?1:0);
|
||||
}
|
||||
|
||||
#if 0
|
||||
typedef struct {
|
||||
uint8_t* buffer;
|
||||
int bitpos;
|
||||
int bytepos;
|
||||
int length;
|
||||
int total_bits_read; // NEW: track total bits consumed
|
||||
} BitReader;
|
||||
|
||||
void br_init(BitReader* br, uint8_t* buf, int len) {
|
||||
br->buffer = buf;
|
||||
br->bitpos = 0;
|
||||
br->bytepos = 0;
|
||||
br->length = len;
|
||||
br->total_bits_read = 0;
|
||||
}
|
||||
|
||||
int br_read_bit(BitReader* br) {
|
||||
if (br->bytepos >= br->length)
|
||||
return -1; // signal bitstream exhausted
|
||||
|
||||
int bit = (br->buffer[br->bytepos] >> (7 - br->bitpos)) & 1;
|
||||
br->total_bits_read++;
|
||||
|
||||
if (++br->bitpos == 8) {
|
||||
br->bitpos = 0;
|
||||
br->bytepos++;
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
typedef struct {
|
||||
uint8_t* buffer;
|
||||
int bitpos;
|
||||
int bytepos;
|
||||
int length;
|
||||
int max_read_ahead;
|
||||
} BitReader;
|
||||
|
||||
|
||||
void br_init(BitReader* br, uint8_t* buf, int len) {
|
||||
br->buffer = buf;
|
||||
br->bitpos = 0;
|
||||
br->bytepos = 0;
|
||||
br->length = len;
|
||||
br->max_read_ahead = 24;
|
||||
}
|
||||
|
||||
int br_read_bit(BitReader* br)
|
||||
{
|
||||
// limit how far we go...
|
||||
if (br->bytepos >= br->length)
|
||||
{
|
||||
br->max_read_ahead--;
|
||||
if (br->max_read_ahead == 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
int bit = (br->buffer[br->bytepos] >> (7 - br->bitpos)) & 1;
|
||||
if (++br->bitpos == 8) {
|
||||
br->bitpos = 0;
|
||||
br->bytepos++;
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
#endif
|
||||
|
||||
int arithmetic_encode(const char* msg, uint8_t* output) {
|
||||
BitWriter bw;
|
||||
bw_init(&bw, output);
|
||||
|
||||
uint64_t low = 0, high = MAX_CODE;
|
||||
int pending = 0;
|
||||
uint64_t total = cum_freq[NUM_SYMBOLS];
|
||||
|
||||
for (int i = 0;; i++) {
|
||||
int sym;
|
||||
if (msg[i] == '\0') {
|
||||
sym = NUM_SYMBOLS - 1; // EOF symbol
|
||||
} else {
|
||||
sym = find_index(msg[i]);
|
||||
if (sym < 0) {
|
||||
fprintf(stderr, "Unknown symbol: %c\n", msg[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t range = high - low + 1;
|
||||
uint64_t sym_low = cum_freq[sym];
|
||||
uint64_t sym_high = cum_freq[sym + 1];
|
||||
|
||||
high = low + (range * sym_high) / total - 1;
|
||||
low = low + (range * sym_low) / total;
|
||||
|
||||
while (1) {
|
||||
if (high < HALF) {
|
||||
bw_write_bit(&bw, 0);
|
||||
while (pending-- > 0)
|
||||
bw_write_bit(&bw, 1);
|
||||
pending = 0;
|
||||
} else if (low >= HALF) {
|
||||
bw_write_bit(&bw, 1);
|
||||
while (pending-- > 0)
|
||||
bw_write_bit(&bw, 0);
|
||||
pending = 0;
|
||||
low -= HALF;
|
||||
high -= HALF;
|
||||
} else if (low >= QUARTER && high < THREE_QUARTERS) {
|
||||
pending++;
|
||||
low -= QUARTER;
|
||||
high -= QUARTER;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
low <<= 1;
|
||||
high = (high << 1) | 1;
|
||||
}
|
||||
|
||||
if (sym == NUM_SYMBOLS - 1) break; // EOF encoded
|
||||
}
|
||||
|
||||
// Final bits
|
||||
pending++;
|
||||
if (low < QUARTER) {
|
||||
bw_write_bit(&bw, 0);
|
||||
while (pending-- > 0)
|
||||
bw_write_bit(&bw, 1);
|
||||
} else {
|
||||
bw_write_bit(&bw, 1);
|
||||
while (pending-- > 0)
|
||||
bw_write_bit(&bw, 0);
|
||||
}
|
||||
|
||||
printf("\n[ENCODE DONE] Total bits written: %d (%d bytes)\n", bw.total_bits, bw_bytes(&bw));
|
||||
printf("Bit packing: MSB-first within each byte (big-endian per byte)\n");
|
||||
|
||||
return bw_bytes(&bw);
|
||||
}
|
||||
|
||||
#if 1
|
||||
int arithmetic_decode(uint8_t* input, int max_len, char* output) {
|
||||
BitReader br;
|
||||
br_init(&br, input, max_len);
|
||||
|
||||
uint64_t low = 0, high = MAX_CODE;
|
||||
uint64_t value = 0;
|
||||
uint64_t total = cum_freq[NUM_SYMBOLS];
|
||||
|
||||
int outpos = 0;
|
||||
//printf("br.total_bits_read %d\n", br.total_bits_read);
|
||||
// Initialize value
|
||||
for (int i = 0; i < CODE_BITS; i++) {
|
||||
int b = br_read_bit(&br);
|
||||
if (b < 0) {
|
||||
fprintf(stderr, "Decode error: bitstream ended too early (init)\n");
|
||||
return -1;
|
||||
}
|
||||
value = (value << 1) | b;
|
||||
}
|
||||
|
||||
|
||||
while (1) {
|
||||
//printf("br.total_bits_read %d\n", br.total_bits_read);
|
||||
|
||||
uint64_t range = high - low + 1;
|
||||
uint64_t scaled = ((value - low + 1) * total - 1) / range;
|
||||
|
||||
int sym = 0;
|
||||
while (!(scaled >= cum_freq[sym] && scaled < cum_freq[sym + 1])) {
|
||||
sym++;
|
||||
if (sym >= NUM_SYMBOLS) {
|
||||
fprintf(stderr, "Decode error: no matching symbol for scaled=%lu\n", scaled);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (symbols[sym] == '\0') {
|
||||
break; // EOF reached
|
||||
}
|
||||
output[outpos++] = symbols[sym];
|
||||
|
||||
high = low + (range * cum_freq[sym + 1]) / total - 1;
|
||||
low = low + (range * cum_freq[sym]) / total;
|
||||
|
||||
while (1) {
|
||||
if (high < HALF) {
|
||||
// no renormalization needed
|
||||
} else if (low >= HALF) {
|
||||
value -= HALF;
|
||||
low -= HALF;
|
||||
high -= HALF;
|
||||
} else if (low >= QUARTER && high < THREE_QUARTERS) {
|
||||
value -= QUARTER;
|
||||
low -= QUARTER;
|
||||
high -= QUARTER;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
low <<= 1;
|
||||
high = (high << 1) | 1;
|
||||
int b = br_read_bit(&br);
|
||||
if (b < 0) {
|
||||
fprintf(stderr, "Decode error: bitstream ended too early (loop)\n");
|
||||
goto finish;
|
||||
}
|
||||
value = (value << 1) | b;
|
||||
}
|
||||
}
|
||||
|
||||
//printf("br.total_bits_read %d\n", br.total_bits_read);
|
||||
finish:
|
||||
output[outpos] = '\0';
|
||||
return 0;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
int arithmetic_decode(uint8_t* input, int max_len, char* output) {
|
||||
BitReader br;
|
||||
br_init(&br, input, max_len);
|
||||
|
||||
uint64_t low = 0, high = MAX_CODE;
|
||||
uint64_t value = 0;
|
||||
uint64_t total = cum_freq[NUM_SYMBOLS];
|
||||
|
||||
// printf("br.bytepos %d br.bitpos %d\n", br.bytepos, br.bitpos);
|
||||
|
||||
for (int i = 0; i < CODE_BITS; i++)
|
||||
value = (value << 1) | br_read_bit(&br);
|
||||
|
||||
int outpos = 0;
|
||||
while (1) {
|
||||
// printf("br.bytepos %d br.bitpos %d\n", br.bytepos, br.bitpos);
|
||||
uint64_t range = high - low + 1;
|
||||
uint64_t scaled = ((value - low + 1) * total - 1) / range;
|
||||
|
||||
int sym = 0;
|
||||
while (!(scaled >= cum_freq[sym] && scaled < cum_freq[sym + 1])) {
|
||||
sym++;
|
||||
if (sym >= NUM_SYMBOLS) {
|
||||
fprintf(stderr, "Decode error: no matching symbol for scaled=%lu\n", scaled);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (symbols[sym] == '\0')
|
||||
{
|
||||
break; // EOF reached
|
||||
}
|
||||
output[outpos++] = symbols[sym];
|
||||
|
||||
high = low + (range * cum_freq[sym + 1]) / total - 1;
|
||||
low = low + (range * cum_freq[sym]) / total;
|
||||
|
||||
while (1) {
|
||||
if (high < HALF) {
|
||||
// do nothing
|
||||
} else if (low >= HALF) {
|
||||
value -= HALF;
|
||||
low -= HALF;
|
||||
high -= HALF;
|
||||
} else if (low >= QUARTER && high < THREE_QUARTERS) {
|
||||
value -= QUARTER;
|
||||
low -= QUARTER;
|
||||
high -= QUARTER;
|
||||
} else break;
|
||||
|
||||
low <<= 1;
|
||||
high = (high << 1) | 1;
|
||||
value = (value << 1) | br_read_bit(&br);
|
||||
}
|
||||
}
|
||||
|
||||
output[outpos] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
int main() {
|
||||
init_model();
|
||||
const char* msg1 = "PU2UIT-15|PU4GNU-15";
|
||||
printf("Inputs: %s \n", msg1);
|
||||
|
||||
uint8_t encoded[BUFFER_SIZE];
|
||||
int enc_len1 = arithmetic_encode(msg1, encoded);
|
||||
printf("Encoded length 1: %d bytes\n", enc_len1);
|
||||
|
||||
// int enc_len2 = arithmetic_encode(msg2, encoded + enc_len1);
|
||||
// printf("Encoded length 2: %d bytes\n", enc_len2);
|
||||
|
||||
|
||||
init_model();
|
||||
char decoded[100];
|
||||
int dec_len1;
|
||||
|
||||
if (arithmetic_decode(encoded, 11, decoded) == 0)
|
||||
printf("Decoded: %s\n", decoded);
|
||||
|
||||
|
||||
// int dec_len2;
|
||||
// if (arithmetic_decode(encoded + dec_len1, 16 - dec_len1, &dec_len2, decoded) == 0)
|
||||
// printf("Decoded: %s encoded length: %d\n", decoded, dec_len2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
904
datalink/arq.c
Normal file
904
datalink/arq.c
Normal file
|
|
@ -0,0 +1,904 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
|
||||
#include "arq.h"
|
||||
#include "fsm.h"
|
||||
#include "../audioio/audioio.h"
|
||||
#include "net.h"
|
||||
#include "defines.h"
|
||||
|
||||
extern cbuf_handle_t capture_buffer;
|
||||
extern cbuf_handle_t playback_buffer;
|
||||
|
||||
cbuf_handle_t data_tx_buffer;
|
||||
cbuf_handle_t data_rx_buffer;
|
||||
|
||||
extern bool shutdown_;
|
||||
|
||||
// ARQ definitions
|
||||
arq_info arq_conn;
|
||||
|
||||
#define DEBUG
|
||||
|
||||
static pthread_t tid[8];
|
||||
|
||||
|
||||
// our simple fsm struct
|
||||
fsm_handle arq_fsm;
|
||||
|
||||
/* FSM States */
|
||||
void state_no_connected_client(int event)
|
||||
{
|
||||
printf("FSM State: no_connected_client\n");
|
||||
|
||||
switch(event)
|
||||
{
|
||||
case EV_CLIENT_CONNECT:
|
||||
clear_connection_data();
|
||||
arq_fsm.current = state_idle;
|
||||
break;
|
||||
default:
|
||||
printf("Event: %d ignored in state_no_connected_client().\n", event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void state_link_connected(int event)
|
||||
{
|
||||
printf("FSM State: link_connected\n");
|
||||
|
||||
switch(event)
|
||||
{
|
||||
case EV_CLIENT_DISCONNECT:
|
||||
arq_fsm.current = state_no_connected_client;
|
||||
break;
|
||||
|
||||
case EV_LINK_DISCONNECT:
|
||||
arq_fsm.current = (arq_conn.listen == true)? state_listen : state_idle;
|
||||
break;
|
||||
default:
|
||||
printf("Event: %d ignored in state_disconnected().\n", event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void state_listen(int event)
|
||||
{
|
||||
printf("FSM State: listen\n");
|
||||
|
||||
switch(event)
|
||||
{
|
||||
case EV_START_LISTEN:
|
||||
printf("EV_START_LISTEN ignored in state_listen() - already listening.\n");
|
||||
break;
|
||||
case EV_STOP_LISTEN:
|
||||
arq_conn.listen = false;
|
||||
arq_fsm.current = state_idle;
|
||||
break;
|
||||
case EV_LINK_CALL_REMOTE:
|
||||
call_remote();
|
||||
arq_fsm.current = state_connecting_caller;
|
||||
break;
|
||||
case EV_LINK_DISCONNECT:
|
||||
printf("EV_LINK_DISCONNECT ignored in state_listen() - not connected.\n");
|
||||
break;
|
||||
case EV_CLIENT_DISCONNECT:
|
||||
clear_connection_data();
|
||||
arq_fsm.current = state_no_connected_client;
|
||||
break;
|
||||
case EV_LINK_INCOMING_CALL:
|
||||
callee_accept_connection(); //packets created to anwser in case of incoming call with correct callsign
|
||||
arq_fsm.current = state_connecting_callee;
|
||||
break;
|
||||
default:
|
||||
printf("Event: %d ignored in state_listen().\n", event);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void state_idle(int event)
|
||||
{
|
||||
printf("FSM State: idle\n");
|
||||
|
||||
switch(event)
|
||||
{
|
||||
case EV_START_LISTEN:
|
||||
arq_conn.listen = true;
|
||||
arq_fsm.current = state_listen;
|
||||
break;
|
||||
case EV_STOP_LISTEN:
|
||||
arq_conn.listen = false;
|
||||
printf("EV_STOP_LISTEN ignored in state_idle() - already stopped.\n");
|
||||
break;
|
||||
case EV_LINK_CALL_REMOTE:
|
||||
call_remote();
|
||||
arq_fsm.current = state_connecting_caller;
|
||||
break;
|
||||
case EV_LINK_DISCONNECT:
|
||||
printf("EV_LINK_DISCONNECT ignored in state_idle() - not connected.\n");
|
||||
break;
|
||||
case EV_CLIENT_DISCONNECT:
|
||||
clear_connection_data();
|
||||
arq_fsm.current = state_no_connected_client;
|
||||
break;
|
||||
default:
|
||||
printf("Event: %d ignored from state_idle\n", event);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void state_connecting_caller(int event)
|
||||
{
|
||||
printf("FSM State: connecting_caller\n");
|
||||
|
||||
switch(event)
|
||||
{
|
||||
case EV_START_LISTEN:
|
||||
arq_conn.listen = true;
|
||||
break;
|
||||
case EV_STOP_LISTEN:
|
||||
arq_conn.listen = false;
|
||||
break;
|
||||
case EV_LINK_CALL_REMOTE:
|
||||
printf("EV_LINK_CALL_REMOTE ignored in state_connecting_caller() - already connecting.\n");
|
||||
break;
|
||||
case EV_LINK_DISCONNECT:
|
||||
// TODO: kill the connection first? Do we need to do something?
|
||||
arq_fsm.current = (arq_conn.listen == true)? state_listen : state_idle;
|
||||
break;
|
||||
case EV_CLIENT_DISCONNECT:
|
||||
clear_connection_data();
|
||||
arq_fsm.current = state_no_connected_client;
|
||||
break;
|
||||
case EV_LINK_ESTABLISHED:
|
||||
tnc_send_connected();
|
||||
arq_fsm.current = state_link_connected;
|
||||
break;
|
||||
default:
|
||||
printf("Event: %d ignored from state_idle\n", event);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void state_connecting_callee(int event)
|
||||
{
|
||||
printf("FSM State: connecting_callee\n");
|
||||
|
||||
switch(event)
|
||||
{
|
||||
break;
|
||||
case EV_STOP_LISTEN:
|
||||
arq_conn.listen = false;
|
||||
break;
|
||||
case EV_LINK_CALL_REMOTE:
|
||||
printf("EV_LINK_CALL_REMOTE ignored in state_connecting_caller() - already connecting.\n");
|
||||
break;
|
||||
case EV_LINK_DISCONNECT:
|
||||
// TODO: kill the connection first? Do we need to do something?
|
||||
arq_fsm.current = (arq_conn.listen == true)? state_idle : state_listen;
|
||||
break;
|
||||
case EV_CLIENT_DISCONNECT:
|
||||
clear_connection_data();
|
||||
arq_fsm.current = state_no_connected_client;
|
||||
break;
|
||||
case EV_LINK_ESTABLISHMENT_TIMEOUT:
|
||||
// TODO: do some house cleeping here?
|
||||
arq_fsm.current = (arq_conn.listen == true)? state_idle : state_listen;
|
||||
break;
|
||||
case EV_LINK_ESTABLISHED:
|
||||
// TODO: do some house cleeping here?
|
||||
tnc_send_connected();
|
||||
arq_fsm.current = state_link_connected;
|
||||
break;
|
||||
default:
|
||||
printf("Event: %d ignored from state_idle\n", event);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void tnc_send_connected()
|
||||
{
|
||||
char buffer[128];
|
||||
sprintf(buffer, "CONNECTED %s %s %d\r", arq_conn.my_call_sign, arq_conn.dst_addr, 2300);
|
||||
ssize_t i = tcp_write(CTL_TCP_PORT, (uint8_t *)buffer, strlen(buffer));
|
||||
if (i < 0)
|
||||
printf("Error sending connected message: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
void tnc_send_disconnected()
|
||||
{
|
||||
char buffer[128];
|
||||
sprintf(buffer, "DISCONNECTED\r");
|
||||
ssize_t i = tcp_write(CTL_TCP_PORT, (uint8_t *)buffer, strlen(buffer));
|
||||
if (i < 0)
|
||||
printf("Error sending disconnected message: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
bool check_crc(uint8_t *data)
|
||||
{
|
||||
// TODO: put the correct frame size here
|
||||
int frame_size = 0;
|
||||
|
||||
uint16_t crc = (uint16_t) (data[0] & 0x3f);
|
||||
uint16_t calculated_crc = crc6_0X6F(1, data + HEADER_SIZE, frame_size - HEADER_SIZE);
|
||||
|
||||
if (crc == calculated_crc)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
int check_for_incoming_connection(uint8_t *data)
|
||||
{
|
||||
char callsigns[CALLSIGN_MAX_SIZE * 2];
|
||||
char dst_callsign[CALLSIGN_MAX_SIZE] = { 0 };
|
||||
char src_callsign[CALLSIGN_MAX_SIZE] = { 0 };
|
||||
|
||||
// TODO: put the correct frame size here
|
||||
int frame_size = 0;
|
||||
|
||||
uint8_t pack_type = (data[0] >> 6) & 0xff;
|
||||
|
||||
if (check_crc(data) == false)
|
||||
{
|
||||
printf("Bad crc for packet type %u.\n", pack_type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pack_type != PACKET_ARQ_CONTROL)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (arithmetic_decode(data + HEADER_SIZE, frame_size - HEADER_SIZE, callsigns) < 0)
|
||||
{
|
||||
printf("Truncated callsigns.\n");
|
||||
}
|
||||
printf("Decoded callsigns: %s\n", callsigns);
|
||||
|
||||
char *needle;
|
||||
if ( (needle = strstr(callsigns,"|")) )
|
||||
{
|
||||
int i = 0;
|
||||
while (callsigns[i] != '|')
|
||||
{
|
||||
dst_callsign[i] = callsigns[i];
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
dst_callsign[i] = 0;
|
||||
|
||||
i = 0;
|
||||
needle++;
|
||||
while (callsigns[i] != 0)
|
||||
{
|
||||
src_callsign[i] = needle[i];
|
||||
i++;
|
||||
}
|
||||
src_callsign[i] = 0;
|
||||
}
|
||||
else // corner case where only the destination address fits in the frame
|
||||
{
|
||||
strcpy(dst_callsign, callsigns);
|
||||
}
|
||||
|
||||
if (!strncmp(dst_callsign, arq_conn.my_call_sign, strlen(dst_callsign)))
|
||||
{
|
||||
fsm_dispatch(&arq_fsm, EV_LINK_INCOMING_CALL);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: or we just wait for timeout, or drop a EV_LINK_ESTABLISHMENT_FAILURE
|
||||
fsm_dispatch(&arq_fsm, EV_LINK_ESTABLISHMENT_TIMEOUT);
|
||||
printf("Called call %s sign does not match my callsign %s. Doing nothing.\n", dst_callsign, arq_conn.my_call_sign);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_for_connection_acceptance_caller(uint8_t *data)
|
||||
{
|
||||
char callsign[CALLSIGN_MAX_SIZE];
|
||||
|
||||
// TODO: put the correct frame size here
|
||||
int frame_size = 0;
|
||||
|
||||
uint8_t pack_type = (data[0] >> 6) & 0xff;
|
||||
|
||||
if (check_crc(data) == false)
|
||||
{
|
||||
printf("Bad crc for packet type %u.\n", pack_type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pack_type != PACKET_ARQ_CONTROL)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (arithmetic_decode(data + HEADER_SIZE, frame_size - HEADER_SIZE, callsign) < 0)
|
||||
{
|
||||
printf("Truncated callsigns.\n");
|
||||
}
|
||||
printf("Decoded callsign: %s\n", callsign);
|
||||
|
||||
if (!strncmp(callsign, arq_conn.dst_addr, strlen(callsign)))
|
||||
{
|
||||
fsm_dispatch(&arq_fsm, EV_LINK_ESTABLISHED);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO:
|
||||
// fsm_dispatch(&arq_fsm, EV_LINK_ESTABLISHED_FAILURE);
|
||||
printf("Responded callsign %s does not match called %s. Doing nothing.\n", callsign, arq_conn.dst_addr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void callee_accept_connection()
|
||||
{
|
||||
// here we just send the callee callsign back
|
||||
uint8_t data[INT_BUFFER_SIZE];
|
||||
char callsign[CALLSIGN_MAX_SIZE];
|
||||
uint8_t encoded_callsign[CALLSIGN_MAX_SIZE];
|
||||
|
||||
// TODO: put the correct frame size here
|
||||
int frame_size = 0;
|
||||
|
||||
memset(data, 0, frame_size);
|
||||
// 1 byte header, 4 bits packet type, 6 bits crc
|
||||
data[0] = (PACKET_ARQ_CONTROL << 6) & 0xff; // set packet type
|
||||
|
||||
// encode the callsign
|
||||
sprintf(callsign, "%s", arq_conn.my_call_sign);
|
||||
int enc_len = arithmetic_encode(callsign, encoded_callsign);
|
||||
|
||||
if (enc_len > frame_size - HEADER_SIZE)
|
||||
{
|
||||
printf("Trucating callsigns. This is ok (%d bytes out of %d transmitted)\n", frame_size - HEADER_SIZE, enc_len);
|
||||
enc_len = frame_size - HEADER_SIZE;
|
||||
}
|
||||
|
||||
memcpy(data + HEADER_SIZE, encoded_callsign, enc_len);
|
||||
|
||||
data[0] |= (uint8_t) crc6_0X6F(1, data + HEADER_SIZE, frame_size - HEADER_SIZE);
|
||||
|
||||
write_buffer(data_tx_buffer, data, frame_size);
|
||||
|
||||
}
|
||||
|
||||
void call_remote()
|
||||
{
|
||||
uint8_t data[INT_BUFFER_SIZE];
|
||||
char joint_callsigns[CALLSIGN_MAX_SIZE * 2];
|
||||
uint8_t encoded_callsigns[INT_BUFFER_SIZE];
|
||||
|
||||
// TODO: put the correct frame size here
|
||||
int frame_size = 0;
|
||||
|
||||
printf("Calling remote %s, frame_size: %d\n", arq_conn.dst_addr, frame_size);
|
||||
|
||||
memset(data, 0, frame_size);
|
||||
// 1 byte header, 4 bits packet type, 6 bits crc
|
||||
data[0] = (PACKET_ARQ_CONTROL << 6) & 0xff; // set packet type
|
||||
|
||||
// encode the destination callsign first, then the source, separated by "|"
|
||||
sprintf(joint_callsigns,"%s|%s", arq_conn.dst_addr, arq_conn.src_addr);
|
||||
printf("Joint callsigns: %s\n", joint_callsigns);
|
||||
|
||||
int enc_len = arithmetic_encode(joint_callsigns, encoded_callsigns);
|
||||
|
||||
printf("Encoded callsigns: %s, length: %d\n", joint_callsigns, enc_len);
|
||||
|
||||
if (enc_len > frame_size - HEADER_SIZE)
|
||||
{
|
||||
printf("Trucating joint destination + source callsigns. This is ok (%d bytes out of %d transmitted)\n", frame_size - HEADER_SIZE, enc_len);
|
||||
enc_len = frame_size - HEADER_SIZE;
|
||||
}
|
||||
memcpy(data + HEADER_SIZE, encoded_callsigns, enc_len);
|
||||
|
||||
data[0] |= (uint8_t) crc6_0X6F(1, (uint8_t *)data + HEADER_SIZE, frame_size - HEADER_SIZE);
|
||||
|
||||
write_buffer(data_tx_buffer, data, frame_size);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---- END OF FSM Definitions ---- */
|
||||
|
||||
|
||||
void clear_connection_data()
|
||||
{
|
||||
clear_buffer(data_tx_buffer);
|
||||
clear_buffer(data_rx_buffer);
|
||||
reset_arq_info(&arq_conn);
|
||||
}
|
||||
|
||||
void reset_arq_info(arq_info *arq_conn_i)
|
||||
{
|
||||
arq_conn_i->TRX = RX;
|
||||
arq_conn_i->bw = 0; // 0 = auto
|
||||
arq_conn_i->encryption = false;
|
||||
arq_conn_i->listen = false;
|
||||
arq_conn_i->my_call_sign[0] = 0;
|
||||
arq_conn_i->src_addr[0] = 0;
|
||||
arq_conn_i->dst_addr[0] = 0;
|
||||
}
|
||||
|
||||
|
||||
int arq_init(int tcp_base_port, int initial_mode)
|
||||
{
|
||||
status_ctl = NET_NONE;
|
||||
status_data = NET_NONE;
|
||||
|
||||
arq_conn.call_burst_size = CALL_BURST_SIZE;
|
||||
|
||||
uint8_t *buffer_tx = (uint8_t *) malloc(DATA_TX_BUFFER_SIZE);
|
||||
uint8_t *buffer_rx = (uint8_t *) malloc(DATA_RX_BUFFER_SIZE);
|
||||
data_tx_buffer = circular_buf_init(buffer_tx, DATA_TX_BUFFER_SIZE);
|
||||
data_rx_buffer = circular_buf_init(buffer_rx, DATA_RX_BUFFER_SIZE);
|
||||
|
||||
// TODO: init modem here?
|
||||
|
||||
reset_arq_info(&arq_conn);
|
||||
|
||||
init_model(); // the arithmetic encoder init function
|
||||
fsm_init(&arq_fsm, state_no_connected_client);
|
||||
|
||||
// here is the thread that runs the accept(), each per port, and mantains the
|
||||
// state of the connection
|
||||
pthread_create(&tid[0], NULL, server_worker_thread_ctl, (void *) &tcp_base_port);
|
||||
pthread_create(&tid[1], NULL, server_worker_thread_data, (void *) &tcp_base_port);
|
||||
|
||||
// control channel threads
|
||||
pthread_create(&tid[2], NULL, control_worker_thread_rx, (void *) NULL);
|
||||
pthread_create(&tid[3], NULL, control_worker_thread_tx, (void *) NULL);
|
||||
|
||||
// data channel threads
|
||||
pthread_create(&tid[4], NULL, data_worker_thread_tx, (void *) NULL);
|
||||
pthread_create(&tid[5], NULL, data_worker_thread_rx, (void *) NULL);
|
||||
|
||||
// dsp threads
|
||||
pthread_create(&tid[6], NULL, dsp_thread_tx, (void *) NULL);
|
||||
pthread_create(&tid[7], NULL, dsp_thread_rx, (void *) NULL);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void arq_shutdown()
|
||||
{
|
||||
pthread_join(tid[0], NULL);
|
||||
pthread_join(tid[1], NULL);
|
||||
pthread_join(tid[2], NULL);
|
||||
pthread_join(tid[3], NULL);
|
||||
pthread_join(tid[4], NULL);
|
||||
pthread_join(tid[5], NULL);
|
||||
pthread_join(tid[6], NULL);
|
||||
pthread_join(tid[7], NULL);
|
||||
|
||||
free(data_tx_buffer->buffer);
|
||||
free(data_rx_buffer->buffer);
|
||||
}
|
||||
|
||||
char *get_timestamp()
|
||||
{
|
||||
static char buffer[32];
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
struct tm *tm = localtime(&tv.tv_sec);
|
||||
snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d.%03ld\n", tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec / 1000);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void ptt_on()
|
||||
{
|
||||
char buffer[] = "PTT ON\r";
|
||||
arq_conn.TRX = TX;
|
||||
ssize_t i = tcp_write(CTL_TCP_PORT, (uint8_t *)buffer, strlen(buffer));
|
||||
// print timestamp with miliseconds precision
|
||||
#ifdef DEBUG
|
||||
printf("PTT ON %s", get_timestamp());
|
||||
#endif
|
||||
}
|
||||
void ptt_off()
|
||||
{
|
||||
char buffer[] = "PTT OFF\r";
|
||||
arq_conn.TRX = RX;
|
||||
ssize_t i = tcp_write(CTL_TCP_PORT, (uint8_t *)buffer, strlen(buffer));
|
||||
// print timestamp with miliseconds precision
|
||||
#ifdef DEBUG
|
||||
printf("PTT OFF %s", get_timestamp());
|
||||
#endif
|
||||
}
|
||||
|
||||
// tx to tcp socket the received data from the modem
|
||||
void *data_worker_thread_tx(void *conn)
|
||||
{
|
||||
uint8_t *buffer = (uint8_t *) malloc(DATA_TX_BUFFER_SIZE);
|
||||
|
||||
while(!shutdown_)
|
||||
{
|
||||
if (status_data != NET_CONNECTED)
|
||||
{
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(arq_fsm.current == state_link_connected)
|
||||
{
|
||||
size_t n = read_buffer_all(data_rx_buffer, buffer);
|
||||
|
||||
ssize_t i = tcp_write(DATA_TCP_PORT, buffer, n);
|
||||
|
||||
if (i < (ssize_t) n)
|
||||
fprintf(stderr, "Problems in data_worker_thread_tx!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
msleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// rx from tcp socket and send to trasmit by the modem
|
||||
void *data_worker_thread_rx(void *conn)
|
||||
{
|
||||
uint8_t *buffer = (uint8_t *) malloc(TCP_BLOCK_SIZE);
|
||||
|
||||
while(!shutdown_)
|
||||
{
|
||||
if (status_data != NET_CONNECTED)
|
||||
{
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if(arq_fsm.current == state_link_connected)
|
||||
{
|
||||
int n = tcp_read(DATA_TCP_PORT, buffer, TCP_BLOCK_SIZE);
|
||||
|
||||
write_buffer(data_tx_buffer, buffer, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *control_worker_thread_tx(void *conn)
|
||||
{
|
||||
int counter = 0;
|
||||
char imalive[] = "IAMALIVE\r";
|
||||
|
||||
while(!shutdown_)
|
||||
{
|
||||
if (status_ctl != NET_CONNECTED)
|
||||
{
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (counter == 60)
|
||||
{
|
||||
counter = 0;
|
||||
ssize_t i = tcp_write(CTL_TCP_PORT, (uint8_t *)imalive, strlen(imalive));
|
||||
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
counter++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *control_worker_thread_rx(void *conn)
|
||||
{
|
||||
char *buffer = (char *) malloc(TCP_BLOCK_SIZE+1);
|
||||
char temp[16];
|
||||
int count = 0;
|
||||
|
||||
memset(buffer, 0, TCP_BLOCK_SIZE+1);
|
||||
|
||||
while(!shutdown_)
|
||||
{
|
||||
if (status_ctl != NET_CONNECTED)
|
||||
{
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
int n = tcp_read(CTL_TCP_PORT, (uint8_t *)buffer + count, 1);
|
||||
|
||||
if (n < 0)
|
||||
{
|
||||
count = 0;
|
||||
fprintf(stderr, "ERROR ctl socket reading\n");
|
||||
status_ctl = NET_RESTART;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buffer[count] != '\r')
|
||||
{
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// we found the '\r'
|
||||
buffer[count] = 0;
|
||||
|
||||
if (count >= TCP_BLOCK_SIZE)
|
||||
{
|
||||
count = 0;
|
||||
fprintf(stderr, "ERROR in command parsing\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"Command received: %s\n", buffer);
|
||||
#endif
|
||||
|
||||
// now we parse the commands
|
||||
if (!memcmp(buffer, "MYCALL", strlen("MYCALL")))
|
||||
{
|
||||
sscanf(buffer,"MYCALL %s", arq_conn.my_call_sign);
|
||||
goto send_ok;
|
||||
}
|
||||
|
||||
if (!memcmp(buffer, "LISTEN", strlen("LISTEN")))
|
||||
{
|
||||
sscanf(buffer,"LISTEN %s", temp);
|
||||
if (temp[1] == 'N') // ON
|
||||
{
|
||||
fsm_dispatch(&arq_fsm, EV_START_LISTEN);
|
||||
}
|
||||
if (temp[1] == 'F') // OFF
|
||||
{
|
||||
fsm_dispatch(&arq_fsm, EV_STOP_LISTEN);
|
||||
}
|
||||
|
||||
goto send_ok;
|
||||
}
|
||||
|
||||
if (!memcmp(buffer, "PUBLIC", strlen("PUBLIC")))
|
||||
{
|
||||
sscanf(buffer,"PUBLIC %s", temp);
|
||||
if (temp[1] == 'N') // ON
|
||||
arq_conn.encryption = false;
|
||||
if (temp[1] == 'F') // OFF
|
||||
arq_conn.encryption = true;
|
||||
|
||||
goto send_ok;
|
||||
}
|
||||
|
||||
if (!memcmp(buffer, "BW", strlen("BW")))
|
||||
{
|
||||
sscanf(buffer,"BW%d", &arq_conn.bw);
|
||||
goto send_ok;
|
||||
}
|
||||
|
||||
if (!memcmp(buffer, "CONNECT", strlen("CONNECT")))
|
||||
{
|
||||
sscanf(buffer,"CONNECT %s %s", arq_conn.src_addr, arq_conn.dst_addr);
|
||||
fsm_dispatch(&arq_fsm, EV_LINK_CALL_REMOTE);
|
||||
goto send_ok;
|
||||
}
|
||||
|
||||
if (!memcmp(buffer, "DISCONNECT", strlen("DISCONNECT")))
|
||||
{
|
||||
fsm_dispatch(&arq_fsm, EV_LINK_DISCONNECT);
|
||||
goto send_ok;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Unknown command\n");
|
||||
tcp_write(CTL_TCP_PORT, (uint8_t *) "WRONG\r", 6);
|
||||
continue;
|
||||
|
||||
send_ok:
|
||||
tcp_write(CTL_TCP_PORT, (uint8_t *) "OK\r", 3);
|
||||
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *server_worker_thread_ctl(void *port)
|
||||
{
|
||||
int tcp_base_port = *((int *) port);
|
||||
int socket;
|
||||
|
||||
while(!shutdown_)
|
||||
{
|
||||
int ret = tcp_open(tcp_base_port, CTL_TCP_PORT);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stderr, "Could not open TCP port %d\n", tcp_base_port);
|
||||
shutdown_ = true;
|
||||
}
|
||||
|
||||
socket = listen4connection(CTL_TCP_PORT);
|
||||
|
||||
if (socket < 0)
|
||||
{
|
||||
status_ctl = NET_RESTART;
|
||||
tcp_close(CTL_TCP_PORT);
|
||||
continue;
|
||||
}
|
||||
|
||||
fsm_dispatch(&arq_fsm, EV_CLIENT_CONNECT);
|
||||
|
||||
// TODO: pthread wait here?
|
||||
while (status_ctl == NET_CONNECTED)
|
||||
sleep(1);
|
||||
|
||||
// inform the data thread
|
||||
if (status_data == NET_CONNECTED)
|
||||
status_data = NET_RESTART;
|
||||
|
||||
fsm_dispatch(&arq_fsm, EV_CLIENT_DISCONNECT);
|
||||
|
||||
tcp_close(CTL_TCP_PORT);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
void *server_worker_thread_data(void *port)
|
||||
{
|
||||
int tcp_base_port = *((int *) port);
|
||||
int socket;
|
||||
|
||||
while(!shutdown_)
|
||||
{
|
||||
int ret = tcp_open(tcp_base_port+1, DATA_TCP_PORT);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stderr, "Could not open TCP port %d\n", tcp_base_port+1);
|
||||
shutdown_ = true;
|
||||
}
|
||||
|
||||
socket = listen4connection(DATA_TCP_PORT);
|
||||
|
||||
if (socket < 0)
|
||||
{
|
||||
status_data = NET_RESTART;
|
||||
tcp_close(DATA_TCP_PORT);
|
||||
continue;
|
||||
}
|
||||
|
||||
// pthread wait here?
|
||||
while (status_data == NET_CONNECTED)
|
||||
sleep(1);
|
||||
|
||||
tcp_close(DATA_TCP_PORT);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *dsp_thread_tx(void *conn)
|
||||
{
|
||||
static uint32_t spinner_anim = 0; char spinner[] = ".oOo";
|
||||
// TODO: put the correct frame size here
|
||||
int frame_size = 0;
|
||||
uint8_t data[INT_BUFFER_SIZE];
|
||||
|
||||
// TODO: may be we need another function to queue the already prepared packets?
|
||||
while(!shutdown_)
|
||||
{
|
||||
// should we add a header already here, to know the size of each package? (size need to match frame_size
|
||||
if ((int) size_buffer(data_tx_buffer) < frame_size ||
|
||||
arq_fsm.current == state_idle ||
|
||||
arq_fsm.current == state_no_connected_client)
|
||||
{
|
||||
msleep(50);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if(arq_fsm.current != state_idle && arq_fsm.current != state_listen)
|
||||
{
|
||||
read_buffer(data_tx_buffer, data, frame_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
msleep(50);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < frame_size; i++)
|
||||
{
|
||||
printf("%02x ", data[i]);
|
||||
}
|
||||
|
||||
|
||||
ptt_on();
|
||||
msleep(10); // TODO: tune me!
|
||||
|
||||
// our connection request
|
||||
if (arq_fsm.current == state_connecting_caller || arq_fsm.current == state_connecting_callee)
|
||||
{
|
||||
// tx_transfer(...);
|
||||
}
|
||||
|
||||
if (arq_fsm.current == state_link_connected)
|
||||
{
|
||||
// here we have the data to transmit, so we call the tx_transfer function
|
||||
|
||||
//tx_transfer(); }
|
||||
}
|
||||
|
||||
// TODO: signal when stream is finished playing via pthread_cond_wait() here
|
||||
while (size_buffer(playback_buffer) != 0)
|
||||
{
|
||||
printf("%c\033[1D", spinner[spinner_anim % 4]); spinner_anim++;
|
||||
fflush(stdout);
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
msleep(40); // TODO: parametrize-me!
|
||||
ptt_off();
|
||||
|
||||
printf("%c\033[1D", spinner[spinner_anim % 4]); spinner_anim++;
|
||||
fflush(stdout);
|
||||
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *dsp_thread_rx(void *conn)
|
||||
{
|
||||
static uint32_t spinner_anim = 0; char spinner[] = ".oOo";
|
||||
|
||||
while(!shutdown_)
|
||||
{
|
||||
msleep(50);
|
||||
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
110
datalink/arq.h
Normal file
110
datalink/arq.h
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/* 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 DEFAULT_ARQ_PORT 8300
|
||||
#define DEFAULT_BROADCAST_PORT 8100
|
||||
|
||||
#define TCP_BLOCK_SIZE 128
|
||||
#define CALLSIGN_MAX_SIZE 16
|
||||
|
||||
#define DATA_TX_BUFFER_SIZE 8192
|
||||
#define DATA_RX_BUFFER_SIZE 8192
|
||||
|
||||
#define RX 0
|
||||
#define TX 1
|
||||
|
||||
#define HEADER_SIZE 1
|
||||
|
||||
#define PACKET_ARQ_CONTROL 0x00
|
||||
#define PACKET_ARQ_DATA 0x01
|
||||
#define PACKET_BROADCAST_CONTROL 0x02
|
||||
#define PACKET_BROADCAST_PAYLOAD 0x03
|
||||
|
||||
#define CALL_BURST_SIZE 3 // 3 frames
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int TRX; // RX (0) or TX (1)
|
||||
char my_call_sign[CALLSIGN_MAX_SIZE];
|
||||
char src_addr[CALLSIGN_MAX_SIZE], dst_addr[CALLSIGN_MAX_SIZE];
|
||||
bool encryption;
|
||||
bool call_burst_size;
|
||||
bool listen;
|
||||
int bw; // in Hz
|
||||
} arq_info;
|
||||
|
||||
|
||||
// frame sizes, no CRC enabled, modes 0 to 16.
|
||||
// extern uint32_t mercury_frame_size[];
|
||||
|
||||
// FSM states
|
||||
void state_listen(int event);
|
||||
void state_idle(int event);
|
||||
void state_connecting_caller(int event);
|
||||
void state_connecting_callee(int event);
|
||||
|
||||
// ARQ core functions
|
||||
int arq_init(int tcp_base_port, int initial_mode);
|
||||
void arq_shutdown();
|
||||
|
||||
void print_arq_stats();
|
||||
|
||||
// TCP/IP server threads
|
||||
void *server_worker_thread_ctl(void *port);
|
||||
void *server_worker_thread_data(void *port);
|
||||
void *data_worker_thread_tx(void *conn);
|
||||
void *data_worker_thread_rx(void *conn);
|
||||
void *control_worker_thread_tx(void *conn);
|
||||
void *control_worker_thread_rx(void *conn);
|
||||
|
||||
// DSP threads
|
||||
void *dsp_thread_tx(void *conn);
|
||||
void *dsp_thread_rx(void *conn);
|
||||
|
||||
// auxiliary functions
|
||||
void clear_connection_data();
|
||||
void reset_arq_info(arq_info *arq_conn);
|
||||
void call_remote();
|
||||
void callee_accept_connection();
|
||||
int check_for_incoming_connection(uint8_t *data);
|
||||
int check_for_connection_acceptance_caller(uint8_t *data);
|
||||
char *get_timestamp();
|
||||
|
||||
// TNC / radio functions
|
||||
void ptt_on();
|
||||
void ptt_off();
|
||||
void tnc_send_connected();
|
||||
void tnc_send_disconnected();
|
||||
|
||||
// file crc6.cc
|
||||
uint16_t crc6_0X6F(uint16_t crc, const uint8_t *data, int data_len);
|
||||
|
||||
// from arith.cc
|
||||
void init_model();
|
||||
int arithmetic_encode(const char* msg, uint8_t* output);
|
||||
int arithmetic_decode(uint8_t* input, int max_len, char* output);
|
||||
|
||||
#endif
|
||||
73
datalink/crc6.c
Normal file
73
datalink/crc6.c
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Functions and types for CRC checks.
|
||||
*
|
||||
* Generated on Wed Jan 2 2019,
|
||||
* by pycrc v0.9.1, http://www.tty1.net/pycrc/
|
||||
* using the configuration:
|
||||
* Width = 6
|
||||
* Poly = 0x6f
|
||||
* XorIn = 0
|
||||
* ReflectIn = False
|
||||
* XorOut = 0
|
||||
* ReflectOut = False
|
||||
*/
|
||||
static const uint8_t crc6_table[256] = {
|
||||
0x00, 0x2f, 0x31, 0x1e, 0x0d, 0x22, 0x3c, 0x13, 0x1a, 0x35, 0x2b, 0x04, 0x17, 0x38, 0x26, 0x09,
|
||||
0x34, 0x1b, 0x05, 0x2a, 0x39, 0x16, 0x08, 0x27, 0x2e, 0x01, 0x1f, 0x30, 0x23, 0x0c, 0x12, 0x3d,
|
||||
0x07, 0x28, 0x36, 0x19, 0x0a, 0x25, 0x3b, 0x14, 0x1d, 0x32, 0x2c, 0x03, 0x10, 0x3f, 0x21, 0x0e,
|
||||
0x33, 0x1c, 0x02, 0x2d, 0x3e, 0x11, 0x0f, 0x20, 0x29, 0x06, 0x18, 0x37, 0x24, 0x0b, 0x15, 0x3a,
|
||||
0x0e, 0x21, 0x3f, 0x10, 0x03, 0x2c, 0x32, 0x1d, 0x14, 0x3b, 0x25, 0x0a, 0x19, 0x36, 0x28, 0x07,
|
||||
0x3a, 0x15, 0x0b, 0x24, 0x37, 0x18, 0x06, 0x29, 0x20, 0x0f, 0x11, 0x3e, 0x2d, 0x02, 0x1c, 0x33,
|
||||
0x09, 0x26, 0x38, 0x17, 0x04, 0x2b, 0x35, 0x1a, 0x13, 0x3c, 0x22, 0x0d, 0x1e, 0x31, 0x2f, 0x00,
|
||||
0x3d, 0x12, 0x0c, 0x23, 0x30, 0x1f, 0x01, 0x2e, 0x27, 0x08, 0x16, 0x39, 0x2a, 0x05, 0x1b, 0x34,
|
||||
0x1c, 0x33, 0x2d, 0x02, 0x11, 0x3e, 0x20, 0x0f, 0x06, 0x29, 0x37, 0x18, 0x0b, 0x24, 0x3a, 0x15,
|
||||
0x28, 0x07, 0x19, 0x36, 0x25, 0x0a, 0x14, 0x3b, 0x32, 0x1d, 0x03, 0x2c, 0x3f, 0x10, 0x0e, 0x21,
|
||||
0x1b, 0x34, 0x2a, 0x05, 0x16, 0x39, 0x27, 0x08, 0x01, 0x2e, 0x30, 0x1f, 0x0c, 0x23, 0x3d, 0x12,
|
||||
0x2f, 0x00, 0x1e, 0x31, 0x22, 0x0d, 0x13, 0x3c, 0x35, 0x1a, 0x04, 0x2b, 0x38, 0x17, 0x09, 0x26,
|
||||
0x12, 0x3d, 0x23, 0x0c, 0x1f, 0x30, 0x2e, 0x01, 0x08, 0x27, 0x39, 0x16, 0x05, 0x2a, 0x34, 0x1b,
|
||||
0x26, 0x09, 0x17, 0x38, 0x2b, 0x04, 0x1a, 0x35, 0x3c, 0x13, 0x0d, 0x22, 0x31, 0x1e, 0x00, 0x2f,
|
||||
0x15, 0x3a, 0x24, 0x0b, 0x18, 0x37, 0x29, 0x06, 0x0f, 0x20, 0x3e, 0x11, 0x02, 0x2d, 0x33, 0x1c,
|
||||
0x21, 0x0e, 0x10, 0x3f, 0x2c, 0x03, 0x1d, 0x32, 0x3b, 0x14, 0x0a, 0x25, 0x36, 0x19, 0x07, 0x28
|
||||
};
|
||||
|
||||
/**
|
||||
* CRC6 is used by 3GPP (TS 25.415, TS 25.446) for header CRCs
|
||||
* Poly: D^6 + D^5 + D^3 + D^2 + D^1 + 1
|
||||
*
|
||||
* TS 25.415 docs: https://www.etsi.org/deliver/etsi_ts/125400_125499/125415/04.06.00_60/ts_125415v040600p.pdf
|
||||
* TS 25.446 docs: https://www.etsi.org/deliver/etsi_ts/125400_125499/125446/10.01.00_60/ts_125446v100100p.pdf
|
||||
*/
|
||||
uint16_t crc6_0X6F(uint16_t crc, const uint8_t *data, int data_len)
|
||||
{
|
||||
uint8_t tbl_idx;
|
||||
|
||||
while (data_len--)
|
||||
{
|
||||
tbl_idx = (crc << 2) ^ *data;
|
||||
crc = crc6_table[tbl_idx] & 0x3f;
|
||||
data++;
|
||||
}
|
||||
return crc & 0x3f;
|
||||
}
|
||||
26
datalink/defines.h
Normal file
26
datalink/defines.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
#ifndef HAVE_DEFINES_H
|
||||
#define HAVE_DEFINES_H
|
||||
|
||||
#define MAX_PATH 255
|
||||
|
||||
// {TX,RX}_SHM broadcast memory interface
|
||||
#define SHM_PAYLOAD_BUFFER_SIZE 131072
|
||||
#define SHM_PAYLOAD_NAME "/broadcast"
|
||||
|
||||
#define INT_BUFFER_SIZE 4096
|
||||
|
||||
// audio buffers shared memory interface
|
||||
// 1536000 * 8
|
||||
#define SIGNAL_BUFFER_SIZE 12288000
|
||||
#define SIGNAL_INPUT "/signal-radio2modem"
|
||||
#define SIGNAL_OUTPUT "/signal-modem2radio"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define msleep(a) Sleep(a)
|
||||
#else
|
||||
#define msleep(a) usleep(a * 1000)
|
||||
#endif
|
||||
|
||||
|
||||
#endif // HAVE_DEFINES_H
|
||||
76
datalink/fsm.c
Normal file
76
datalink/fsm.c
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "fsm.h"
|
||||
|
||||
const char *fsm_event_names[] = {
|
||||
"EV_CLIENT_CONNECT",
|
||||
"EV_CLIENT_DISCONNECT",
|
||||
"EV_START_LISTEN",
|
||||
"EV_STOP_LISTEN",
|
||||
"EV_LINK_CALL_REMOTE",
|
||||
"EV_LINK_INCOMING_CALL",
|
||||
"EV_LINK_DISCONNECT",
|
||||
"EV_LINK_ESTABLISHMENT_TIMEOUT",
|
||||
"EV_LINK_ESTABLISHED"
|
||||
};
|
||||
|
||||
// Initialize the FSM
|
||||
void fsm_init(fsm_handle* fsm, fsm_state initial_state)
|
||||
{
|
||||
printf("Initializing FSM\n");
|
||||
|
||||
if (!fsm)
|
||||
return;
|
||||
|
||||
fsm->current = initial_state;
|
||||
pthread_mutex_init(&fsm->lock, NULL);
|
||||
}
|
||||
|
||||
// Dispatch an event (thread-safe)
|
||||
void fsm_dispatch(fsm_handle* fsm, int event)
|
||||
{
|
||||
if (!fsm)
|
||||
return;
|
||||
|
||||
printf("Dispatching event %s\n", fsm_event_names[event]);
|
||||
|
||||
pthread_mutex_lock(&fsm->lock);
|
||||
if (fsm->current)
|
||||
fsm->current(event); // Execute current state
|
||||
|
||||
pthread_mutex_unlock(&fsm->lock);
|
||||
}
|
||||
|
||||
// Clean up resources
|
||||
void fsm_destroy(fsm_handle* fsm)
|
||||
{
|
||||
if (!fsm)
|
||||
return;
|
||||
|
||||
printf("Destroying FSM\n");
|
||||
|
||||
pthread_mutex_destroy(&fsm->lock);
|
||||
fsm->current = NULL;
|
||||
}
|
||||
69
datalink/fsm.h
Normal file
69
datalink/fsm.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* 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 FSM_H
|
||||
#define FSM_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/* ---- FSM Definitions ---- */
|
||||
|
||||
/* FSM Events */
|
||||
// TNC TCP client event
|
||||
#define EV_CLIENT_CONNECT 0
|
||||
#define EV_CLIENT_DISCONNECT 1
|
||||
|
||||
#define EV_START_LISTEN 2
|
||||
#define EV_STOP_LISTEN 3
|
||||
|
||||
#define EV_LINK_CALL_REMOTE 4
|
||||
#define EV_LINK_INCOMING_CALL 5
|
||||
#define EV_LINK_DISCONNECT 6
|
||||
|
||||
#define EV_LINK_ESTABLISHMENT_TIMEOUT 7
|
||||
|
||||
#define EV_LINK_ESTABLISHED 8
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const char* fsm_event_names[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// State function pointer type
|
||||
typedef void (*fsm_state)(int event);
|
||||
|
||||
// Thread-safe FSM structure
|
||||
typedef struct {
|
||||
fsm_state current;
|
||||
pthread_mutex_t lock;
|
||||
} fsm_handle;
|
||||
|
||||
// Public API
|
||||
void fsm_init(fsm_handle* fsm, fsm_state initial_state);
|
||||
void fsm_dispatch(fsm_handle* fsm, int event);
|
||||
void fsm_destroy(fsm_handle* fsm);
|
||||
|
||||
#endif // FSM_H
|
||||
|
|
@ -32,10 +32,14 @@
|
|||
|
||||
|
||||
#include "freedv_api.h"
|
||||
#include "arq.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "audioio/audioio.h"
|
||||
|
||||
extern cbuf_handle_t capture_buffer;
|
||||
extern cbuf_handle_t playback_buffer;
|
||||
|
||||
int freedv_modes[] = { FREEDV_MODE_DATAC1,
|
||||
FREEDV_MODE_DATAC3,
|
||||
FREEDV_MODE_DATAC0,
|
||||
|
|
@ -65,8 +69,8 @@ int main(int argc, char *argv[])
|
|||
int cpu_nr = -1;
|
||||
bool list_modes = false;
|
||||
bool list_sndcards = false;
|
||||
int base_tcp_port = 7002; // default ARQ TCP port
|
||||
int broadcast_port = 7004; // default broadcast TCP port
|
||||
int base_tcp_port = DEFAULT_ARQ_PORT; // default ARQ TCP port
|
||||
int broadcast_port = DEFAULT_BROADCAST_PORT; // default broadcast TCP port
|
||||
int audio_system = -1; // default audio system
|
||||
char *input_dev = (char *) malloc(MAX_PATH);
|
||||
char *output_dev = (char *) malloc(MAX_PATH);
|
||||
|
|
@ -88,8 +92,8 @@ int main(int argc, char *argv[])
|
|||
printf(" -i [device] Radio Capture device id (eg: \"plughw:0,0\").\n");
|
||||
printf(" -o [device] Radio Playback device id (eg: \"plughw:0,0\").\n");
|
||||
printf(" -x [sound_system] Sets the sound system or IO API to use: alsa, pulse, dsound, wasapi or shm. Default is alsa on Linux and dsound on Windows.\n");
|
||||
printf(" -p [arq_tcp_base_port] Sets the ARQ TCP base port (control is base_port, data is base_port + 1). Default is 7002.\n");
|
||||
printf(" -b [broadcast_tcp_port] Sets the broadcast TCP port. Default is 7004.\n");
|
||||
printf(" -p [arq_tcp_base_port] Sets the ARQ TCP base port (control is base_port, data is base_port + 1). Default is 8300.\n");
|
||||
printf(" -b [broadcast_tcp_port] Sets the broadcast TCP port. Default is 8100.\n");
|
||||
printf(" -l Lists all modulator/coding modes.\n");
|
||||
printf(" -z Lists all available sound cards.\n");
|
||||
printf(" -v Verbose mode. Prints more information during execution.\n");
|
||||
|
|
@ -297,7 +301,7 @@ int main(int argc, char *argv[])
|
|||
default:
|
||||
printf("Selected audio system not supported. Trying to continue.\n");
|
||||
}
|
||||
|
||||
|
||||
if (list_sndcards)
|
||||
{
|
||||
list_soundcards(audio_system);
|
||||
|
|
@ -307,7 +311,51 @@ int main(int argc, char *argv[])
|
|||
free(output_dev);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
pthread_t radio_capture;
|
||||
pthread_t radio_playback;
|
||||
|
||||
if (audio_system == AUDIO_SUBSYSTEM_SHM)
|
||||
{
|
||||
try_shm_connect1:
|
||||
capture_buffer = circular_buf_connect_shm(SIGNAL_BUFFER_SIZE, SIGNAL_INPUT);
|
||||
if (capture_buffer == NULL)
|
||||
{
|
||||
printf("Shared memory not created. Waiting for the radio daemon\n");
|
||||
sleep(2);
|
||||
goto try_shm_connect1;
|
||||
}
|
||||
|
||||
try_shm_connect2:
|
||||
playback_buffer = circular_buf_connect_shm(SIGNAL_BUFFER_SIZE, SIGNAL_OUTPUT);
|
||||
if (playback_buffer == NULL)
|
||||
{
|
||||
printf("Shared memory not created. Waiting for the radio daemon...\n");
|
||||
sleep(2);
|
||||
goto try_shm_connect2;
|
||||
}
|
||||
printf("Connected to shared memory buffers.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
audioio_init_internal(input_dev, output_dev, audio_system, &radio_capture, &radio_playback);
|
||||
}
|
||||
|
||||
|
||||
arq_init(base_tcp_port, mod_config);
|
||||
|
||||
|
||||
if (audio_system == AUDIO_SUBSYSTEM_SHM)
|
||||
{
|
||||
// test code
|
||||
circular_buf_destroy_shm(capture_buffer, SIGNAL_BUFFER_SIZE, SIGNAL_INPUT);
|
||||
circular_buf_destroy_shm(playback_buffer, SIGNAL_BUFFER_SIZE, SIGNAL_OUTPUT);
|
||||
circular_buf_free_shm(capture_buffer);
|
||||
circular_buf_free_shm(playback_buffer);
|
||||
}
|
||||
else{
|
||||
audioio_deinit(&radio_capture, &radio_playback);
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
|
|
|||
206
datalink/net.c
Normal file
206
datalink/net.c
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/* 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/>.
|
||||
*
|
||||
* Network related functions
|
||||
*
|
||||
*/
|
||||
|
||||
#include "net.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
static int ctl_sockfd, data_sockfd;
|
||||
|
||||
int cli_ctl_sockfd, cli_data_sockfd;
|
||||
atomic_int status_ctl, status_data;
|
||||
|
||||
static pthread_mutex_t read_mutex[2] = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER };
|
||||
static pthread_mutex_t write_mutex[2] = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER };
|
||||
|
||||
|
||||
int listen4connection(int port_type)
|
||||
{
|
||||
socklen_t clilen;
|
||||
int newsockfd = 0;
|
||||
struct sockaddr_in cli_addr;
|
||||
|
||||
clilen = sizeof(cli_addr);
|
||||
|
||||
if (port_type == CTL_TCP_PORT)
|
||||
newsockfd = accept(ctl_sockfd, (struct sockaddr *) &cli_addr, &clilen);
|
||||
|
||||
if (port_type == DATA_TCP_PORT)
|
||||
newsockfd = accept(data_sockfd, (struct sockaddr *) &cli_addr, &clilen);
|
||||
|
||||
if (newsockfd < 0)
|
||||
{
|
||||
fprintf(stderr, "ERROR on accept");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (port_type == CTL_TCP_PORT)
|
||||
{
|
||||
cli_ctl_sockfd = newsockfd;
|
||||
status_ctl = NET_CONNECTED;
|
||||
}
|
||||
|
||||
if (port_type == DATA_TCP_PORT)
|
||||
{
|
||||
cli_data_sockfd = newsockfd;
|
||||
status_data = NET_CONNECTED;
|
||||
}
|
||||
return newsockfd;
|
||||
}
|
||||
|
||||
int tcp_open(int portno, int port_type)
|
||||
{
|
||||
int sockfd;
|
||||
struct sockaddr_in serv_addr;
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0){
|
||||
fprintf(stderr, "ERROR opening socket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int opt = 1;
|
||||
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
|
||||
{
|
||||
fprintf(stderr, "setsockopt(SO_REUSEADDR) failed\n");
|
||||
close(sockfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset((char *) &serv_addr, 0, sizeof(serv_addr));
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
serv_addr.sin_port = htons(portno);
|
||||
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
|
||||
{
|
||||
fprintf(stderr, "ERROR on binding\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
listen(sockfd,1); // just 1 concurrent connections
|
||||
|
||||
if (port_type == CTL_TCP_PORT)
|
||||
{
|
||||
status_ctl = NET_LISTENING;
|
||||
ctl_sockfd = sockfd;
|
||||
}
|
||||
if (port_type == DATA_TCP_PORT)
|
||||
{
|
||||
status_data = NET_LISTENING;
|
||||
data_sockfd = sockfd;
|
||||
}
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
ssize_t tcp_read(int port_type, uint8_t *buffer, size_t rx_size)
|
||||
{
|
||||
ssize_t n = 0;
|
||||
|
||||
pthread_mutex_lock(&read_mutex[port_type]);
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
if (port_type == CTL_TCP_PORT && status_ctl == NET_CONNECTED)
|
||||
{
|
||||
ioctl(cli_ctl_sockfd, FIONREAD, &count);
|
||||
if (count < rx_size)
|
||||
rx_size = count ? count : rx_size;
|
||||
n = recv(cli_ctl_sockfd, buffer, rx_size, MSG_NOSIGNAL);
|
||||
|
||||
if (n < 0) status_ctl = NET_RESTART;
|
||||
}
|
||||
if (port_type == DATA_TCP_PORT && status_data == NET_CONNECTED)
|
||||
{
|
||||
ioctl(cli_data_sockfd, FIONREAD, &count);
|
||||
if (count < rx_size)
|
||||
rx_size = count ? count : rx_size;
|
||||
n = recv(cli_data_sockfd, buffer, rx_size, MSG_NOSIGNAL);
|
||||
|
||||
if (n < 0) status_data = NET_RESTART;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&read_mutex[port_type]);
|
||||
|
||||
if (n < 0)
|
||||
fprintf(stderr, "ERROR reading from socket\n");
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
ssize_t tcp_write(int port_type, uint8_t *buffer, size_t tx_size)
|
||||
{
|
||||
ssize_t n = 0;
|
||||
|
||||
pthread_mutex_lock(&write_mutex[port_type]);
|
||||
|
||||
if (port_type == CTL_TCP_PORT && status_ctl == NET_CONNECTED)
|
||||
{
|
||||
n = send(cli_ctl_sockfd, buffer, tx_size, MSG_NOSIGNAL);
|
||||
|
||||
if (n != (ssize_t) tx_size)
|
||||
status_ctl = NET_RESTART;
|
||||
}
|
||||
|
||||
if (port_type == DATA_TCP_PORT && status_data == NET_CONNECTED)
|
||||
{
|
||||
n = send(cli_data_sockfd, buffer, tx_size, MSG_NOSIGNAL);
|
||||
|
||||
if (n != (ssize_t) tx_size)
|
||||
status_data = NET_RESTART;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&write_mutex[port_type]);
|
||||
|
||||
if (n != (ssize_t) tx_size)
|
||||
fprintf(stderr, "ERROR writing to socket\n");
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int tcp_close(int port_type)
|
||||
{
|
||||
|
||||
if(port_type == CTL_TCP_PORT)
|
||||
{
|
||||
close(cli_ctl_sockfd);
|
||||
close(ctl_sockfd);
|
||||
}
|
||||
if(port_type == DATA_TCP_PORT)
|
||||
{
|
||||
close(cli_data_sockfd);
|
||||
close(data_sockfd);
|
||||
}
|
||||
status_ctl = NET_NONE;
|
||||
status_data = NET_NONE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
64
datalink/net.h
Normal file
64
datalink/net.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* 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/>.
|
||||
*
|
||||
* Network related functions
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file net.h
|
||||
* @author Rafael Diniz
|
||||
* @date 01 Apr 2025
|
||||
* @brief File containing network related functions
|
||||
*
|
||||
* For the sake of reusable and clean code, some network related functions.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NET_H__
|
||||
#define NET_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define CTL_TCP_PORT 0
|
||||
#define DATA_TCP_PORT 1
|
||||
|
||||
#define NET_NONE 0
|
||||
#define NET_LISTENING 1
|
||||
#define NET_RESTART 2
|
||||
#define NET_CONNECTED 3
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
extern int cli_ctl_sockfd, cli_data_sockfd;
|
||||
|
||||
extern atomic_int status_ctl, status_data;
|
||||
|
||||
int listen4connection(int port_type);
|
||||
|
||||
int tcp_open(int portno, int port_type);
|
||||
|
||||
ssize_t tcp_read(int port_type, uint8_t *buffer, size_t rx_size);
|
||||
|
||||
ssize_t tcp_write(int port_type, uint8_t *buffer, size_t tx_size);
|
||||
|
||||
int tcp_close(int port_type);
|
||||
|
||||
#endif // NET_H__
|
||||
Loading…
Add table
Add a link
Reference in a new issue