Skip to content

Commit

Permalink
Merge pull request #265 from drowe67/dr-tnc
Browse files Browse the repository at this point in the history
FreeData TNC support
  • Loading branch information
drowe67 authored Dec 26, 2021
2 parents bc8b049 + 84b6507 commit 2943557
Show file tree
Hide file tree
Showing 17 changed files with 713 additions and 299 deletions.
49 changes: 29 additions & 20 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,22 @@ if(UNITTEST)
")
set_tests_properties(test_fdmdv_16to8_short PROPERTIES PASS_REGULAR_EXPRESSION "PASS")

# 48<->8 kHz float resamplers
add_test(NAME test_fdmdv_48to8_short
COMMAND sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/octave;
${CMAKE_CURRENT_BINARY_DIR}/unittest/t48_8_short;
DISPLAY=\"\" echo \"diff_fft_mag('in8.raw','out8.raw'); quit;\" | octave-cli -qf
")
set_tests_properties(test_fdmdv_48to8_short PROPERTIES PASS_REGULAR_EXPRESSION "PASS")

# 48<->8 kHz short resamplers
add_test(NAME test_fdmdv_48to8
COMMAND sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/octave;
${CMAKE_CURRENT_BINARY_DIR}/unittest/t48_8;
DISPLAY=\"\" echo \"diff_fft_mag('in8.raw','out8.raw'); quit;\" | octave-cli -qf
")
set_tests_properties(test_fdmdv_48to8 PROPERTIES PASS_REGULAR_EXPRESSION "PASS")

add_test(NAME test_CML_ldpcut
COMMAND sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/octave; SHORT_VERSION_FOR_CTEST=1 octave-cli -qf ldpcut.m")
set_tests_properties(test_CML_ldpcut PROPERTIES PASS_REGULAR_EXPRESSION "Nerr: 0")
Expand Down Expand Up @@ -1118,35 +1134,20 @@ endif(NOT APPLE)
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
head -c $((14*10)) </dev/urandom > binaryIn.bin;
./freedv_data_raw_tx DATAC0 binaryIn.bin - --bursts 10 |
./freedv_data_raw_rx DATAC0 - binaryOut.bin --framesperburst 1 --vv;
./freedv_data_raw_rx DATAC0 - binaryOut.bin -v;
diff binaryIn.bin binaryOut.bin")

add_test(NAME test_freedv_data_raw_ofdm_datac1_burst_file
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
head -c $((510*10)) </dev/urandom > binaryIn.bin;
./freedv_data_raw_tx DATAC1 binaryIn.bin - --framesperburst 2 --bursts 5 |
./freedv_data_raw_rx DATAC1 - binaryOut.bin --framesperburst 2 --vv;
diff binaryIn.bin binaryOut.bin")

# Streaming mode tests, with data file I/O
add_test(NAME test_freedv_data_raw_ofdm_datac0
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
head -c $((14*10)) </dev/urandom > binaryIn.bin;
./freedv_data_raw_tx DATAC0 binaryIn.bin - |
./freedv_data_raw_rx DATAC0 - binaryOut.bin -v;
diff binaryIn.bin binaryOut.bin")

add_test(NAME test_freedv_data_raw_ofdm_datac1
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
head -c $((510*10)) </dev/urandom > binaryIn.bin;
./freedv_data_raw_tx DATAC1 binaryIn.bin - |
./freedv_data_raw_tx DATAC1 binaryIn.bin - --bursts 10 |
./freedv_data_raw_rx DATAC1 - binaryOut.bin -v;
diff binaryIn.bin binaryOut.bin")

add_test(NAME test_freedv_data_raw_ofdm_datac3
add_test(NAME test_freedv_data_raw_ofdm_datac3_burst_file
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
head -c $((126*10)) </dev/urandom > binaryIn.bin;
./freedv_data_raw_tx DATAC3 binaryIn.bin - |
./freedv_data_raw_tx DATAC3 binaryIn.bin - --bursts 10 |
./freedv_data_raw_rx DATAC3 - binaryOut.bin -v;
diff binaryIn.bin binaryOut.bin")

Expand Down Expand Up @@ -1213,7 +1214,15 @@ endif(NOT APPLE)
cat binaryIn.bin | ./demo/freedv_datac1_tx |
./demo/freedv_datac1_rx > binaryOut.bin;
diff binaryIn.bin binaryOut.bin")


# test Rx of two modes in parallel, with AWGN noise and sample clock offsets
add_test(NAME test_demo_datac0c1
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR};
./demo/freedv_datac0c1_tx |
./src/cohpsk_ch - - -24 -f 20 --Fs 8000 |
sox -t .s16 -c 1 -r 8000 - -t .s16 -c 1 -r 8008 - |
./demo/freedv_datac0c1_rx")
set_tests_properties(test_demo_datac0c1 PROPERTIES PASS_REGULAR_EXPRESSION "DATAC0 Frames: 10 DATAC1 Frames: 10")

# Set common properties for tests that need Octave/CML
set_tests_properties(
Expand Down
2 changes: 2 additions & 0 deletions README_freedv.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ The current demo programs are as follows:
| [freedv_700d_rx.py](demo/freedv_700d_rx.py) | Receive a voice signal using the FreeDV API in Python |
| [freedv_datac1_tx.c](demo/freedv_datac1_tx.c) | Transmit raw data frames using the FreeDV API |
| [freedv_datac1_rx.c](demo/freedv_datac1_rx.c) | Receive raw data frames using the FreeDV API |
| [freedv_datac0c1_tx.c](demo/freedv_datac0c1_tx.c) | Transmit two types of raw data frames using the FreeDV API |
| [freedv_datac0c1_rx.c](demo/freedv_datac0c1_rx.c) | Receive two types of raw data frames using the FreeDV API |

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.

Expand Down
4 changes: 4 additions & 0 deletions demo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ add_executable(freedv_datac1_tx freedv_datac1_tx.c)
target_link_libraries(freedv_datac1_tx codec2)
add_executable(freedv_datac1_rx freedv_datac1_rx.c)
target_link_libraries(freedv_datac1_rx codec2)
add_executable(freedv_datac0c1_tx freedv_datac0c1_tx.c)
target_link_libraries(freedv_datac0c1_tx codec2)
add_executable(freedv_datac0c1_rx freedv_datac0c1_rx.c)
target_link_libraries(freedv_datac0c1_rx codec2)
128 changes: 128 additions & 0 deletions demo/freedv_datac0c1_rx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*---------------------------------------------------------------------------*\
FILE........: freedv_datac0c1_rx.c
AUTHOR......: David Rowe
DATE CREATED: Dec 2021
Demonstrates receiving frames of raw data bytes using the FreeDV
API. Two parallel receivers are running, so we can receive either
DATAC0 or DATAC1 frames. Demonstrates a common use case for HF data
- the ability to receive signalling as well as payload data frames.
usage:
cd codec2/build_linux
./demo/freedv_datacc01_tx | ./demo/freedv_datac0c1_rx
Give it a hard time with some channel noise, frequency offset, and sample
clock offsets:
./demo/freedv_datac0c1_tx | ./src/cohpsk_ch - - -24 -f 20 --Fs 8000 |
sox -t .s16 -c 1 -r 8000 - -t .s16 -c 1 -r 8008 - |
./demo/freedv_datac0c1_rx
Replace the final line with "aplay -f S16" to listen to the
simulated Rx signal.
\*---------------------------------------------------------------------------*/

/*
Copyright (C) 2021 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. 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 General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#include "freedv_api.h"

#define NBUF 160

int run_receiver(struct freedv *freedv, short buf[], short demod_in[], int *pn, uint8_t bytes_out[]);

int main(int argc, char *argv[]) {

// set up DATAC0 Rx
struct freedv *freedv_c0 = freedv_open(FREEDV_MODE_DATAC0);
assert(freedv_c0 != NULL);
freedv_set_frames_per_burst(freedv_c0, 1);
freedv_set_verbose(freedv_c0, 0);
int bytes_per_modem_frame_c0 = freedv_get_bits_per_modem_frame(freedv_c0)/8;
uint8_t bytes_out_c0[bytes_per_modem_frame_c0];
short demod_in_c0[freedv_get_n_max_modem_samples(freedv_c0)];

// set up DATAC1 Rx
struct freedv *freedv_c1 = freedv_open(FREEDV_MODE_DATAC1);
assert(freedv_c1 != NULL);
freedv_set_frames_per_burst(freedv_c1, 1);
freedv_set_verbose(freedv_c1, 0);
int bytes_per_modem_frame_c1 = freedv_get_bits_per_modem_frame(freedv_c1)/8;
uint8_t bytes_out_c1[bytes_per_modem_frame_c1];
short demod_in_c1[freedv_get_n_max_modem_samples(freedv_c1)];

// number of samples in demod_in buffer for each Rx
int n_c0 = 0;
int n_c1 = 0;
// number of frames received in each mode
int c0_frames = 0;
int c1_frames = 0;

short buf[NBUF];

// read a fixed buffer from stdin, use that to fill c0 and c1 demod_in buffers
while(fread(buf, sizeof(short), NBUF, stdin) == NBUF) {

if (run_receiver(freedv_c0, buf, demod_in_c0, &n_c0, bytes_out_c0)) {
fprintf(stderr, "DATAC0 frame received!\n");
c0_frames++;
}
if (run_receiver(freedv_c1, buf, demod_in_c1, &n_c1, bytes_out_c1)) {
fprintf(stderr, "DATAC1 frame received!\n");
c1_frames++;
}

}

fprintf(stderr, "DATAC0 Frames: %d DATAC1 Frames: %d\n", c0_frames, c1_frames);

freedv_close(freedv_c0);
freedv_close(freedv_c1);

return 0;
}

int run_receiver(struct freedv *freedv, short buf[], short demod_in[], int *pn, uint8_t bytes_out[]) {
int n = *pn;
int nbytes_out = 0;
int nin;

// NBUF new samples into DATAC1 Rx
memcpy(&demod_in[n], buf, sizeof(short)*NBUF);
n += NBUF; assert(n <= freedv_get_n_max_modem_samples(freedv));
nin = freedv_nin(freedv);
while (n > nin) {
nbytes_out = freedv_rawdatarx(freedv, bytes_out, demod_in);
// nin samples were read
n -= nin; assert(n >= 0);
memmove(demod_in, &demod_in[nin], sizeof(short)*n);
nin = freedv_nin(freedv);
}

*pn = n;
return nbytes_out;
}
107 changes: 107 additions & 0 deletions demo/freedv_datac0c1_tx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*---------------------------------------------------------------------------*\
FILE........: freedv_datac0c1_tx.c
AUTHOR......: David Rowe
DATE CREATED: Dec 2021
Transmitting alternate frames of two different raw data modes. See
freedv_datac0c1_rx.c
\*---------------------------------------------------------------------------*/

/*
Copyright (C) 2021 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. 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 General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

#include "freedv_api.h"
#include "ofdm_internal.h"

#define FRAMES 10

void send_burst(struct freedv *freedv);

int main(void) {
struct freedv *freedv_c0, *freedv_c1;

freedv_c0 = freedv_open(FREEDV_MODE_DATAC0); assert(freedv_c0 != NULL);
freedv_c1 = freedv_open(FREEDV_MODE_DATAC1); assert(freedv_c1 != NULL);

// send frames in different modes in random order
int c0_frames = 0;
int c1_frames = 0;
while ((c0_frames < FRAMES) || (c1_frames < FRAMES)) {
if (rand() & 1) {
if (c0_frames < FRAMES) {
send_burst(freedv_c0);
c0_frames++;
}
} else {
if (c1_frames < FRAMES) {
send_burst(freedv_c1);
c1_frames++;
}
}
}

freedv_close(freedv_c0);
freedv_close(freedv_c1);

return 0;
}


void send_burst(struct freedv *freedv) {
size_t bits_per_frame = freedv_get_bits_per_modem_frame(freedv);
size_t bytes_per_modem_frame = bits_per_frame/8;
size_t payload_bytes_per_modem_frame = bytes_per_modem_frame - 2; /* 16 bits used for the CRC */
size_t n_mod_out = freedv_get_n_tx_modem_samples(freedv);
uint8_t bytes_in[bytes_per_modem_frame];
short mod_out_short[n_mod_out];

/* generate a test frame */
uint8_t testframe_bits[bits_per_frame];
ofdm_generate_payload_data_bits(testframe_bits, bits_per_frame);
freedv_pack(bytes_in, testframe_bits, bits_per_frame);

/* send preamble */
int n_preamble = freedv_rawdatapreambletx(freedv, mod_out_short);
fwrite(mod_out_short, sizeof(short), n_preamble, stdout);

/* The raw data modes require a CRC in the last two bytes */
uint16_t crc16 = freedv_gen_crc16(bytes_in, payload_bytes_per_modem_frame);
bytes_in[bytes_per_modem_frame-2] = crc16 >> 8;
bytes_in[bytes_per_modem_frame-1] = crc16 & 0xff;

/* modulate and send a data frame */
freedv_rawdatatx(freedv, mod_out_short, bytes_in);
fwrite(mod_out_short, sizeof(short), n_mod_out, stdout);

/* send postamble */
int n_postamble = freedv_rawdatapostambletx(freedv, mod_out_short);
fwrite(mod_out_short, sizeof(short), n_postamble, stdout);

/* create some silence between bursts */
int inter_burst_delay_ms = 200;
int samples_delay = FREEDV_FS_8000*inter_burst_delay_ms/1000;
short sil_short[samples_delay];
for(int i=0; i<samples_delay; i++) sil_short[i] = 0;
fwrite(sil_short, sizeof(short), samples_delay, stdout);
}
12 changes: 11 additions & 1 deletion src/codec2_fdmdv.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,18 @@ extern "C" {
#define FDMDV_SCALE 750 /* suggested scaling for 16 bit shorts */
#define FDMDV_FCENTRE 1500 /* Centre frequency, Nc/2 carriers below this, Nc/2 carriers above (Hz) */

/* 8 to 48 kHz sample rate conversion */
/* 8 to 18 kHz sample rate conversion */

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

/* 8 to 48 kHz sample rate conversion */

#define FDMDV_OS_48 6 /* oversampling rate */
#define FDMDV_OS_TAPS_48K 48 /* number of OS filter taps at 48kHz */
#define FDMDV_OS_TAPS_48_8K (FDMDV_OS_TAPS_48K/FDMDV_OS_48) /* number of OS filter taps at 8kHz */

/* FDMDV states and stats structures */

struct FDMDV;
Expand All @@ -97,6 +103,10 @@ void fdmdv_8_to_16(float out16k[], float in8k[], int n);
void fdmdv_8_to_16_short(short out16k[], short in8k[], int n);
void fdmdv_16_to_8(float out8k[], float in16k[], int n);
void fdmdv_16_to_8_short(short out8k[], short in16k[], int n);
void fdmdv_8_to_48(float out48k[], float in8k[], int n);
void fdmdv_48_to_8(float out8k[], float in48k[], int n);
void fdmdv_8_to_48_short(short out48k[], short in8k[], int n);
void fdmdv_48_to_8_short(short out8k[], short in48k[], int n);

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

Expand Down
Loading

0 comments on commit 2943557

Please sign in to comment.