diff options
| author | Author Name <[email protected]> | 2023-07-07 12:20:59 +0930 |
|---|---|---|
| committer | David Rowe <[email protected]> | 2023-07-07 12:29:06 +0930 |
| commit | ac7c48b4dee99d4c772f133d70d8d1b38262fcd2 (patch) | |
| tree | a2d0ace57a9c0e2e5b611c4987f6fed1b38b81e7 /src/freedv_data_raw_tx.c | |
shallow zip-file copy from codec2 e9d726bf20
Diffstat (limited to 'src/freedv_data_raw_tx.c')
| -rw-r--r-- | src/freedv_data_raw_tx.c | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/src/freedv_data_raw_tx.c b/src/freedv_data_raw_tx.c new file mode 100644 index 0000000..b2e47f8 --- /dev/null +++ b/src/freedv_data_raw_tx.c @@ -0,0 +1,467 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: freedv_data_raw_tx.c + AUTHOR......: David Rowe + DATE CREATED: May 2020 + + Demonstrates transmitting frames of raw data bytes (instead of + compressed speech) using the FreeDV API. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2020 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 <string.h> +#include <errno.h> +#include <stdint.h> +#include <getopt.h> + +#include "freedv_api.h" +#include "fsk.h" +#include "ofdm_internal.h" +#include "ldpc_codes.h" + +size_t send_preamble(struct freedv *freedv, FILE *fout, int use_complex, size_t n_mod_out); +size_t send_modulated_data(struct freedv *freedv, FILE *fout, int use_complex, size_t n_mod_out, uint8_t bytes_in[]); +size_t send_postamble(struct freedv *freedv, FILE *fout, int use_complex, size_t n_mod_out); +size_t send_silence(FILE *fout, size_t shorts_per_sample, size_t samples_delay); +void comp_to_short(short mod_out_short[], COMP mod_out_comp[], int n_mod_out); + +int main(int argc, char *argv[]) { + FILE *fin, *fout; + char codename[80] = "H_256_512_4"; + struct freedv_advanced adv = {0,2,100,8000,1000,200, codename}; + struct freedv *freedv; + int mode; + int use_clip, use_txbpf, testframes, Ntestframes = 0; + int use_complex = 0; + float amp = FSK_SCALE; + size_t shorts_per_sample = 1; + int Nbursts = 1, sequence_numbers = 0; + int inter_burst_delay_ms = 0; + int postdelay_ms = 0; + uint8_t source_byte = 0; + + use_clip = -1; use_txbpf = -1; testframes = 0; + int framesperburst = 1; + int quiet = 0; + + int o = 0; + int opt_idx = 0; + while( o != -1 ){ + static struct option long_opts[] = { + {"testframes", required_argument, 0, 't'}, + {"help", no_argument, 0, 'h'}, + {"txbpf", required_argument, 0, 'b'}, + {"clip", required_argument, 0, 'l'}, + {"Fs", required_argument, 0, 'f'}, + {"Rs", required_argument, 0, 'r'}, + {"tone1", required_argument, 0, '1'}, + {"shift", required_argument, 0, 's'}, + {"bursts", required_argument, 0, 'e'}, + {"framesperburst", required_argument, 0, 'g'}, + {"delay", required_argument, 0, 'j'}, + {"postdelay", required_argument, 0, 'k'}, + {"seq", no_argument, 0, 'd'}, + {"source", required_argument, 0, 'i'}, + {"amp", required_argument, 0, 'a'}, + {"quiet", no_argument, 0, 'q'}, + {"complexout", no_argument, 0, 'c'}, + {"code", required_argument, 0, 'o'}, + {"listcodes", no_argument, 0, 'x'}, + {0, 0, 0, 0} + }; + + o = getopt_long(argc,argv,"a:cdt:hb:l:e:f:g:r:1:s:m:qi:o:x",long_opts,&opt_idx); + + switch(o) { + case 'a': + amp = atof(optarg)/2.0; + break; + case 'b': + use_txbpf = atoi(optarg); + break; + case 'c': + use_complex = 1; + shorts_per_sample = 2; + break; + case 'd': + sequence_numbers = 1; + break; + case 'i': + source_byte = strtol(optarg, NULL, 0); + fprintf(stderr,"source byte: 0x%02x\n", source_byte); + break; + case 'e': + Nbursts = atoi(optarg); + break; + case 'g': + framesperburst = atoi(optarg); + break; + case 'j': + inter_burst_delay_ms = atoi(optarg); + break; + case 'k': + postdelay_ms = atoi(optarg); + break; + case 't': + testframes = 1; + Ntestframes = atoi(optarg); + break; + case 'l': + use_clip = atoi(optarg); + break; + case 'm': + adv.M = atoi(optarg); + break; + case 'q': + quiet = 1; + break; + case 'f': + adv.Fs = atoi(optarg); + break; + case 'r': + adv.Rs = atoi(optarg); + break; + case '1': + adv.first_tone = atoi(optarg); + break; + case 's': + adv.tone_spacing = atoi(optarg); + break; + case 'o': + if (ldpc_codes_find(optarg) == -1) { + fprintf(stderr, "%s not found, try --listcodes\n", optarg); + exit(1); + } + strcpy(codename, optarg); + break; + case 'x': + ldpc_codes_list(); + exit(0); + break; + case 'h': + case '?': + goto helpmsg; + break; + } + } + int dx = optind; + + if (argc < 4) { + helpmsg: + fprintf(stderr, "\nusage: %s [options] FSK_LDPC|DATAC0|... InputBinaryDataFile OutputModemRawFile\n" + "\n" + " --testframes T send a total of T test frames (T should equal B*N)\n" + " --bursts B send B bursts of N testframes (default 1)\n" + " --framesperburst N burst mode, N frames per burst (default 1)\n" + " --delay ms testframe inter-burst delay in ms\n" + " --postdelay ms additional delay at end of run in ms\n" + " -c complex signed 16 bit output format (default real)\n" + " --clip 0|1 clipping for reduced PAPR\n" + " --txbpf 0|1 bandpass filter\n" + " --seq send packet sequence numbers (breaks testframe BER counting)\n" + " --source Byte insert a (non-zero) source address att byte[0]\n" + " --complexout complex sample output (default real)\n" + " --quiet\n" + "\n" + "For FSK_LDPC only:\n\n" + " -a amp maximum amplitude of FSK signal\n" + " -m 2|4 number of FSK tones\n" + " --Fs FreqHz sample rate (default 8000)\n" + " --Rs FreqHz symbol rate (default 100)\n" + " --tone1 FreqHz freq of first tone (default 1000)\n" + " --shift FreqHz shift between tones (default 200)\n\n" + " --code CodeName LDPC code (defaults (512,256)\n" + " --listcodes list available LDPC codes\n\n" + , argv[0]); + fprintf(stderr, "example: $ %s --testframes 6 --bursts 3 --framesperburst 2 datac0 /dev/zero samples.s16\n", argv[0]); + fprintf(stderr, "example: $ %s -c --testframes 10 FSK_LDPC /dev/zero samples.iq16\n\n", argv[0]); + exit(1); + } + + if( (argc - dx) < 3) { + fprintf(stderr, "too few arguments.\n"); + goto helpmsg; + } + + mode = -1; + if (!strcmp(argv[dx],"FSK_LDPC") || !strcmp(argv[dx],"fsk_ldpc")) mode = FREEDV_MODE_FSK_LDPC; + if (!strcmp(argv[dx],"DATAC0") || !strcmp(argv[dx],"datac0")) mode = FREEDV_MODE_DATAC0; + if (!strcmp(argv[dx],"DATAC1") || !strcmp(argv[dx],"datac1")) mode = FREEDV_MODE_DATAC1; + if (!strcmp(argv[dx],"DATAC3") || !strcmp(argv[dx],"datac3")) mode = FREEDV_MODE_DATAC3; + if (!strcmp(argv[dx],"DATAC4") || !strcmp(argv[dx],"datac4")) mode = FREEDV_MODE_DATAC4; + if (!strcmp(argv[dx],"DATAC13") || !strcmp(argv[dx],"datac13")) mode = FREEDV_MODE_DATAC13; + if (mode == -1) { + fprintf(stderr, "Error: in mode: %s", argv[dx]); + exit(1); + } + + if (strcmp(argv[dx+1], "-") == 0) fin = stdin; + else if ( (fin = fopen(argv[dx+1],"rb")) == NULL ) { + fprintf(stderr, "Error opening input file of bytes: %s: %s.\n", argv[dx+1], strerror(errno)); + exit(1); + } + + if (strcmp(argv[dx+2], "-") == 0) fout = stdout; + else if ( (fout = fopen(argv[dx+2],"wb")) == NULL ) { + fprintf(stderr, "Error opening output modem sample file: %s: %s.\n", argv[dx+2], strerror(errno)); + exit(1); + } + + if (mode != FREEDV_MODE_FSK_LDPC) + freedv = freedv_open(mode); + else + freedv = freedv_open_advanced(mode, &adv); + + assert(freedv != NULL); + + /* these are optional ------------------ */ + if (use_clip != -1) freedv_set_clip(freedv, use_clip); + if (use_txbpf != -1) freedv_set_tx_bpf(freedv, use_txbpf); + freedv_set_tx_amp(freedv, amp); + + /* Data modes have a multiple of 8 payload bits/frame */ + int bytes_per_modem_frame = freedv_get_bits_per_modem_frame(freedv)/8; + int payload_bytes_per_modem_frame = bytes_per_modem_frame; + payload_bytes_per_modem_frame -= 2; /* 16 bits used for the CRC */ + if (!quiet) fprintf(stderr, "payload bytes_per_modem_frame: %d ", payload_bytes_per_modem_frame); + assert((freedv_get_bits_per_modem_frame(freedv) % 8) == 0); + int n_mod_out = freedv_get_n_tx_modem_samples(freedv); + uint8_t bytes_in[bytes_per_modem_frame]; + + if (mode == FREEDV_MODE_FSK_LDPC) { + if (!quiet) fprintf(stderr, "Frequency: Fs: %4.1f Hz Rs: %5.0f Hz Tone1: %5.0f Hz Shift: %5.0f Hz M: %d \n", + (float)adv.Fs, (float)adv.Rs, (float)adv.first_tone, (float)adv.tone_spacing, adv.M); + + if (adv.tone_spacing < adv.Rs) { + fprintf(stderr, "Need shift: %d > Rs: %d\n", adv.tone_spacing, adv.Rs); + exit(1); + } + } + + /* a few sanity checks */ + if (testframes) { + if (Ntestframes != framesperburst*Nbursts) { + if (!quiet) fprintf(stderr, "Adjusting testframes to equal framesperburst*bursts\n"); + Ntestframes = framesperburst*Nbursts; + } + } else { + if (framesperburst != 1) { + fprintf(stderr, "Only one frame per burst currently supported in stdin mode\n"); + exit(1); + } + } + + int frames = 0; + size_t on_samples = 0; + size_t off_samples = 0; + + /* initial silence */ + + int samples_delay = 0; + if (inter_burst_delay_ms) { + /* user defined inter-burst delay */ + samples_delay = FREEDV_FS_8000*inter_burst_delay_ms/1000; + } + else { + /* just enough silence at the end of burst to allow demod to complete processing */ + samples_delay = 2*freedv_get_n_nom_modem_samples(freedv); + } + off_samples += send_silence(fout, shorts_per_sample, samples_delay); + + /* --------- Test Frame Mode --------------------------------------------------*/ + + if (testframes) { + + /* generate a fixed test frame */ + uint8_t testframe_bytes[bytes_per_modem_frame]; + memset(testframe_bytes, 0, bytes_per_modem_frame); + int bits_per_frame = freedv_get_bits_per_modem_frame(freedv); + uint8_t testframe_bits[bits_per_frame]; + ofdm_generate_payload_data_bits(testframe_bits, bits_per_frame); + freedv_pack(testframe_bytes, testframe_bits, bits_per_frame); + if (!quiet) fprintf(stderr, "\n"); + + for(int b=0; b<Nbursts; b++) { + on_samples += send_preamble(freedv, fout, use_complex, n_mod_out); + + for(int fpb=0; fpb<framesperburst; fpb++) { + memcpy(bytes_in, testframe_bytes, bytes_per_modem_frame); + if (source_byte) bytes_in[0] = source_byte; + if (sequence_numbers) bytes_in[1] = (frames+1) & 0xff; + + /* The raw data modes requires 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; + + on_samples += send_modulated_data(freedv, fout, use_complex, n_mod_out, bytes_in); + + /* if using pipes we don't want the usual buffering to occur */ + if (fout == stdout) fflush(stdout); + + frames++; + } + + on_samples += send_postamble(freedv, fout, use_complex, n_mod_out); + + int samples_delay = 0; + if (inter_burst_delay_ms) { + /* user defined inter-burst delay */ + samples_delay = FREEDV_FS_8000*inter_burst_delay_ms/1000; + } + else { + /* just enough silence at the end of burst to allow demod to complete processing */ + samples_delay = 2*freedv_get_n_nom_modem_samples(freedv); + } + off_samples += send_silence(fout, shorts_per_sample, samples_delay); + } + + } else { + + /* --------- modulate data from stdin mode --------------------------------------------------*/ + + while (fread(bytes_in, sizeof(uint8_t), payload_bytes_per_modem_frame, fin) == payload_bytes_per_modem_frame) { + on_samples += send_preamble(freedv, fout, use_complex, n_mod_out); + + if (source_byte) bytes_in[0] = source_byte; + if (sequence_numbers) bytes_in[1] = (frames+1) & 0xff; + + /* The raw data modes requires 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; + + on_samples += send_modulated_data(freedv, fout, use_complex, n_mod_out, bytes_in); + + /* if using pipes we don't want the usual buffering to occur */ + if (fout == stdout) fflush(stdout); + + on_samples += send_postamble(freedv, fout, use_complex, n_mod_out); + + int samples_delay = 0; + if (inter_burst_delay_ms) { + /* user defined inter-burst delay */ + samples_delay = FREEDV_FS_8000*inter_burst_delay_ms/1000; + } + else { + /* just enough silence at the end of burst to allow demod to complete processing */ + samples_delay = 2*freedv_get_n_nom_modem_samples(freedv); + } + off_samples += send_silence(fout, shorts_per_sample, samples_delay); + frames++; + } + } + + /* optional silence at the end of run */ + if (postdelay_ms) { + int samples_delay = FREEDV_FS_8000*postdelay_ms/1000; + if (!quiet) fprintf(stderr, "postdelay: %d %d\n", postdelay_ms, samples_delay); + off_samples += send_silence(fout, shorts_per_sample, samples_delay); + } + + /* SNR offset to use in channel simulator to account for on/off time of burst signal */ + float mark_space_ratio = (float)on_samples/(on_samples+off_samples); + float mark_space_SNR_offset = 10*log10(mark_space_ratio); + if (!quiet) fprintf(stderr, "mark:space: %3.2f SNR offset: %5.2f\n", mark_space_ratio, mark_space_SNR_offset); + + freedv_close(freedv); + fclose(fin); + fclose(fout); + + return 0; +} + + +size_t send_preamble(struct freedv *freedv, FILE *fout, int use_complex, size_t n_mod_out) { + short mod_out_short[2*n_mod_out]; + int shorts_per_sample = 1; + int n_preamble = 0; + + if (use_complex == 0) { + n_preamble = freedv_rawdatapreambletx(freedv, mod_out_short); + } else { + COMP mod_out_comp[n_mod_out]; + n_preamble = freedv_rawdatapreamblecomptx(freedv, mod_out_comp); + comp_to_short(mod_out_short, mod_out_comp, n_preamble); + shorts_per_sample = 2; + } + assert(n_preamble == freedv_get_n_tx_preamble_modem_samples(freedv)); + assert(n_preamble <= n_mod_out); + fwrite(mod_out_short, sizeof(short), shorts_per_sample*n_preamble, fout); + return n_preamble; +} + + +size_t send_modulated_data(struct freedv *freedv, FILE *fout, int use_complex, size_t n_mod_out, uint8_t bytes_in[]) { + short mod_out_short[2*n_mod_out]; + int shorts_per_sample = 1; + + if (use_complex == 0) { + freedv_rawdatatx(freedv, mod_out_short, bytes_in); + } else { + COMP mod_out_comp[n_mod_out]; + freedv_rawdatacomptx(freedv, mod_out_comp, bytes_in); + comp_to_short(mod_out_short, mod_out_comp, n_mod_out); + shorts_per_sample = 2; + } + fwrite(mod_out_short, sizeof(short), shorts_per_sample*n_mod_out, fout); + return n_mod_out; +} + + +size_t send_postamble(struct freedv *freedv, FILE *fout, int use_complex, size_t n_mod_out) { + short mod_out_short[2*n_mod_out]; + int shorts_per_sample = 1; + int n_postamble = 0; + + if (use_complex == 0) { + n_postamble = freedv_rawdatapostambletx(freedv, mod_out_short); + } else { + COMP mod_out_comp[n_mod_out]; + n_postamble = freedv_rawdatapostamblecomptx(freedv, mod_out_comp); + comp_to_short(mod_out_short, mod_out_comp, n_postamble); + shorts_per_sample = 2; + } + assert(n_postamble == freedv_get_n_tx_postamble_modem_samples(freedv)); + assert(n_postamble <= n_mod_out); + fwrite(mod_out_short, sizeof(short), shorts_per_sample*n_postamble, fout); + return n_postamble; +} + + +size_t send_silence(FILE *fout, size_t shorts_per_sample, size_t samples_delay) { + size_t n = shorts_per_sample*samples_delay; + short sil_short[n]; + for(int i=0; i<n; i++) sil_short[i] = 0; + fwrite(sil_short, sizeof(short), n, fout); + return samples_delay; +} + + +void comp_to_short(short mod_out_short[], COMP mod_out_comp[], int n_mod_out) { + for(int i=0; i<n_mod_out; i++) { + mod_out_short[2*i] = (short)(mod_out_comp[i].real); + mod_out_short[2*i+1] = (short)(mod_out_comp[i].imag); + } +} + |
