Skip to content

Commit 2943557

Browse files
authored
Merge pull request drowe67#265 from drowe67/dr-tnc
FreeData TNC support
2 parents bc8b049 + 84b6507 commit 2943557

17 files changed

+713
-299
lines changed

CMakeLists.txt

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,22 @@ if(UNITTEST)
373373
")
374374
set_tests_properties(test_fdmdv_16to8_short PROPERTIES PASS_REGULAR_EXPRESSION "PASS")
375375

376+
# 48<->8 kHz float resamplers
377+
add_test(NAME test_fdmdv_48to8_short
378+
COMMAND sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/octave;
379+
${CMAKE_CURRENT_BINARY_DIR}/unittest/t48_8_short;
380+
DISPLAY=\"\" echo \"diff_fft_mag('in8.raw','out8.raw'); quit;\" | octave-cli -qf
381+
")
382+
set_tests_properties(test_fdmdv_48to8_short PROPERTIES PASS_REGULAR_EXPRESSION "PASS")
383+
384+
# 48<->8 kHz short resamplers
385+
add_test(NAME test_fdmdv_48to8
386+
COMMAND sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/octave;
387+
${CMAKE_CURRENT_BINARY_DIR}/unittest/t48_8;
388+
DISPLAY=\"\" echo \"diff_fft_mag('in8.raw','out8.raw'); quit;\" | octave-cli -qf
389+
")
390+
set_tests_properties(test_fdmdv_48to8 PROPERTIES PASS_REGULAR_EXPRESSION "PASS")
391+
376392
add_test(NAME test_CML_ldpcut
377393
COMMAND sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/octave; SHORT_VERSION_FOR_CTEST=1 octave-cli -qf ldpcut.m")
378394
set_tests_properties(test_CML_ldpcut PROPERTIES PASS_REGULAR_EXPRESSION "Nerr: 0")
@@ -1118,35 +1134,20 @@ endif(NOT APPLE)
11181134
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
11191135
head -c $((14*10)) </dev/urandom > binaryIn.bin;
11201136
./freedv_data_raw_tx DATAC0 binaryIn.bin - --bursts 10 |
1121-
./freedv_data_raw_rx DATAC0 - binaryOut.bin --framesperburst 1 --vv;
1137+
./freedv_data_raw_rx DATAC0 - binaryOut.bin -v;
11221138
diff binaryIn.bin binaryOut.bin")
11231139

11241140
add_test(NAME test_freedv_data_raw_ofdm_datac1_burst_file
11251141
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
11261142
head -c $((510*10)) </dev/urandom > binaryIn.bin;
1127-
./freedv_data_raw_tx DATAC1 binaryIn.bin - --framesperburst 2 --bursts 5 |
1128-
./freedv_data_raw_rx DATAC1 - binaryOut.bin --framesperburst 2 --vv;
1129-
diff binaryIn.bin binaryOut.bin")
1130-
1131-
# Streaming mode tests, with data file I/O
1132-
add_test(NAME test_freedv_data_raw_ofdm_datac0
1133-
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
1134-
head -c $((14*10)) </dev/urandom > binaryIn.bin;
1135-
./freedv_data_raw_tx DATAC0 binaryIn.bin - |
1136-
./freedv_data_raw_rx DATAC0 - binaryOut.bin -v;
1137-
diff binaryIn.bin binaryOut.bin")
1138-
1139-
add_test(NAME test_freedv_data_raw_ofdm_datac1
1140-
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
1141-
head -c $((510*10)) </dev/urandom > binaryIn.bin;
1142-
./freedv_data_raw_tx DATAC1 binaryIn.bin - |
1143+
./freedv_data_raw_tx DATAC1 binaryIn.bin - --bursts 10 |
11431144
./freedv_data_raw_rx DATAC1 - binaryOut.bin -v;
11441145
diff binaryIn.bin binaryOut.bin")
11451146

1146-
add_test(NAME test_freedv_data_raw_ofdm_datac3
1147+
add_test(NAME test_freedv_data_raw_ofdm_datac3_burst_file
11471148
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
11481149
head -c $((126*10)) </dev/urandom > binaryIn.bin;
1149-
./freedv_data_raw_tx DATAC3 binaryIn.bin - |
1150+
./freedv_data_raw_tx DATAC3 binaryIn.bin - --bursts 10 |
11501151
./freedv_data_raw_rx DATAC3 - binaryOut.bin -v;
11511152
diff binaryIn.bin binaryOut.bin")
11521153

@@ -1213,7 +1214,15 @@ endif(NOT APPLE)
12131214
cat binaryIn.bin | ./demo/freedv_datac1_tx |
12141215
./demo/freedv_datac1_rx > binaryOut.bin;
12151216
diff binaryIn.bin binaryOut.bin")
1216-
1217+
1218+
# test Rx of two modes in parallel, with AWGN noise and sample clock offsets
1219+
add_test(NAME test_demo_datac0c1
1220+
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR};
1221+
./demo/freedv_datac0c1_tx |
1222+
./src/cohpsk_ch - - -24 -f 20 --Fs 8000 |
1223+
sox -t .s16 -c 1 -r 8000 - -t .s16 -c 1 -r 8008 - |
1224+
./demo/freedv_datac0c1_rx")
1225+
set_tests_properties(test_demo_datac0c1 PROPERTIES PASS_REGULAR_EXPRESSION "DATAC0 Frames: 10 DATAC1 Frames: 10")
12171226

12181227
# Set common properties for tests that need Octave/CML
12191228
set_tests_properties(

README_freedv.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ The current demo programs are as follows:
3434
| [freedv_700d_rx.py](demo/freedv_700d_rx.py) | Receive a voice signal using the FreeDV API in Python |
3535
| [freedv_datac1_tx.c](demo/freedv_datac1_tx.c) | Transmit raw data frames using the FreeDV API |
3636
| [freedv_datac1_rx.c](demo/freedv_datac1_rx.c) | Receive raw data frames using the FreeDV API |
37+
| [freedv_datac0c1_tx.c](demo/freedv_datac0c1_tx.c) | Transmit two types of raw data frames using the FreeDV API |
38+
| [freedv_datac0c1_rx.c](demo/freedv_datac0c1_rx.c) | Receive two types of raw data frames using the FreeDV API |
3739

3840
So also [freedv_api.h](src/freedv_api.h) and [freedv_api.c](src/freedv_api.c) for the full list of API functions. Only a small set of these functions are needed for basic FreeDV use, please see the demo programs for minimal examples.
3941

demo/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ add_executable(freedv_datac1_tx freedv_datac1_tx.c)
1111
target_link_libraries(freedv_datac1_tx codec2)
1212
add_executable(freedv_datac1_rx freedv_datac1_rx.c)
1313
target_link_libraries(freedv_datac1_rx codec2)
14+
add_executable(freedv_datac0c1_tx freedv_datac0c1_tx.c)
15+
target_link_libraries(freedv_datac0c1_tx codec2)
16+
add_executable(freedv_datac0c1_rx freedv_datac0c1_rx.c)
17+
target_link_libraries(freedv_datac0c1_rx codec2)

demo/freedv_datac0c1_rx.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*---------------------------------------------------------------------------*\
2+
3+
FILE........: freedv_datac0c1_rx.c
4+
AUTHOR......: David Rowe
5+
DATE CREATED: Dec 2021
6+
7+
Demonstrates receiving frames of raw data bytes using the FreeDV
8+
API. Two parallel receivers are running, so we can receive either
9+
DATAC0 or DATAC1 frames. Demonstrates a common use case for HF data
10+
- the ability to receive signalling as well as payload data frames.
11+
12+
usage:
13+
14+
cd codec2/build_linux
15+
./demo/freedv_datacc01_tx | ./demo/freedv_datac0c1_rx
16+
17+
Give it a hard time with some channel noise, frequency offset, and sample
18+
clock offsets:
19+
20+
./demo/freedv_datac0c1_tx | ./src/cohpsk_ch - - -24 -f 20 --Fs 8000 |
21+
sox -t .s16 -c 1 -r 8000 - -t .s16 -c 1 -r 8008 - |
22+
./demo/freedv_datac0c1_rx
23+
24+
Replace the final line with "aplay -f S16" to listen to the
25+
simulated Rx signal.
26+
27+
\*---------------------------------------------------------------------------*/
28+
29+
/*
30+
Copyright (C) 2021 David Rowe
31+
32+
All rights reserved.
33+
34+
This program is free software; you can redistribute it and/or modify
35+
it under the terms of the GNU Lesser General Public License version 2.1, as
36+
published by the Free Software Foundation. This program is
37+
distributed in the hope that it will be useful, but WITHOUT ANY
38+
WARRANTY; without even the implied warranty of MERCHANTABILITY or
39+
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
40+
License for more details.
41+
42+
You should have received a copy of the GNU Lesser General Public License
43+
along with this program; if not, see <http://www.gnu.org/licenses/>.
44+
*/
45+
46+
#include <assert.h>
47+
#include <stdlib.h>
48+
#include <stdio.h>
49+
#include <stdint.h>
50+
#include <string.h>
51+
52+
#include "freedv_api.h"
53+
54+
#define NBUF 160
55+
56+
int run_receiver(struct freedv *freedv, short buf[], short demod_in[], int *pn, uint8_t bytes_out[]);
57+
58+
int main(int argc, char *argv[]) {
59+
60+
// set up DATAC0 Rx
61+
struct freedv *freedv_c0 = freedv_open(FREEDV_MODE_DATAC0);
62+
assert(freedv_c0 != NULL);
63+
freedv_set_frames_per_burst(freedv_c0, 1);
64+
freedv_set_verbose(freedv_c0, 0);
65+
int bytes_per_modem_frame_c0 = freedv_get_bits_per_modem_frame(freedv_c0)/8;
66+
uint8_t bytes_out_c0[bytes_per_modem_frame_c0];
67+
short demod_in_c0[freedv_get_n_max_modem_samples(freedv_c0)];
68+
69+
// set up DATAC1 Rx
70+
struct freedv *freedv_c1 = freedv_open(FREEDV_MODE_DATAC1);
71+
assert(freedv_c1 != NULL);
72+
freedv_set_frames_per_burst(freedv_c1, 1);
73+
freedv_set_verbose(freedv_c1, 0);
74+
int bytes_per_modem_frame_c1 = freedv_get_bits_per_modem_frame(freedv_c1)/8;
75+
uint8_t bytes_out_c1[bytes_per_modem_frame_c1];
76+
short demod_in_c1[freedv_get_n_max_modem_samples(freedv_c1)];
77+
78+
// number of samples in demod_in buffer for each Rx
79+
int n_c0 = 0;
80+
int n_c1 = 0;
81+
// number of frames received in each mode
82+
int c0_frames = 0;
83+
int c1_frames = 0;
84+
85+
short buf[NBUF];
86+
87+
// read a fixed buffer from stdin, use that to fill c0 and c1 demod_in buffers
88+
while(fread(buf, sizeof(short), NBUF, stdin) == NBUF) {
89+
90+
if (run_receiver(freedv_c0, buf, demod_in_c0, &n_c0, bytes_out_c0)) {
91+
fprintf(stderr, "DATAC0 frame received!\n");
92+
c0_frames++;
93+
}
94+
if (run_receiver(freedv_c1, buf, demod_in_c1, &n_c1, bytes_out_c1)) {
95+
fprintf(stderr, "DATAC1 frame received!\n");
96+
c1_frames++;
97+
}
98+
99+
}
100+
101+
fprintf(stderr, "DATAC0 Frames: %d DATAC1 Frames: %d\n", c0_frames, c1_frames);
102+
103+
freedv_close(freedv_c0);
104+
freedv_close(freedv_c1);
105+
106+
return 0;
107+
}
108+
109+
int run_receiver(struct freedv *freedv, short buf[], short demod_in[], int *pn, uint8_t bytes_out[]) {
110+
int n = *pn;
111+
int nbytes_out = 0;
112+
int nin;
113+
114+
// NBUF new samples into DATAC1 Rx
115+
memcpy(&demod_in[n], buf, sizeof(short)*NBUF);
116+
n += NBUF; assert(n <= freedv_get_n_max_modem_samples(freedv));
117+
nin = freedv_nin(freedv);
118+
while (n > nin) {
119+
nbytes_out = freedv_rawdatarx(freedv, bytes_out, demod_in);
120+
// nin samples were read
121+
n -= nin; assert(n >= 0);
122+
memmove(demod_in, &demod_in[nin], sizeof(short)*n);
123+
nin = freedv_nin(freedv);
124+
}
125+
126+
*pn = n;
127+
return nbytes_out;
128+
}

demo/freedv_datac0c1_tx.c

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*---------------------------------------------------------------------------*\
2+
3+
FILE........: freedv_datac0c1_tx.c
4+
AUTHOR......: David Rowe
5+
DATE CREATED: Dec 2021
6+
7+
Transmitting alternate frames of two different raw data modes. See
8+
freedv_datac0c1_rx.c
9+
10+
\*---------------------------------------------------------------------------*/
11+
12+
/*
13+
Copyright (C) 2021 David Rowe
14+
15+
All rights reserved.
16+
17+
This program is free software; you can redistribute it and/or modify
18+
it under the terms of the GNU Lesser General Public License version 2.1, as
19+
published by the Free Software Foundation. This program is
20+
distributed in the hope that it will be useful, but WITHOUT ANY
21+
WARRANTY; without even the implied warranty of MERCHANTABILITY or
22+
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
23+
License for more details.
24+
25+
You should have received a copy of the GNU Lesser General Public License
26+
along with this program; if not, see <http://www.gnu.org/licenses/>.
27+
*/
28+
29+
#include <assert.h>
30+
#include <stdlib.h>
31+
#include <stdio.h>
32+
#include <stdint.h>
33+
34+
#include "freedv_api.h"
35+
#include "ofdm_internal.h"
36+
37+
#define FRAMES 10
38+
39+
void send_burst(struct freedv *freedv);
40+
41+
int main(void) {
42+
struct freedv *freedv_c0, *freedv_c1;
43+
44+
freedv_c0 = freedv_open(FREEDV_MODE_DATAC0); assert(freedv_c0 != NULL);
45+
freedv_c1 = freedv_open(FREEDV_MODE_DATAC1); assert(freedv_c1 != NULL);
46+
47+
// send frames in different modes in random order
48+
int c0_frames = 0;
49+
int c1_frames = 0;
50+
while ((c0_frames < FRAMES) || (c1_frames < FRAMES)) {
51+
if (rand() & 1) {
52+
if (c0_frames < FRAMES) {
53+
send_burst(freedv_c0);
54+
c0_frames++;
55+
}
56+
} else {
57+
if (c1_frames < FRAMES) {
58+
send_burst(freedv_c1);
59+
c1_frames++;
60+
}
61+
}
62+
}
63+
64+
freedv_close(freedv_c0);
65+
freedv_close(freedv_c1);
66+
67+
return 0;
68+
}
69+
70+
71+
void send_burst(struct freedv *freedv) {
72+
size_t bits_per_frame = freedv_get_bits_per_modem_frame(freedv);
73+
size_t bytes_per_modem_frame = bits_per_frame/8;
74+
size_t payload_bytes_per_modem_frame = bytes_per_modem_frame - 2; /* 16 bits used for the CRC */
75+
size_t n_mod_out = freedv_get_n_tx_modem_samples(freedv);
76+
uint8_t bytes_in[bytes_per_modem_frame];
77+
short mod_out_short[n_mod_out];
78+
79+
/* generate a test frame */
80+
uint8_t testframe_bits[bits_per_frame];
81+
ofdm_generate_payload_data_bits(testframe_bits, bits_per_frame);
82+
freedv_pack(bytes_in, testframe_bits, bits_per_frame);
83+
84+
/* send preamble */
85+
int n_preamble = freedv_rawdatapreambletx(freedv, mod_out_short);
86+
fwrite(mod_out_short, sizeof(short), n_preamble, stdout);
87+
88+
/* The raw data modes require a CRC in the last two bytes */
89+
uint16_t crc16 = freedv_gen_crc16(bytes_in, payload_bytes_per_modem_frame);
90+
bytes_in[bytes_per_modem_frame-2] = crc16 >> 8;
91+
bytes_in[bytes_per_modem_frame-1] = crc16 & 0xff;
92+
93+
/* modulate and send a data frame */
94+
freedv_rawdatatx(freedv, mod_out_short, bytes_in);
95+
fwrite(mod_out_short, sizeof(short), n_mod_out, stdout);
96+
97+
/* send postamble */
98+
int n_postamble = freedv_rawdatapostambletx(freedv, mod_out_short);
99+
fwrite(mod_out_short, sizeof(short), n_postamble, stdout);
100+
101+
/* create some silence between bursts */
102+
int inter_burst_delay_ms = 200;
103+
int samples_delay = FREEDV_FS_8000*inter_burst_delay_ms/1000;
104+
short sil_short[samples_delay];
105+
for(int i=0; i<samples_delay; i++) sil_short[i] = 0;
106+
fwrite(sil_short, sizeof(short), samples_delay, stdout);
107+
}

src/codec2_fdmdv.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,18 @@ extern "C" {
6767
#define FDMDV_SCALE 750 /* suggested scaling for 16 bit shorts */
6868
#define FDMDV_FCENTRE 1500 /* Centre frequency, Nc/2 carriers below this, Nc/2 carriers above (Hz) */
6969

70-
/* 8 to 48 kHz sample rate conversion */
70+
/* 8 to 18 kHz sample rate conversion */
7171

7272
#define FDMDV_OS 2 /* oversampling rate */
7373
#define FDMDV_OS_TAPS_16K 48 /* number of OS filter taps at 16kHz */
7474
#define FDMDV_OS_TAPS_8K (FDMDV_OS_TAPS_16K/FDMDV_OS) /* number of OS filter taps at 8kHz */
7575

76+
/* 8 to 48 kHz sample rate conversion */
77+
78+
#define FDMDV_OS_48 6 /* oversampling rate */
79+
#define FDMDV_OS_TAPS_48K 48 /* number of OS filter taps at 48kHz */
80+
#define FDMDV_OS_TAPS_48_8K (FDMDV_OS_TAPS_48K/FDMDV_OS_48) /* number of OS filter taps at 8kHz */
81+
7682
/* FDMDV states and stats structures */
7783

7884
struct FDMDV;
@@ -97,6 +103,10 @@ void fdmdv_8_to_16(float out16k[], float in8k[], int n);
97103
void fdmdv_8_to_16_short(short out16k[], short in8k[], int n);
98104
void fdmdv_16_to_8(float out8k[], float in16k[], int n);
99105
void fdmdv_16_to_8_short(short out8k[], short in16k[], int n);
106+
void fdmdv_8_to_48(float out48k[], float in8k[], int n);
107+
void fdmdv_48_to_8(float out8k[], float in48k[], int n);
108+
void fdmdv_8_to_48_short(short out48k[], short in8k[], int n);
109+
void fdmdv_48_to_8_short(short out8k[], short in48k[], int n);
100110

101111
void fdmdv_freq_shift(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, COMP *foff_phase_rect, int nin);
102112

0 commit comments

Comments
 (0)