aboutsummaryrefslogtreecommitdiff
path: root/unittest/tcohpsk.c
diff options
context:
space:
mode:
authorMarin Ivanov <[email protected]>2025-07-25 10:17:14 +0300
committerMarin Ivanov <[email protected]>2026-01-18 20:09:26 +0200
commit0168586485e6310c598713c911b1dec5618d61a1 (patch)
tree6aabc2a12ef8fef70683f5389bea00f948015f77 /unittest/tcohpsk.c
Initial commitHEADmaster
* codec2 cut-down version 1.2.0 * Remove codebook and generation of sources * remove c2dec c2enc binaries * prepare for emscripten
Diffstat (limited to 'unittest/tcohpsk.c')
-rw-r--r--unittest/tcohpsk.c324
1 files changed, 324 insertions, 0 deletions
diff --git a/unittest/tcohpsk.c b/unittest/tcohpsk.c
new file mode 100644
index 0000000..1519d7d
--- /dev/null
+++ b/unittest/tcohpsk.c
@@ -0,0 +1,324 @@
+/*---------------------------------------------------------------------------*\
+
+ FILE........: tcohpsk.c
+ AUTHOR......: David Rowe
+ DATE CREATED: March 2015
+
+ Tests for the C version of the coherent PSK FDM modem. This program
+ outputs a file of Octave vectors that are loaded and automatically
+ tested against the Octave version of the modem by the Octave script
+ tcohpsk.m
+
+\*---------------------------------------------------------------------------*/
+
+/*
+ Copyright (C) 2015 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 <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "codec2_cohpsk.h"
+#include "codec2_fdmdv.h"
+#include "cohpsk_defs.h"
+#include "cohpsk_internal.h"
+#include "comp_prim.h"
+#include "fdmdv_internal.h"
+#include "noise_samples.h"
+#include "octave.h"
+
+#define FRAMES \
+ 30 /* LOG_FRAMES is #defined in cohpsk_internal.h */
+#define SYNC_FRAMES \
+ 12 /* sync state uses up extra log storage as we reprocess several times */
+#define FRAMESL \
+ (SYNC_FRAMES * FRAMES) /* worst case is every frame is out of sync */
+
+#define FOFF 58.7
+#define DFOFF (-0.5 / (float)COHPSK_FS)
+#define ESNODB 8
+#define PPM -1500
+
+extern float pilots_coh[][PILOTS_NC];
+
+int main(int argc, char *argv[]) {
+ struct COHPSK *coh;
+ int tx_bits[COHPSK_BITS_PER_FRAME];
+ COMP tx_symb[NSYMROWPILOT][COHPSK_NC * COHPSK_ND];
+ COMP tx_fdm_frame[COHPSK_M * NSYMROWPILOT];
+ COMP ch_fdm_frame[COHPSK_M * NSYMROWPILOT];
+ // COMP rx_fdm_frame_bb[M*NSYMROWPILOT];
+ // COMP ch_symb[NSYMROWPILOT][COHPSK_NC*COHPSK_ND];
+ float rx_bits_sd[COHPSK_BITS_PER_FRAME];
+ int rx_bits[COHPSK_BITS_PER_FRAME];
+
+ int tx_bits_log[COHPSK_BITS_PER_FRAME * FRAMES];
+ COMP tx_symb_log[NSYMROWPILOT * FRAMES][COHPSK_NC * COHPSK_ND];
+ COMP tx_fdm_frame_log[COHPSK_M * NSYMROWPILOT * FRAMES];
+ COMP ch_fdm_frame_log[COHPSK_M * NSYMROWPILOT * FRAMES];
+ COMP ch_fdm_frame_log_out[(COHPSK_M * NSYMROWPILOT + 1) * FRAMES];
+ // COMP rx_fdm_frame_bb_log[M*NSYMROWPILOT*FRAMES];
+ // COMP ch_symb_log[NSYMROWPILOT*FRAMES][COHPSK_NC*COHPSK_ND];
+ COMP ct_symb_ff_log[NSYMROWPILOT * FRAMES][COHPSK_NC * COHPSK_ND];
+ float rx_amp_log[NSYMROW * FRAMES][COHPSK_NC * COHPSK_ND];
+ float rx_phi_log[NSYMROW * FRAMES][COHPSK_NC * COHPSK_ND];
+ COMP rx_symb_log[NSYMROW * FRAMES][COHPSK_NC * COHPSK_ND];
+ int rx_bits_log[COHPSK_BITS_PER_FRAME * FRAMES];
+
+ FILE *fout;
+ int f, r, c, log_r, log_data_r, noise_r, ff_log_r, i;
+ double foff;
+ COMP foff_rect, phase_ch;
+
+ struct FDMDV *fdmdv;
+ // COMP rx_filt[COHPSK_NC*COHPSK_ND][P+1];
+ // int rx_filt_log_col_index = 0;
+ // float env[NT*P];
+ // float __attribute__((unused)) rx_timing;
+ COMP tx_onesym[COHPSK_NC * COHPSK_ND];
+ // COMP rx_onesym[COHPSK_NC*COHPSK_ND];
+ // int rx_baseband_log_col_index = 0;
+ // COMP rx_baseband_log[COHPSK_NC*COHPSK_ND][(M+M/P)*NSYMROWPILOT*FRAMES];
+ float f_est_log[FRAMES], sig_rms_log[FRAMES], noise_rms_log[FRAMES];
+ int f_est_samples;
+
+ int log_bits;
+ float EsNo, variance;
+ COMP scaled_noise;
+ int reliable_sync_bit;
+ int ch_fdm_frame_log_index, nin_frame, tmp, nout;
+
+ coh = cohpsk_create();
+ fdmdv = coh->fdmdv;
+ assert(coh != NULL);
+ cohpsk_set_verbose(coh, 1);
+
+ /* these puppies are used for logging data in the bowels on the modem */
+
+ coh->rx_baseband_log_col_sz =
+ (COHPSK_M + COHPSK_M / P) * NSYMROWPILOT * FRAMESL;
+ coh->rx_baseband_log = (COMP *)malloc(sizeof(COMP) * COHPSK_NC * COHPSK_ND *
+ coh->rx_baseband_log_col_sz);
+
+ coh->rx_filt_log_col_sz = (P + 1) * NSYMROWPILOT * FRAMESL;
+ coh->rx_filt_log = (COMP *)malloc(sizeof(COMP) * COHPSK_NC * COHPSK_ND *
+ coh->rx_filt_log_col_sz);
+
+ coh->ch_symb_log_col_sz = COHPSK_NC * COHPSK_ND;
+ coh->ch_symb_log = (COMP *)malloc(sizeof(COMP) * NSYMROWPILOT * FRAMESL *
+ coh->ch_symb_log_col_sz);
+
+ coh->rx_timing_log = (float *)malloc(sizeof(float) * NSYMROWPILOT * FRAMESL);
+
+ /* init stuff */
+
+ log_r = log_data_r = noise_r = log_bits = ff_log_r = f_est_samples = 0;
+ phase_ch.real = 1.0;
+ phase_ch.imag = 0.0;
+ foff = FOFF;
+
+ /* each carrier has power = 2, total power 2Nc, total symbol rate
+ NcRs, noise BW B=Fs Es/No = (C/Rs)/(N/B), N = var =
+ 2NcFs/NcRs(Es/No) = 2Fs/Rs(Es/No) */
+
+ EsNo = pow(10.0, ESNODB / 10.0);
+ variance = 2.0 * COHPSK_FS / (COHPSK_RS * EsNo);
+ // fprintf(stderr, "doff: %e\n", DFOFF);
+
+ /* Main Loop
+ * ---------------------------------------------------------------------*/
+
+ for (f = 0; f < FRAMES; f++) {
+ /* --------------------------------------------------------*\
+ Mod
+ \*---------------------------------------------------------*/
+
+ cohpsk_get_test_bits(coh, tx_bits);
+ bits_to_qpsk_symbols(tx_symb, (int *)tx_bits, COHPSK_BITS_PER_FRAME);
+
+ for (r = 0; r < NSYMROWPILOT; r++) {
+ for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) tx_onesym[c] = tx_symb[r][c];
+ tx_filter_and_upconvert_coh(
+ &tx_fdm_frame[r * COHPSK_M], COHPSK_NC * COHPSK_ND, tx_onesym,
+ fdmdv->tx_filter_memory, fdmdv->phase_tx, fdmdv->freq,
+ &fdmdv->fbb_phase_tx, fdmdv->fbb_rect);
+ }
+ cohpsk_clip(tx_fdm_frame, COHPSK_CLIP, NSYMROWPILOT * COHPSK_M);
+
+ /* --------------------------------------------------------*\
+ Channel
+ \*---------------------------------------------------------*/
+
+ for (r = 0; r < NSYMROWPILOT * COHPSK_M; r++) {
+ foff_rect.real = cos(2.0 * M_PI * foff / COHPSK_FS);
+ foff_rect.imag = sin(2.0 * M_PI * foff / COHPSK_FS);
+ foff += DFOFF;
+ phase_ch = cmult(phase_ch, foff_rect);
+ ch_fdm_frame[r] = cmult(tx_fdm_frame[r], phase_ch);
+ }
+ phase_ch.real /= cabsolute(phase_ch);
+ phase_ch.imag /= cabsolute(phase_ch);
+ // fprintf(stderr, "%f\n", foff);
+ for (r = 0; r < NSYMROWPILOT * COHPSK_M; r++, noise_r++) {
+ scaled_noise = fcmult(sqrt(variance), noise[noise_r]);
+ ch_fdm_frame[r] = cadd(ch_fdm_frame[r], scaled_noise);
+ }
+
+ /* optional gain to test level sensitivity */
+
+ for (r = 0; r < NSYMROWPILOT * COHPSK_M; r++) {
+ ch_fdm_frame[r] = fcmult(1.0, ch_fdm_frame[r]);
+ }
+
+ /* tx vector logging */
+
+ memcpy(&tx_bits_log[COHPSK_BITS_PER_FRAME * f], tx_bits,
+ sizeof(int) * COHPSK_BITS_PER_FRAME);
+ memcpy(&tx_fdm_frame_log[COHPSK_M * NSYMROWPILOT * f], tx_fdm_frame,
+ sizeof(COMP) * COHPSK_M * NSYMROWPILOT);
+ memcpy(&ch_fdm_frame_log[COHPSK_M * NSYMROWPILOT * f], ch_fdm_frame,
+ sizeof(COMP) * COHPSK_M * NSYMROWPILOT);
+
+ for (r = 0; r < NSYMROWPILOT; r++, log_r++) {
+ for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) {
+ tx_symb_log[log_r][c] = tx_symb[r][c];
+ }
+ }
+ }
+
+ /* Fs offset simulation */
+
+ nout = cohpsk_fs_offset(ch_fdm_frame_log_out, ch_fdm_frame_log,
+ COHPSK_M * NSYMROWPILOT * FRAMES, PPM);
+ assert(nout < (COHPSK_M * NSYMROWPILOT + 1) * FRAMES);
+
+ nin_frame = COHPSK_NOM_SAMPLES_PER_FRAME;
+ ch_fdm_frame_log_index = 0;
+
+ /* --------------------------------------------------------*\
+ Demod
+ \*---------------------------------------------------------*/
+
+ for (f = 0; f < FRAMES; f++) {
+ coh->frame = f;
+
+ // printf("nin_frame: %d\n", nin_frame);
+
+ assert(ch_fdm_frame_log_index < COHPSK_M * NSYMROWPILOT * FRAMES);
+ tmp = nin_frame;
+ cohpsk_demod(coh, rx_bits_sd, &reliable_sync_bit,
+ &ch_fdm_frame_log_out[ch_fdm_frame_log_index], &nin_frame);
+ for (i = 0; i < COHPSK_BITS_PER_FRAME; i++)
+ rx_bits[i] = rx_bits_sd[i] < 0.0;
+
+ ch_fdm_frame_log_index += tmp;
+
+ /* --------------------------------------------------------*\
+ Log each vector
+ \*---------------------------------------------------------*/
+
+ if (coh->sync == 1) {
+ for (r = 0; r < NSYMROWPILOT; r++, ff_log_r++) {
+ for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) {
+ ct_symb_ff_log[ff_log_r][c] = coh->ct_symb_ff_buf[r][c];
+ }
+ }
+
+ for (r = 0; r < NSYMROW; r++, log_data_r++) {
+ for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) {
+ rx_amp_log[log_data_r][c] = coh->amp_[r][c];
+ rx_phi_log[log_data_r][c] = coh->phi_[r][c];
+ rx_symb_log[log_data_r][c] = coh->rx_symb[r][c];
+ }
+ }
+ memcpy(&rx_bits_log[COHPSK_BITS_PER_FRAME * log_bits], rx_bits,
+ sizeof(int) * COHPSK_BITS_PER_FRAME);
+ log_bits++;
+ f_est_log[f_est_samples] = coh->f_est;
+ sig_rms_log[f_est_samples] = coh->sig_rms;
+ noise_rms_log[f_est_samples] = coh->noise_rms;
+ f_est_samples++;
+ ;
+ }
+
+ assert(log_r <= NSYMROWPILOT * FRAMES);
+ assert(noise_r <= NSYMROWPILOT * COHPSK_M * FRAMES);
+ assert(log_data_r <= NSYMROW * FRAMES);
+
+ printf("\r [%d]", f + 1);
+ }
+ printf("\n");
+
+ /*---------------------------------------------------------*\
+ Dump logs to Octave file for evaluation
+ by tcohpsk.m Octave script
+ \*---------------------------------------------------------*/
+
+ fout = fopen("tcohpsk_out.txt", "wt");
+ assert(fout != NULL);
+ fprintf(fout, "# Created by tcohpsk.c\n");
+ octave_save_int(fout, "tx_bits_log_c", tx_bits_log, 1,
+ COHPSK_BITS_PER_FRAME * FRAMES);
+ octave_save_complex(fout, "tx_symb_log_c", (COMP *)tx_symb_log,
+ NSYMROWPILOT * FRAMES, COHPSK_NC * COHPSK_ND,
+ COHPSK_NC * COHPSK_ND);
+ octave_save_complex(fout, "tx_fdm_frame_log_c", (COMP *)tx_fdm_frame_log, 1,
+ COHPSK_M * NSYMROWPILOT * FRAMES,
+ COHPSK_M * NSYMROWPILOT * FRAMES);
+ octave_save_complex(fout, "ch_fdm_frame_log_c", (COMP *)ch_fdm_frame_log_out,
+ 1, nout - 1, nout - 1);
+ // octave_save_complex(fout, "rx_fdm_frame_bb_log_c",
+ // (COMP*)rx_fdm_frame_bb_log, 1, M*NSYMROWPILOT*FRAMES,
+ // M*NSYMROWPILOT*FRAMES);
+ octave_save_complex(fout, "rx_baseband_log_c", (COMP *)coh->rx_baseband_log,
+ COHPSK_NC * COHPSK_ND, coh->rx_baseband_log_col_index,
+ coh->rx_baseband_log_col_sz);
+ octave_save_complex(fout, "rx_filt_log_c", (COMP *)coh->rx_filt_log,
+ COHPSK_NC * COHPSK_ND, coh->rx_filt_log_col_index,
+ coh->rx_filt_log_col_sz);
+ octave_save_complex(fout, "ch_symb_log_c", (COMP *)coh->ch_symb_log,
+ coh->ch_symb_log_r, COHPSK_NC * COHPSK_ND,
+ COHPSK_NC * COHPSK_ND);
+ octave_save_float(fout, "rx_timing_log_c", (float *)coh->rx_timing_log, 1,
+ coh->rx_timing_log_index, coh->rx_timing_log_index);
+ octave_save_complex(fout, "ct_symb_ff_log_c", (COMP *)ct_symb_ff_log,
+ NSYMROWPILOT * FRAMES, COHPSK_NC * COHPSK_ND,
+ COHPSK_NC * COHPSK_ND);
+ octave_save_float(fout, "rx_amp_log_c", (float *)rx_amp_log, log_data_r,
+ COHPSK_NC * COHPSK_ND, COHPSK_NC * COHPSK_ND);
+ octave_save_float(fout, "rx_phi_log_c", (float *)rx_phi_log, log_data_r,
+ COHPSK_NC * COHPSK_ND, COHPSK_NC * COHPSK_ND);
+ octave_save_complex(fout, "rx_symb_log_c", (COMP *)rx_symb_log, log_data_r,
+ COHPSK_NC * COHPSK_ND, COHPSK_NC * COHPSK_ND);
+ octave_save_int(fout, "rx_bits_log_c", rx_bits_log, 1,
+ COHPSK_BITS_PER_FRAME * log_bits);
+ octave_save_float(fout, "f_est_log_c", &f_est_log[1], 1, f_est_samples - 1,
+ f_est_samples - 1);
+ octave_save_float(fout, "sig_rms_log_c", sig_rms_log, 1, f_est_samples,
+ f_est_samples - 1);
+ octave_save_float(fout, "noise_rms_log_c", noise_rms_log, 1, f_est_samples,
+ f_est_samples);
+#ifdef XX
+#endif
+ fclose(fout);
+
+ cohpsk_destroy(coh);
+
+ return 0;
+}