diff --git a/CMakeLists.txt b/CMakeLists.txt index e62c58a7..dbd2eca7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -472,7 +472,7 @@ endif() add_test(NAME test_OFDM_modem_octave_qam16_uncoded COMMAND sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/octave; - echo \"ofdm_tx('test_qam16.raw','qam16c1',3,12,'awgn','bursts',3); ofdm_rx('test_qam16.raw','qam16c1', 'passber', 0.05, 'packetsperburst', 1); quit\" | + echo \"ofdm_tx('test_qam16.raw','qam16c2',1,12,'awgn','bursts',3); ofdm_rx('test_qam16.raw','qam16c2', 'passber', 0.05, 'packetsperburst', 1); quit\" | DISPLAY=\"\" octave-cli") set_tests_properties(test_OFDM_modem_octave_qam16_uncoded PROPERTIES PASS_REGULAR_EXPRESSION "Pass") @@ -669,6 +669,13 @@ endif() cd ${CMAKE_CURRENT_BINARY_DIR}/src; cat test.raw | ./ofdm_demod --mode datac14 --out /dev/null --testframes --ldpc --verbose 2 --packetsperburst 1") + # QAM16C2 Octave Tx, C Rx, burst mode + add_test(NAME test_OFDM_modem_qam16c2_octave + COMMAND sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/octave; + DISPLAY=\"\" octave-cli -qf --eval 'ofdm_ldpc_tx(\"${CMAKE_CURRENT_BINARY_DIR}/src/test.raw\",\"qam16c2\",1,10,\"awgn\",\"bursts\",2)'; + cd ${CMAKE_CURRENT_BINARY_DIR}/src; + cat test.raw | ./ofdm_demod --mode qam16c2 --out /dev/null --testframes --ldpc --verbose 2 --packetsperburst 1") + # DATAC4 C Tx, C Rx, burst mode add_test(NAME test_OFDM_modem_datac4_ldpc_burst COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src; @@ -690,6 +697,13 @@ endif() ./ch - - --No -17 | ./ofdm_demod --mode datac14 --out /dev/null --testframes --ldpc --verbose 2 --packetsperburst 1") + # QAM16C2 C Tx, C Rx, burst mode + add_test(NAME test_OFDM_modem_qam16c2_ldpc_burst + COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src; + ./ofdm_mod --mode qam16c2 --in /dev/zero --testframes 1 --verbose 1 --ldpc --bursts 10 | + ./ch - - --No -30 | + ./ofdm_demod --mode qam16c2 --out /dev/null --testframes --ldpc --verbose 2 --packetsperburst 1") + # ------------------------------------------------------------------------- # LDPC # ------------------------------------------------------------------------- @@ -1340,6 +1354,13 @@ endif(NOT APPLE) ./freedv_data_raw_rx DATAC14 - binaryOut.bin -v; diff binaryIn.bin binaryOut.bin") + add_test(NAME test_freedv_data_raw_ofdm_qam16c2_burst_file + COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src; + head -c $((1213*10)) binaryIn.bin; + ./freedv_data_raw_tx qam16c2 binaryIn.bin - --bursts 10 | + ./freedv_data_raw_rx qam16c2 - binaryOut.bin -v; + diff binaryIn.bin binaryOut.bin") + # FSK LDPC default 100 bit/s 2FSK, enough noise for several % raw BER to give # FEC/acquisition a work out, bursts of 1 frame as that stresses acquisition add_test(NAME test_freedv_data_raw_fsk_ldpc_100 @@ -1426,6 +1447,7 @@ endif(NOT APPLE) test_OFDM_modem_datac4_octave test_OFDM_modem_datac13_octave test_OFDM_modem_datac14_octave + test_OFDM_modem_qam16c2_octave test_fsk_lib_4fsk_ldpc test_OFDM_modem_datac0_compression PROPERTIES diff --git a/README_data.md b/README_data.md index 49fb9e3e..70d48171 100644 --- a/README_data.md +++ b/README_data.md @@ -144,9 +144,10 @@ These modes use an OFDM modem with powerful LDPC codes and are designed for send | DATAC0 | 500 | 291 | 14 | (256,128) | 0.44 | 70/100 at 0dB | Reverse link ACK packets | | DATAC1 | 1700 | 980 | 510 | (8192,4096) | 4.18 | 92/100 at 5dB | Forward link data (medium SNR) | | DATAC3 | 500 | 321 | 126 | (2048,1024) | 3.19 | 74/100 at 0dB | Forward link data (low SNR) | -| DATAC4 | 250 | 87 | 56 | (1472,448) | 5.17 | 90/100 at -4dB | Forward link data (low SNR) | +| DATAC4 | 250 | 87 | 54 | (1472,448) | 5.17 | 90/100 at -4dB | Forward link data (low SNR) | | DATAC13 | 200 | 64 | 14 | (384,128) | 2.0 | 90/100 at -4dB | Reverse link ACK packets (low SNR) | | DATAC14 | 250 | 58 | 3 | (112,56) | 0.69 | 90/100 at -2dB | Reverse link ACK packets (low SNR) | +| QAM16C2 | 2100 | 3100 | 1213 | (16200,9720) | 3.2 | 90/100 at 15dB | Forward link data (high SNR) | Notes: 1. 16 bits (2 bytes) per frame are reserved for a 16 bit CRC, e.g. for `datac3` we have 128 byte frames, and 128-2=126 bytes/frame of payload data. diff --git a/octave/ofdm_lib.m b/octave/ofdm_lib.m index e4ae35a2..93a2da7a 100644 --- a/octave/ofdm_lib.m +++ b/octave/ofdm_lib.m @@ -52,7 +52,6 @@ bad_uw_errors = config.bad_uw_errors; amp_scale = config.amp_scale; amp_est_mode = config.amp_est_mode; - EsNo_est_all_symbols = config.EsNo_est_all_symbols; EsNodB = config.EsNodB; state_machine = config.state_machine; edge_pilots = config.edge_pilots; @@ -74,10 +73,10 @@ states.Nsampersymbol = states.M+states.Ncp; % number of samples in a single symbol states.Nsamperframe = Ns*states.Nsampersymbol; % number of samples in a modem frame states.qam16 = [ - 1 + j, 1 + j*3, 3 + j, 3 + j*3; - 1 - j, 1 - j*3, 3 - j, 3 - j*3; - -1 + j, -1 + j*3, -3 + j, -3 + j*3; - -1 - j, -1 - j*3, -3 - j, -3 - j*3]/3; + 1 + j, 1 + j*3, 3 + j, 3 + j*3, ... + 1 - j, 1 - j*3, 3 - j, 3 - j*3, ... + -1 + j, -1 + j*3, -3 + j, -3 + j*3, ... + -1 - j, -1 - j*3, -3 - j, -3 - j*3]; rms = sqrt(states.qam16(:)'*states.qam16(:)/16);% set average Es to 1 states.qam16 /= rms; states.qam16 *= exp(-j*pi/4); % same rotation as QPSK constellation @@ -128,7 +127,7 @@ states.uw_ind = [states.uw_ind bps*ind_sym-b]; % bit index end end - + % how many of the first few frames have UW symbols in them Nsymsperframe = states.Nbitsperframe/states.bps; states.Nuwframes = ceil(states.uw_ind_sym(end)/Nsymsperframe); @@ -275,7 +274,6 @@ % Es/No (SNR) est states - states.EsNo_est_all_symbols = EsNo_est_all_symbols; states.clock_offset_est = 0; % pre-amble for data modes diff --git a/octave/ofdm_mode.m b/octave/ofdm_mode.m index d88f960b..87009e72 100644 --- a/octave/ofdm_mode.m +++ b/octave/ofdm_mode.m @@ -25,7 +25,6 @@ config.bad_uw_errors = 3; config.amp_scale = 245E3; config.amp_est_mode = 0; - config.EsNo_est_all_symbols = 1; config.EsNodB = 3; config.state_machine = "voice1"; config.edge_pilots = 1; @@ -59,12 +58,12 @@ config.bps=4; config.Ntxtbits = 0; config.Nuwbits = 15*4; config.bad_uw_errors = 5; config.state_machine = "data"; config.ftwindow_width = 32; config.amp_scale = 132E3; - config.EsNo_est_all_symbols = 0; config.amp_est_mode = 1; config.EsNodB = 10; + config.amp_est_mode = 1; config.EsNodB = 10; elseif strcmp(mode,"qam16c2") Ns=5; config.Np=31; Tcp = 0.004; Ts = 0.016; Nc = 33; config.data_mode = "streaming"; - config.bps=4; config.Ntxtbits = 0; config.Nuwbits = 42*4; config.bad_uw_errors = 15; + config.bps=4; config.Ntxtbits = 0; config.Nuwbits = 42*4; config.bad_uw_errors = 50; config.ftwindow_width = 80; config.amp_scale = 135E3; config.state_machine = "data"; - config.EsNo_est_all_symbols = 0; config.amp_est_mode = 1; config.EsNodB = 10; + config.amp_est_mode = 1; config.EsNodB = 10; config.tx_uw = zeros(1,config.Nuwbits = 42*4); config.tx_uw(1:24) = [1 1 0 0 1 0 1 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0]; config.tx_uw(end-24+1:end) = [1 1 0 0 1 0 1 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0]; diff --git a/octave/qam16.m b/octave/qam16.m index 5b0f047c..7d2dff4d 100644 --- a/octave/qam16.m +++ b/octave/qam16.m @@ -25,7 +25,7 @@ function test_qam16_mod_demod(constellation) for decimal=0:15 tx_bits = zeros(1,4); for i=1:4 - tx_bits(1,5-i) = bitand(bitshift(decimal-1,1-i),1); + tx_bits(1,5-i) = bitand(bitshift(decimal,1-i),1); end symbol = qam16_mod(constellation, tx_bits); rx_bits = qam16_demod(constellation,symbol); diff --git a/src/freedv_2020.c b/src/freedv_2020.c index f1dee284..a1a3d318 100644 --- a/src/freedv_2020.c +++ b/src/freedv_2020.c @@ -229,8 +229,7 @@ int freedv_comprx_2020(struct freedv *f, COMP demod_in[]) { f->sync = 0; - // TODO: should be higher for 2020? - float EsNo = 3.0; + float EsNo = pow(10.0, ofdm->EsNodB / 10); /* looking for modem sync */ @@ -249,7 +248,7 @@ int freedv_comprx_2020(struct freedv *f, COMP demod_in[]) { ofdm_demod(ofdm, rx_bits, demod_in); ofdm_extract_uw(ofdm, ofdm->rx_np, ofdm->rx_amp, rx_uw); - ofdm_disassemble_qpsk_modem_packet_with_text_amps( + ofdm_disassemble_psk_modem_packet_with_text_amps( ofdm, ofdm->rx_np, ofdm->rx_amp, payload_syms, payload_amps, txt_bits, &txt_sym_index); @@ -280,14 +279,14 @@ int freedv_comprx_2020(struct freedv *f, COMP demod_in[]) { uint8_t out_char[coded_bits_per_frame]; if (f->test_frames) { - Nerrs_raw = - count_uncoded_errors(ldpc, &f->ofdm->config, codeword_symbols_de, 0); + Nerrs_raw = count_uncoded_errors( + ldpc, &f->ofdm->config, codeword_symbols_de, codeword_amps_de, 0); f->total_bit_errors += Nerrs_raw; f->total_bits += f->ofdm_bitsperframe; } symbols_to_llrs(llr, codeword_symbols_de, codeword_amps_de, EsNo, - ofdm->mean_amp, coded_syms_per_frame); + ofdm->mean_amp, ofdm->bps, coded_syms_per_frame); ldpc_decode_frame(ldpc, &parityCheckCount, &iter, out_char, llr); if (parityCheckCount != ldpc->NumberParityBits) rx_status |= FREEDV_RX_BIT_ERRORS; diff --git a/src/freedv_700.c b/src/freedv_700.c index a7d7aac1..f2c795e8 100644 --- a/src/freedv_700.c +++ b/src/freedv_700.c @@ -198,6 +198,7 @@ void freedv_ofdm_data_open(struct freedv *f, struct freedv_advanced *adv) { if (f->mode == FREEDV_MODE_DATAC4) strcpy(mode, "datac4"); if (f->mode == FREEDV_MODE_DATAC13) strcpy(mode, "datac13"); if (f->mode == FREEDV_MODE_DATAC14) strcpy(mode, "datac14"); + if (f->mode == FREEDV_MODE_QAM16C2) strcpy(mode, "qam16c2"); if (f->mode == FREEDV_MODE_DATA_CUSTOM) { assert(adv != NULL); assert(adv->config != NULL); @@ -447,7 +448,7 @@ int freedv_comp_short_rx_ofdm(struct freedv *f, void *demod_in_8kHz, assert((demod_in_is_short == 0) || (demod_in_is_short == 1)); int rx_status = 0; - float EsNo = 3.0; /* further work: estimate this properly from signal */ + float EsNo = pow(10.0, ofdm->EsNodB / 10); f->sync = 0; /* looking for OFDM modem sync */ @@ -490,7 +491,7 @@ int freedv_comp_short_rx_ofdm(struct freedv *f, void *demod_in_8kHz, /* we have received enough modem frames to complete packet and run LDPC * decoder */ int txt_sym_index = 0; - ofdm_disassemble_qpsk_modem_packet_with_text_amps( + ofdm_disassemble_psk_modem_packet_with_text_amps( ofdm, rx_syms, rx_amps, payload_syms, payload_amps, txt_bits, &txt_sym_index); @@ -504,15 +505,48 @@ int freedv_comp_short_rx_ofdm(struct freedv *f, void *demod_in_8kHz, float llr[Npayloadbitsperpacket]; uint8_t decoded_codeword[Npayloadbitsperpacket]; symbols_to_llrs(llr, payload_syms_de, payload_amps_de, EsNo, - ofdm->mean_amp, Npayloadsymsperpacket); + ofdm->mean_amp, ofdm->bps, Npayloadsymsperpacket); ldpc_decode_frame(ldpc, &parityCheckCount, &iter, decoded_codeword, llr); - // iter = run_ldpc_decoder(ldpc, decoded_codeword, llr, - // &parityCheckCount); memcpy(f->rx_payload_bits, decoded_codeword, Ndatabitsperpacket); if (strlen(ofdm->data_mode)) { + int crc_ok = + freedv_check_crc16_unpacked(f->rx_payload_bits, Ndatabitsperpacket); + if (!crc_ok && f->verbose) { + int bytes_per_frame = (Ndatabitsperpacket + 7) / 8; + uint8_t rx_bytes[bytes_per_frame]; + freedv_pack(rx_bytes, f->rx_payload_bits, Ndatabitsperpacket); + uint16_t rx_crc16 = + ((uint16_t)rx_bytes[bytes_per_frame - 2] << 8) | + rx_bytes[bytes_per_frame - 1]; + uint16_t calc_crc16 = + freedv_gen_crc16(rx_bytes, bytes_per_frame - 2); + fprintf(stderr, + "OFDM data CRC fail: mode=%s bytes=%d rx_crc=%04x calc_crc=%04x " + "pcc=%d/%d iter=%d euw=%d foff=%4.1f\n", + ofdm->mode, bytes_per_frame, rx_crc16, calc_crc16, + parityCheckCount, ldpc->NumberParityBits, iter, ofdm->uw_errors, + (double)ofdm->foff_est_hz); + if (f->verbose >= 2) { + int n = bytes_per_frame < 16 ? bytes_per_frame : 16; + fprintf(stderr, " payload[0:%d] =", n); + for (int bi = 0; bi < n; bi++) fprintf(stderr, " %02x", rx_bytes[bi]); + fprintf(stderr, "\n"); + } + } else if (crc_ok && f->verbose >= 2) { + int bytes_per_frame = (Ndatabitsperpacket + 7) / 8; + uint8_t rx_bytes[bytes_per_frame]; + freedv_pack(rx_bytes, f->rx_payload_bits, Ndatabitsperpacket); + fprintf(stderr, "OFDM data OK: mode=%s bytes=%d pcc=%d/%d iter=%d\n", + ofdm->mode, bytes_per_frame, parityCheckCount, + ldpc->NumberParityBits, iter); + int n = bytes_per_frame < 16 ? bytes_per_frame : 16; + fprintf(stderr, " payload[0:%d] =", n); + for (int bi = 0; bi < n; bi++) fprintf(stderr, " %02x", rx_bytes[bi]); + fprintf(stderr, "\n"); + } // we need a valid CRC to declare a data packet valid - if (freedv_check_crc16_unpacked(f->rx_payload_bits, Ndatabitsperpacket)) + if (crc_ok) rx_status |= FREEDV_RX_BITS; else rx_status |= FREEDV_RX_BIT_ERRORS; @@ -526,8 +560,9 @@ int freedv_comp_short_rx_ofdm(struct freedv *f, void *demod_in_8kHz, if (f->test_frames) { /* est uncoded BER from payload bits */ - Nerrs_raw = count_uncoded_errors( - ldpc, &f->ofdm->config, payload_syms_de, strlen(ofdm->data_mode)); + Nerrs_raw = + count_uncoded_errors(ldpc, &f->ofdm->config, payload_syms_de, + payload_amps_de, strlen(ofdm->data_mode)); f->total_bit_errors += Nerrs_raw; f->total_bits += Npayloadbitsperpacket; diff --git a/src/freedv_api.c b/src/freedv_api.c index 3c812cac..bbf594b8 100644 --- a/src/freedv_api.c +++ b/src/freedv_api.c @@ -131,7 +131,8 @@ struct freedv *freedv_open_advanced(int mode, struct freedv_advanced *adv) { FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, mode) || - FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, mode)) == false) + FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, mode) || + FDV_MODE_ACTIVE(FREEDV_MODE_QAM16C2, mode)) == false) return NULL; /* set everything to zero just in case */ @@ -165,6 +166,8 @@ struct freedv *freedv_open_advanced(int mode, struct freedv_advanced *adv) { freedv_ofdm_data_open(f, NULL); if (FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, mode)) freedv_ofdm_data_open(f, adv); + if (FDV_MODE_ACTIVE(FREEDV_MODE_QAM16C2, mode)) + freedv_ofdm_data_open(f, NULL); varicode_decode_init(&f->varicode_dec_states, 1); @@ -248,6 +251,7 @@ void freedv_close(struct freedv *freedv) { FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, freedv->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, freedv->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, freedv->mode) || + FDV_MODE_ACTIVE(FREEDV_MODE_QAM16C2, freedv->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, freedv->mode)) { FREE(freedv->rx_syms); FREE(freedv->rx_amps); @@ -281,6 +285,7 @@ static int is_ofdm_mode(struct freedv *f) { FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode) || + FDV_MODE_ACTIVE(FREEDV_MODE_QAM16C2, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, f->mode); } @@ -291,6 +296,7 @@ static int is_ofdm_data_mode(struct freedv *f) { FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode) || + FDV_MODE_ACTIVE(FREEDV_MODE_QAM16C2, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, f->mode); } @@ -483,6 +489,7 @@ void freedv_rawdatacomptx(struct freedv *f, COMP mod_out[], FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode) || + FDV_MODE_ACTIVE(FREEDV_MODE_QAM16C2, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, f->mode)) freedv_comptx_ofdm(f, mod_out); @@ -1084,6 +1091,7 @@ int freedv_rawdatacomprx(struct freedv *f, unsigned char *packed_payload_bits, FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode) || + FDV_MODE_ACTIVE(FREEDV_MODE_QAM16C2, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, f->mode)) rx_status = freedv_comp_short_rx_ofdm(f, (void *)demod_in, 0, 1.0f); if (FDV_MODE_ACTIVE(FREEDV_MODE_FSK_LDPC, f->mode)) { diff --git a/src/freedv_api.h b/src/freedv_api.h index 0ca80ce1..4569a5b7 100644 --- a/src/freedv_api.h +++ b/src/freedv_api.h @@ -63,6 +63,7 @@ extern "C" { #define FREEDV_MODE_DATAC13 19 #define FREEDV_MODE_DATAC14 20 #define FREEDV_MODE_DATA_CUSTOM 21 +#define FREEDV_MODE_QAM16C2 22 // Sample rates used #define FREEDV_FS_8000 8000 @@ -148,6 +149,9 @@ extern "C" { #if !defined(FREEDV_MODE_DATA_CUSTOM_EN) #define FREEDV_MODE_DATA_CUSTOM_EN FREEDV_MODE_EN_DEFAULT #endif +#if !defined(FREEDV_MODE_QAM16C2_EN) +#define FREEDV_MODE_QAM16C2_EN FREEDV_MODE_EN_DEFAULT +#endif #define FDV_MODE_ACTIVE(mode_name, var) \ ((mode_name##_EN) == 0 ? 0 : (var) == mode_name) diff --git a/src/freedv_data_raw_rx.c b/src/freedv_data_raw_rx.c index 435648d7..50fcb32f 100644 --- a/src/freedv_data_raw_rx.c +++ b/src/freedv_data_raw_rx.c @@ -217,6 +217,8 @@ int main(int argc, char *argv[]) { mode = FREEDV_MODE_DATAC13; if (!strcmp(argv[dx], "DATAC14") || !strcmp(argv[dx], "datac14")) mode = FREEDV_MODE_DATAC14; + if (!strcmp(argv[dx], "QAM16C2") || !strcmp(argv[dx], "qam16c2")) + mode = FREEDV_MODE_QAM16C2; if (!strcmp(argv[dx], "CUSTOM") || !strcmp(argv[dx], "custom")) mode = FREEDV_MODE_DATA_CUSTOM; if (mode == -1) { diff --git a/src/freedv_data_raw_tx.c b/src/freedv_data_raw_tx.c index 7fa1e842..7efe7038 100644 --- a/src/freedv_data_raw_tx.c +++ b/src/freedv_data_raw_tx.c @@ -238,6 +238,8 @@ int main(int argc, char *argv[]) { mode = FREEDV_MODE_DATAC13; if (!strcmp(argv[dx], "DATAC14") || !strcmp(argv[dx], "datac14")) mode = FREEDV_MODE_DATAC14; + if (!strcmp(argv[dx], "QAM16C2") || !strcmp(argv[dx], "qam16c2")) + mode = FREEDV_MODE_QAM16C2; if (!strcmp(argv[dx], "CUSTOM") || !strcmp(argv[dx], "custom")) mode = FREEDV_MODE_DATA_CUSTOM; if (mode == -1) { diff --git a/src/freedv_fsk.c b/src/freedv_fsk.c index 06132630..db011754 100644 --- a/src/freedv_fsk.c +++ b/src/freedv_fsk.c @@ -612,9 +612,13 @@ int freedv_rx_fsk_ldpc_data(struct freedv *f, COMP demod_in[]) { for (int i = 0; i < 8; i++) seq |= f->rx_payload_bits[8 + i] << (7 - i); } - if (f->fsk_ldpc_state == 1) + if (f->fsk_ldpc_state == 1) { rx_status |= FREEDV_RX_SYNC; /* need this set before verbose logging fprintf() */ + f->sync = 1; + } else { + f->sync = 0; + } if (((f->verbose == 1) && (rx_status & FREEDV_RX_BITS)) || (f->verbose == 2)) { fprintf(stderr, @@ -628,7 +632,12 @@ int freedv_rx_fsk_ldpc_data(struct freedv *f, COMP demod_in[]) { } } else { /* set RX_SYNC flag even if we don't perform frame processing */ - if (f->fsk_ldpc_state == 1) rx_status |= FREEDV_RX_SYNC; + if (f->fsk_ldpc_state == 1) { + rx_status |= FREEDV_RX_SYNC; + f->sync = 1; + } else { + f->sync = 0; + } } return rx_status; diff --git a/src/interldpc.c b/src/interldpc.c index b94f7116..c05a48e8 100644 --- a/src/interldpc.c +++ b/src/interldpc.c @@ -136,17 +136,18 @@ void ldpc_encode_frame(struct LDPC *ldpc, int codeword[], for (j = 0; j < ldpc->NumberParityBits; i++, j++) codeword[i] = pbits[j]; } -void qpsk_modulate_frame(COMP tx_symbols[], int codeword[], int n) { +void psk_modulate_frame(int bps, COMP tx_symbols[], int codeword[], int n) { int s, i; - int dibit[2]; - complex float qpsk_symb; - - for (s = 0, i = 0; i < n; s += 2, i++) { - dibit[0] = codeword[s + 1] & 0x1; - dibit[1] = codeword[s] & 0x1; - qpsk_symb = qpsk_mod(dibit); - tx_symbols[i].real = crealf(qpsk_symb); - tx_symbols[i].imag = cimagf(qpsk_symb); + int bits[bps]; + complex float symb; + + assert((bps == 2) || (bps == 4)); + for (s = 0, i = 0; i < n; s += bps, i++) { + for (int b = 0; b < bps; b++) bits[b] = codeword[s + bps - 1 - b] & 0x1; + if (bps == 2) symb = qpsk_mod(bits); + if (bps == 4) symb = qam16_mod(bits); + tx_symbols[i].real = crealf(symb); + tx_symbols[i].imag = cimagf(symb); } } @@ -222,7 +223,8 @@ void ldpc_decode_frame(struct LDPC *ldpc, int *parityCheckCount, int *iter, of txt bits as this is done after we dissassemmble the frame */ int count_uncoded_errors(struct LDPC *ldpc, struct OFDM_CONFIG *config, - COMP codeword_symbols_de[], int crc16) { + COMP codeword_symbols_de[], float codeword_amps_de[], + int crc16) { int i, Nerrs; int coded_syms_per_frame = ldpc->coded_bits_per_frame / config->bps; @@ -250,12 +252,13 @@ int count_uncoded_errors(struct LDPC *ldpc, struct OFDM_CONFIG *config, ldpc_encode_frame(ldpc, test_codeword, tx_bits); for (i = 0; i < coded_syms_per_frame; i++) { - int bits[2]; + int bits[config->bps]; complex float s = codeword_symbols_de[i].real + I * codeword_symbols_de[i].imag; - qpsk_demod(s, bits); - rx_bits_raw[config->bps * i] = bits[1]; - rx_bits_raw[config->bps * i + 1] = bits[0]; + if (config->bps == 2) qpsk_demod(s, bits); + if (config->bps == 4) qam16_demod(s, bits, codeword_amps_de[i]); + for (int b = 0; b < config->bps; b++) + rx_bits_raw[config->bps * i + b] = bits[config->bps - 1 - b]; } Nerrs = 0; @@ -331,10 +334,11 @@ void ofdm_ldpc_interleave_tx(struct OFDM *ofdm, struct LDPC *ldpc, complex float tx_symbols[Nbitsperpacket / ofdm->bps]; ldpc_encode_frame(ldpc, codeword, tx_bits); - qpsk_modulate_frame(payload_symbols, codeword, Npayloadsymsperpacket); + psk_modulate_frame(ofdm->bps, payload_symbols, codeword, + Npayloadsymsperpacket); gp_interleave_comp(payload_symbols_inter, payload_symbols, Npayloadsymsperpacket); - ofdm_assemble_qpsk_modem_packet_symbols(ofdm, tx_symbols, - payload_symbols_inter, txt_bits); + ofdm_assemble_psk_modem_packet_symbols(ofdm, tx_symbols, + payload_symbols_inter, txt_bits); ofdm_txframe(ofdm, tx_sams, tx_symbols); } diff --git a/src/interldpc.h b/src/interldpc.h index 3b1d1caf..891033de 100644 --- a/src/interldpc.h +++ b/src/interldpc.h @@ -45,11 +45,12 @@ void set_data_bits_per_frame(struct LDPC *ldpc, int new_data_bits_per_frame); void ldpc_mode_specific_setup(struct OFDM *ofdm, struct LDPC *ldpc); void ldpc_encode_frame(struct LDPC *ldpc, int codeword[], unsigned char tx_bits_char[]); -void qpsk_modulate_frame(COMP tx_symbols[], int codeword[], int n); +void psk_modulate_frame(int bps, COMP tx_symbols[], int codeword[], int n); void ldpc_decode_frame(struct LDPC *ldpc, int *parityCheckCount, int *iter, uint8_t out_char[], float llr[]); int count_uncoded_errors(struct LDPC *ldpc, struct OFDM_CONFIG *config, - COMP codeword_symbols_de[], int crc16); + COMP codeword_symbols_de[], float codeword_amps_de[], + int crc16); int count_errors(uint8_t tx_bits[], uint8_t rx_bits[], int n); void count_errors_protection_mode(int protection_mode, int *pNerrs, int *pNcoded, uint8_t tx_bits[], diff --git a/src/mpdecode_core.c b/src/mpdecode_core.c index 53742551..06b3fa94 100644 --- a/src/mpdecode_core.c +++ b/src/mpdecode_core.c @@ -27,11 +27,21 @@ #define QPSK_CONSTELLATION_SIZE 4 #define QPSK_BITS_PER_SYMBOL 2 -/* QPSK constellation for symbol likelihood calculations */ +/* Constellations for symbol likelihood calculations */ -static COMP S_matrix[] = { +static COMP S_matrix_qpsk[] = { {1.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, -1.0f}, {-1.0f, 0.0f}}; +static COMP S_matrix_qam16[] = { + {4.4721e-01, 2.7756e-17}, {8.9443e-01, 4.4721e-01}, + {8.9443e-01, -4.4721e-01}, {1.3416e+00, 1.1102e-16}, + {2.7756e-17, -4.4721e-01}, {-4.4721e-01, -8.9443e-01}, + {4.4721e-01, -8.9443e-01}, {1.1102e-16, -1.3416e+00}, + {-2.7756e-17, 4.4721e-01}, {4.4721e-01, 8.9443e-01}, + {-4.4721e-01, 8.9443e-01}, {-1.1102e-16, 1.3416e+00}, + {-4.4721e-01, -2.7756e-17}, {-8.9443e-01, -4.4721e-01}, + {-8.9443e-01, 4.4721e-01}, {-1.3416e+00, -1.1102e-16}}; + // c_nodes will be an array of NumberParityBits of struct c_node // Each c_node contains an array of c_sub_node elements // This structure reduces the indexing calclations in SumProduct() @@ -565,17 +575,14 @@ void sd_to_llr(float llr[], float sd[], int n) { */ void Demod2D(float symbol_likelihood[], /* output, M*number_symbols */ - COMP r[], /* received QPSK symbols, number_symbols */ - COMP S_matrix[], /* constellation of size M */ - float EsNo, - float fading[], /* real fading values, number_symbols */ + COMP r[], /* received PSK symbols, number_symbols */ + COMP S_matrix[], /* constellation of size M */ + int M, float EsNo, + float fading[], /* real fading values, number_symbols */ float mean_amp, int number_symbols) { - int M = QPSK_CONSTELLATION_SIZE; int i, j; float tempsr, tempsi, Er, Ei; - /* determine output */ - for (i = 0; i < number_symbols; i++) { /* go through each received symbol */ for (j = 0; j < M; j++) { /* each postulated symbol */ tempsr = fading[i] * S_matrix[j].real / mean_amp; @@ -583,10 +590,7 @@ void Demod2D(float symbol_likelihood[], /* output, M*number_symbols */ Er = r[i].real / mean_amp - tempsr; Ei = r[i].imag / mean_amp - tempsi; symbol_likelihood[i * M + j] = -EsNo * (Er * Er + Ei * Ei); - // printf("symbol_likelihood[%d][%d] = %f\n", - // i,j,symbol_likelihood[i*M+j]); } - // exit(0); } } @@ -633,20 +637,30 @@ void Somap(float bit_likelihood[], /* number_bits, bps*number_symbols */ } } -void symbols_to_llrs(float llr[], COMP rx_qpsk_symbols[], float rx_amps[], - float EsNo, float mean_amp, int nsyms) { +void symbols_to_llrs(float llr[], COMP rx_psk_symbols[], float rx_amps[], + float EsNo, float mean_amp, int bps, int nsyms) { int i; - - float symbol_likelihood[nsyms * QPSK_CONSTELLATION_SIZE]; - float bit_likelihood[nsyms * QPSK_BITS_PER_SYMBOL]; - - Demod2D(symbol_likelihood, rx_qpsk_symbols, S_matrix, EsNo, rx_amps, mean_amp, - nsyms); - Somap(bit_likelihood, symbol_likelihood, QPSK_CONSTELLATION_SIZE, - QPSK_BITS_PER_SYMBOL, nsyms); - for (i = 0; i < nsyms * QPSK_BITS_PER_SYMBOL; i++) { + int constellation_points = 1 << bps; + size_t symbol_likelihood_size = (size_t)nsyms * constellation_points; + size_t bit_likelihood_size = (size_t)nsyms * bps; + float *symbol_likelihood = MALLOC(symbol_likelihood_size * sizeof(float)); + float *bit_likelihood = MALLOC(bit_likelihood_size * sizeof(float)); + + COMP *S_matrix; + assert(symbol_likelihood != NULL); + assert(bit_likelihood != NULL); + assert((bps == 2) || (bps == 4)); + if (bps == 2) S_matrix = S_matrix_qpsk; + if (bps == 4) S_matrix = S_matrix_qam16; + + Demod2D(symbol_likelihood, rx_psk_symbols, S_matrix, constellation_points, + EsNo, rx_amps, mean_amp, nsyms); + Somap(bit_likelihood, symbol_likelihood, constellation_points, bps, nsyms); + for (i = 0; i < nsyms * bps; i++) { llr[i] = -bit_likelihood[i]; } + FREE(bit_likelihood); + FREE(symbol_likelihood); } /* diff --git a/src/mpdecode_core.h b/src/mpdecode_core.h index 95ead460..d36a1f00 100644 --- a/src/mpdecode_core.h +++ b/src/mpdecode_core.h @@ -47,12 +47,12 @@ int run_ldpc_decoder(struct LDPC *ldpc, uint8_t out_char[], float input[], int *parityCheckCount); void sd_to_llr(float llr[], float sd[], int n); -void Demod2D(float symbol_likelihood[], COMP r[], COMP S_matrix[], float EsNo, - float fading[], float mean_amp, int number_symbols); +void Demod2D(float symbol_likelihood[], COMP r[], COMP S_matrix[], int M, + float EsNo, float fading[], float mean_amp, int number_symbols); void Somap(float bit_likelihood[], float symbol_likelihood[], int M, int bps, int number_symbols); void symbols_to_llrs(float llr[], COMP rx_qpsk_symbols[], float rx_amps[], - float EsNo, float mean_amp, int nsyms); + float EsNo, float mean_amp, int bps, int nsyms); void fsk_rx_filt_to_llrs(float llr[], float rx_filt[], float v_est, float SNRest, int M, int nsyms); diff --git a/src/ofdm.c b/src/ofdm.c index 953179fe..b2018810 100644 --- a/src/ofdm.c +++ b/src/ofdm.c @@ -77,10 +77,14 @@ static const complex float qpsk[] = {1.0f + 0.0f * I, 0.0f + 1.0f * I, 0.0f - 1.0f * I, -1.0f + 0.0f * I}; static const complex float qam16[] = { - 1.0f + 1.0f * I, 1.0f + 3.0f * I, 3.0f + 1.0f * I, 3.0f + 3.0f * I, - 1.0f - 1.0f * I, 1.0f - 3.0f * I, 3.0f - 1.0f * I, 3.0f - 3.0f * I, - -1.0f + 1.0f * I, -1.0f + 3.0f * I, -3.0f + 1.0f * I, -3.0f + 3.0f * I, - -1.0f - 1.0f * I, -1.0f - 3.0f * I, -3.0f - 1.0f * I, -3.0f - 3.0f * I}; + 4.4721e-01 + 2.7756e-17 * I, 8.9443e-01 + 4.4721e-01 * I, + 8.9443e-01 - 4.4721e-01 * I, 1.3416e+00 + 1.1102e-16 * I, + 2.7756e-17 - 4.4721e-01 * I, -4.4721e-01 - 8.9443e-01 * I, + 4.4721e-01 - 8.9443e-01 * I, 1.1102e-16 - 1.3416e+00 * I, + -2.7756e-17 + 4.4721e-01 * I, 4.4721e-01 + 8.9443e-01 * I, + -4.4721e-01 + 8.9443e-01 * I, -1.1102e-16 + 1.3416e+00 * I, + -4.4721e-01 - 2.7756e-17 * I, -8.9443e-01 - 4.4721e-01 * I, + -8.9443e-01 + 4.4721e-01 * I, -1.3416e+00 - 1.1102e-16 * I}; /* * These pilots are compatible with Octave version @@ -123,10 +127,13 @@ complex float qam16_mod(int *bits) { return qam16[(bits[3] << 3) | (bits[2] << 2) | (bits[1] << 1) | bits[0]]; } -void qam16_demod(complex float symbol, int *bits) { +void qam16_demod(complex float symbol, int *bits, float amp_est) { float dist[16]; int i; + amp_est += 1E-12; // prevent /0 errors + symbol /= amp_est; + for (i = 0; i < 16; i++) { dist[i] = cnormf(symbol - qam16[i]); } @@ -190,6 +197,7 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG *config) { ofdm->state_machine = "voice1"; ofdm->edge_pilots = 1; ofdm->codename = "HRA_112_112"; + ofdm->EsNodB = 3.0; ofdm->amp_est_mode = 0; ofdm->tx_bpf_en = true; ofdm->tx_bpf_proto = filtP650S900; @@ -226,6 +234,7 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG *config) { ofdm->state_machine = config->state_machine; ofdm->edge_pilots = config->edge_pilots; ofdm->codename = config->codename; + ofdm->EsNodB = config->EsNodB; ofdm->amp_est_mode = config->amp_est_mode; ofdm->tx_bpf_en = config->tx_bpf_en; ofdm->tx_bpf_proto = config->tx_bpf_proto; @@ -277,6 +286,7 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG *config) { ofdm->config.state_machine = ofdm->state_machine; ofdm->config.edge_pilots = ofdm->edge_pilots; ofdm->config.codename = ofdm->codename; + ofdm->config.EsNodB = ofdm->EsNodB; ofdm->config.amp_est_mode = ofdm->amp_est_mode; ofdm->config.tx_bpf_en = ofdm->tx_bpf_en; ofdm->config.tx_bpf_proto = ofdm->tx_bpf_proto; @@ -470,13 +480,12 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG *config) { ofdm->tx_uw_syms = MALLOC(sizeof(complex float) * (ofdm->nuwbits / ofdm->bps)); assert(ofdm->tx_uw_syms != NULL); - - assert(ofdm->bps == 2); // TODO generalise - for (int s = 0; s < (ofdm->nuwbits / ofdm->bps); s++) { - int dibit[2]; - dibit[1] = ofdm->tx_uw[2 * s]; - dibit[0] = ofdm->tx_uw[2 * s + 1]; - ofdm->tx_uw_syms[s] = qpsk_mod(dibit); + for (int b = 0, s = 0; b < ofdm->nuwbits; b += ofdm->bps, s++) { + int bits[ofdm->bps]; + for (int i = 0; i < ofdm->bps; i++) + bits[ofdm->bps - 1 - i] = ofdm->tx_uw[b + i]; + if (ofdm->bps == 2) ofdm->tx_uw_syms[s] = qpsk_mod(bits); + if (ofdm->bps == 4) ofdm->tx_uw_syms[s] = qam16_mod(bits); } /* sync state machine */ @@ -1178,25 +1187,15 @@ void ofdm_mod(struct OFDM *ofdm, COMP *result, const int *tx_bits) { complex float *tx = (complex float *)result; // complex has same memory layout complex float tx_sym_lin[length]; - int dibit[2]; - int s, i; - if (ofdm->bps == 1) { - /* Here we will have Nbitsperpacket / 1 */ - - for (s = 0; s < length; s++) { - tx_sym_lin[s] = (float)(2 * tx_bits[s] - 1); - } - } else if (ofdm->bps == 2) { - /* Here we will have Nbitsperpacket / 2 */ - - for (s = 0, i = 0; i < length; s += 2, i++) { - dibit[0] = tx_bits[s + 1] & 0x1; - dibit[1] = tx_bits[s] & 0x1; - - tx_sym_lin[i] = qpsk_mod(dibit); - } - } /* else if (ofdm->bps == 3) { } TODO */ + assert((ofdm->bps == 2) || (ofdm->bps == 4)); + for (int b = 0, s = 0; b < ofdm->bitsperpacket; b += ofdm->bps, s++) { + int bits[ofdm->bps]; + for (int i = 0; i < ofdm->bps; i++) + bits[ofdm->bps - 1 - i] = tx_bits[b + i] & 0x1; + if (ofdm->bps == 2) tx_sym_lin[s] = qpsk_mod(bits); + if (ofdm->bps == 4) tx_sym_lin[s] = qam16_mod(bits); + } ofdm_txframe(ofdm, tx, tx_sym_lin); } @@ -1866,7 +1865,7 @@ static void ofdm_demod_core(struct OFDM *ofdm, int *rx_bits) { * frame bit ordering correct */ complex float rx_corr; - int abit[2]; + int abit[ofdm->bps]; int bit_index = 0; float sum_amp = 0.0f; @@ -1913,17 +1912,10 @@ static void ofdm_demod_core(struct OFDM *ofdm, int *rx_bits) { ofdm->aphase_est_pilot_log[(rr * ofdm->nc) + (i - 1)] = aphase_est_pilot[i]; - if (ofdm->bps == 1) { - rx_bits[bit_index++] = crealf(rx_corr) > 0.0f; - } else if (ofdm->bps == 2) { - /* - * Only one final task, decode what quadrant the phase - * is in, and return the dibits - */ - qpsk_demod(rx_corr, abit); - rx_bits[bit_index++] = abit[1]; - rx_bits[bit_index++] = abit[0]; - } + if (ofdm->bps == 2) qpsk_demod(rx_corr, abit); + if (ofdm->bps == 4) qam16_demod(rx_corr, abit, aamp_est_pilot[i]); + for (int i = 0; i < ofdm->bps; i++) + rx_bits[bit_index++] = abit[ofdm->bps - 1 - i]; } } @@ -2380,9 +2372,9 @@ void ofdm_get_demod_stats(struct OFDM *ofdm, struct MODEM_STATS *stats, /* * Assemble packet of bits from UW, payload bits, and txt bits */ -void ofdm_assemble_qpsk_modem_packet(struct OFDM *ofdm, uint8_t modem_frame[], - uint8_t payload_bits[], - uint8_t txt_bits[]) { +void ofdm_assemble_psk_modem_packet(struct OFDM *ofdm, uint8_t modem_frame[], + uint8_t payload_bits[], + uint8_t txt_bits[]) { int s, t; int p = 0; @@ -2409,10 +2401,10 @@ void ofdm_assemble_qpsk_modem_packet(struct OFDM *ofdm, uint8_t modem_frame[], /* * Assemble packet of symbols from UW, payload symbols, and txt bits */ -void ofdm_assemble_qpsk_modem_packet_symbols(struct OFDM *ofdm, - complex float modem_packet[], - COMP payload_syms[], - uint8_t txt_bits[]) { +void ofdm_assemble_psk_modem_packet_symbols(struct OFDM *ofdm, + complex float modem_packet[], + COMP payload_syms[], + uint8_t txt_bits[]) { complex float *payload = (complex float *)&payload_syms[0]; // complex has same memory layout int Nsymsperpacket = ofdm->bitsperpacket / ofdm->bps; @@ -2424,10 +2416,6 @@ void ofdm_assemble_qpsk_modem_packet_symbols(struct OFDM *ofdm, int p = 0; int u = 0; - assert( - ofdm->bps == - 2); /* this only works for QPSK at this stage (e.g. modem packet mod) */ - for (s = 0; s < (Nsymsperpacket - Ntxtsyms); s++) { if ((u < Nuwsyms) && (s == ofdm->uw_ind_sym[u])) { modem_packet[s] = ofdm->tx_uw_syms[u++]; @@ -2439,6 +2427,9 @@ void ofdm_assemble_qpsk_modem_packet_symbols(struct OFDM *ofdm, assert(u == Nuwsyms); assert(p == (Nsymsperpacket - Nuwsyms - Ntxtsyms)); + /* txt bit insertion only works for QPSK at this stage, however QAM modes + * generally has no txt bits */ + assert((Ntxtsyms == 0) || (ofdm->bps == 2)); for (t = 0; s < Nsymsperpacket; s++, t += 2) { dibit[1] = txt_bits[t] & 0x1; dibit[0] = txt_bits[t + 1] & 0x1; @@ -2452,24 +2443,22 @@ void ofdm_assemble_qpsk_modem_packet_symbols(struct OFDM *ofdm, * Disassemble a received packet of symbols into UW bits and payload data * symbols */ -void ofdm_disassemble_qpsk_modem_packet(struct OFDM *ofdm, - complex float rx_syms[], - float rx_amps[], COMP codeword_syms[], - float codeword_amps[], - short txt_bits[]) { +void ofdm_disassemble_psk_modem_packet(struct OFDM *ofdm, + complex float rx_syms[], float rx_amps[], + COMP codeword_syms[], + float codeword_amps[], + short txt_bits[]) { complex float *codeword = (complex float *)&codeword_syms[0]; // complex has same memory layout int Nsymsperpacket = ofdm->bitsperpacket / ofdm->bps; int Nuwsyms = ofdm->nuwbits / ofdm->bps; int Ntxtsyms = ofdm->ntxtbits / ofdm->bps; - int dibit[2]; + int bits[ofdm->bps]; int s, t; int p = 0; int u = 0; - assert(ofdm->bps == 2); /* this only works for QPSK at this stage */ - for (s = 0; s < (Nsymsperpacket - Ntxtsyms); s++) { if ((u < Nuwsyms) && (s == ofdm->uw_ind_sym[u])) { u++; @@ -2483,11 +2472,12 @@ void ofdm_disassemble_qpsk_modem_packet(struct OFDM *ofdm, assert(u == Nuwsyms); assert(p == (Nsymsperpacket - Nuwsyms - Ntxtsyms)); - for (t = 0; s < Nsymsperpacket; s++, t += 2) { - qpsk_demod(rx_syms[s], dibit); + for (t = 0; s < Nsymsperpacket; s++, t += ofdm->bps) { + if (ofdm->bps == 2) qpsk_demod(rx_syms[s], bits); + if (ofdm->bps == 4) qam16_demod(rx_syms[s], bits, rx_amps[s]); - txt_bits[t] = dibit[1]; - txt_bits[t + 1] = dibit[0]; + for (int i = 0; i < ofdm->bps; i++) + txt_bits[t + i] = bits[ofdm->bps - 1 - i]; } assert(t == ofdm->ntxtbits); @@ -2497,7 +2487,7 @@ void ofdm_disassemble_qpsk_modem_packet(struct OFDM *ofdm, * Disassemble a received packet of symbols into UW bits and payload data * symbols */ -void ofdm_disassemble_qpsk_modem_packet_with_text_amps( +void ofdm_disassemble_psk_modem_packet_with_text_amps( struct OFDM *ofdm, complex float rx_syms[], float rx_amps[], COMP codeword_syms[], float codeword_amps[], short txt_bits[], int *textIndex) { @@ -2506,13 +2496,12 @@ void ofdm_disassemble_qpsk_modem_packet_with_text_amps( int Nsymsperpacket = ofdm->bitsperpacket / ofdm->bps; int Nuwsyms = ofdm->nuwbits / ofdm->bps; int Ntxtsyms = ofdm->ntxtbits / ofdm->bps; - int dibit[2]; + int bits[ofdm->bps]; int s, t; int p = 0; int u = 0; - assert(ofdm->bps == 2); /* this only works for QPSK at this stage */ assert(textIndex != NULL); for (s = 0; s < (Nsymsperpacket - Ntxtsyms); s++) { @@ -2529,11 +2518,12 @@ void ofdm_disassemble_qpsk_modem_packet_with_text_amps( assert(p == (Nsymsperpacket - Nuwsyms - Ntxtsyms)); *textIndex = s; - for (t = 0; s < Nsymsperpacket; s++, t += 2) { - qpsk_demod(rx_syms[s], dibit); + for (t = 0; s < Nsymsperpacket; s++, t += ofdm->bps) { + if (ofdm->bps == 2) qpsk_demod(rx_syms[s], bits); + if (ofdm->bps == 4) qam16_demod(rx_syms[s], bits, rx_amps[s]); - txt_bits[t] = dibit[1]; - txt_bits[t + 1] = dibit[0]; + for (int i = 0; i < ofdm->bps; i++) + txt_bits[t + i] = bits[ofdm->bps - 1 - i]; } assert(t == ofdm->ntxtbits); @@ -2546,17 +2536,15 @@ void ofdm_extract_uw(struct OFDM *ofdm, complex float rx_syms[], float rx_amps[], uint8_t rx_uw[]) { int Nsymsperframe = ofdm->bitsperframe / ofdm->bps; int Nuwsyms = ofdm->nuwbits / ofdm->bps; - int dibit[2]; int s, u; - assert(ofdm->bps == - 2); /* this only works for QPSK at this stage (e.g. UW demod) */ - for (s = 0, u = 0; s < Nsymsperframe * ofdm->nuwframes; s++) { if ((u < Nuwsyms) && (s == ofdm->uw_ind_sym[u])) { - qpsk_demod(rx_syms[s], dibit); - rx_uw[2 * u] = dibit[1]; - rx_uw[2 * u + 1] = dibit[0]; + int bits[ofdm->bps]; + if (ofdm->bps == 2) qpsk_demod(rx_syms[s], bits); + if (ofdm->bps == 4) qam16_demod(rx_syms[s], bits, rx_amps[s]); + for (int i = 0; i < ofdm->bps; i++) + rx_uw[ofdm->bps * u + i] = bits[ofdm->bps - 1 - i]; u++; } } diff --git a/src/ofdm_demod.c b/src/ofdm_demod.c index 80f01bd6..7548e46a 100644 --- a/src/ofdm_demod.c +++ b/src/ofdm_demod.c @@ -408,9 +408,7 @@ int main(int argc, char *argv[]) { else Ndiscard = 1; /* much longer packets, so discard thresh smaller */ - float EsNo = 3.0f; - - if (verbose == 2) fprintf(stderr, "Warning EsNo: %f hard coded\n", EsNo); + float EsNo = pow(10.0, ofdm->EsNodB / 10.0); /* More logging */ COMP payload_syms_log[NFRAMES][Npayloadsymsperpacket]; @@ -473,8 +471,8 @@ int main(int argc, char *argv[]) { /* we have received enough frames to make a complete packet .... */ /* extract payload symbols from packet */ - ofdm_disassemble_qpsk_modem_packet(ofdm, rx_syms, rx_amps, payload_syms, - payload_amps, txt_bits); + ofdm_disassemble_psk_modem_packet(ofdm, rx_syms, rx_amps, payload_syms, + payload_amps, txt_bits); if (ldpc_en) { assert((ofdm_nuwbits + ofdm_ntxtbits + Npayloadbitsperpacket) <= @@ -492,15 +490,15 @@ int main(int argc, char *argv[]) { uint8_t out_char[Npayloadbitsperpacket]; if (testframes == true) { - Nerrs_raw = - count_uncoded_errors(&ldpc, ofdm_config, payload_syms_de, 0); + Nerrs_raw = count_uncoded_errors( + &ldpc, ofdm_config, payload_syms_de, payload_amps_de, 0); Terrs += Nerrs_raw; Tbits += Npayloadbitsperpacket; /* not counting errors in txt bits */ } symbols_to_llrs(llr, payload_syms_de, payload_amps_de, EsNo, - ofdm->mean_amp, Npayloadsymsperpacket); + ofdm->mean_amp, ofdm->bps, Npayloadsymsperpacket); assert(Ndatabitsperpacket == ldpc.data_bits_per_frame); ldpc_decode_frame(&ldpc, &parityCheckCount, &iter, out_char, llr); @@ -524,11 +522,19 @@ int main(int argc, char *argv[]) { assert(Npayloadsymsperpacket * ofdm_config->bps == Npayloadbitsperpacket); for (i = 0; i < Npayloadsymsperpacket; i++) { - int bits[2]; + int bits[ofdm->bps]; complex float s = payload_syms[i].real + I * payload_syms[i].imag; - qpsk_demod(s, bits); - rx_bits_char[ofdm_config->bps * i] = bits[1]; - rx_bits_char[ofdm_config->bps * i + 1] = bits[0]; + if (ofdm->bps == 2) { + qpsk_demod(s, bits); + rx_bits_char[ofdm_config->bps * i] = bits[1]; + rx_bits_char[ofdm_config->bps * i + 1] = bits[0]; + } else if (ofdm->bps == 4) { + qam16_demod(s, bits, payload_amps[i]); + for (int b = 0; b < ofdm->bps; b++) + rx_bits_char[ofdm_config->bps * i + b] = bits[ofdm->bps - 1 - b]; + } else { + assert(0); + } } fwrite(rx_bits_char, sizeof(uint8_t), Npayloadbitsperpacket, fout); @@ -547,17 +553,16 @@ int main(int argc, char *argv[]) { memset(txt_bits, 0, ofdm_ntxtbits); uint8_t tx_bits[Nbitsperpacket]; ofdm_generate_payload_data_bits(payload_bits, Npayloadbitsperpacket); - ofdm_assemble_qpsk_modem_packet(ofdm, tx_bits, payload_bits, - txt_bits); + ofdm_assemble_psk_modem_packet(ofdm, tx_bits, payload_bits, txt_bits); /* count errors across UW, payload, txt bits */ int rx_bits[Nbitsperpacket]; - int dibit[2]; - assert(ofdm->bps == 2); /* this only works for QPSK at this stage */ + int bits[ofdm->bps]; for (int s = 0; s < Nsymsperpacket; s++) { - qpsk_demod(rx_syms[s], dibit); - rx_bits[2 * s] = dibit[1]; - rx_bits[2 * s + 1] = dibit[0]; + if (ofdm->bps == 2) qpsk_demod(rx_syms[s], bits); + if (ofdm->bps == 4) qam16_demod(rx_syms[s], bits, rx_amps[s]); + for (int i = 0; i < ofdm->bps; i++) + rx_bits[ofdm->bps * s + i] = bits[ofdm->bps - 1 - i]; } for (Nerrs_raw = 0, i = 0; i < Nbitsperpacket; i++) if (tx_bits[i] != rx_bits[i]) Nerrs_raw++; diff --git a/src/ofdm_internal.h b/src/ofdm_internal.h index 3cb91308..1131756d 100644 --- a/src/ofdm_internal.h +++ b/src/ofdm_internal.h @@ -46,7 +46,7 @@ extern "C" { #define TAU (2.0f * M_PI) #define ROT45 (M_PI / 4.0f) -#define MAX_UW_BITS 64 +#define MAX_UW_BITS 192 #define cmplx(value) (cosf(value) + sinf(value) * I) #define cmplxconj(value) (cosf(value) + sinf(value) * -I) @@ -110,6 +110,7 @@ struct OFDM_CONFIG { char *data_mode; float fmin; float fmax; + float EsNodB; /* EsNo est used for LDPC decoder */ }; struct OFDM { @@ -252,6 +253,7 @@ struct OFDM { detector */ char *codename; + float EsNodB; /* EsNo est used for LDPC decoder */ char *state_machine; }; @@ -260,19 +262,19 @@ struct OFDM { complex float qpsk_mod(int *); complex float qam16_mod(int *); void qpsk_demod(complex float, int *); -void qam16_demod(complex float, int *); +void qam16_demod(complex float, int *, float); void ofdm_txframe(struct OFDM *, complex float *, complex float[]); -void ofdm_assemble_qpsk_modem_packet(struct OFDM *, uint8_t[], uint8_t[], - uint8_t[]); -void ofdm_assemble_qpsk_modem_packet_symbols(struct OFDM *, complex float[], - COMP[], uint8_t[]); -void ofdm_disassemble_qpsk_modem_packet(struct OFDM *, complex float rx_syms[], - float rx_amps[], COMP[], float[], - short[]); -void ofdm_disassemble_qpsk_modem_packet_with_text_amps(struct OFDM *, - complex float rx_syms[], - float rx_amps[], COMP[], - float[], short[], int *); +void ofdm_assemble_psk_modem_packet(struct OFDM *, uint8_t[], uint8_t[], + uint8_t[]); +void ofdm_assemble_psk_modem_packet_symbols(struct OFDM *, complex float[], + COMP[], uint8_t[]); +void ofdm_disassemble_psk_modem_packet(struct OFDM *, complex float rx_syms[], + float rx_amps[], COMP[], float[], + short[]); +void ofdm_disassemble_psk_modem_packet_with_text_amps(struct OFDM *, + complex float rx_syms[], + float rx_amps[], COMP[], + float[], short[], int *); void ofdm_extract_uw(struct OFDM *ofdm, complex float rx_syms[], float rx_amps[], uint8_t rx_uw[]); void ofdm_rand(uint16_t[], int); diff --git a/src/ofdm_mod.c b/src/ofdm_mod.c index d4c495e8..23444f57 100644 --- a/src/ofdm_mod.c +++ b/src/ofdm_mod.c @@ -398,8 +398,7 @@ int main(int argc, char *argv[]) { /* assemble packet of bits then modulate */ uint8_t tx_bits_char[Nbitsperpacket]; - ofdm_assemble_qpsk_modem_packet(ofdm, tx_bits_char, data_bits, - txt_bits); + ofdm_assemble_psk_modem_packet(ofdm, tx_bits_char, data_bits, txt_bits); int tx_bits[Nbitsperpacket]; for (i = 0; i < Nbitsperpacket; i++) tx_bits[i] = tx_bits_char[i]; COMP tx_sams[Nsamperpacket]; diff --git a/src/ofdm_mode.c b/src/ofdm_mode.c index b6c8c369..7b5aab7e 100644 --- a/src/ofdm_mode.c +++ b/src/ofdm_mode.c @@ -42,6 +42,7 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) { config->state_machine = "voice1"; config->data_mode = ""; config->codename = "HRA_112_112"; + config->EsNodB = 3.0; config->clip_gain1 = 2.5; config->clip_gain2 = 0.8; config->clip_en = true; @@ -119,6 +120,36 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) { config->data_mode = "streaming"; config->tx_bpf_proto = NULL; config->tx_bpf_proto_n = 0; + } else if (strcmp(mode, "qam16c2") == 0) { + config->ns = 5; + config->np = 31; + config->tcp = 0.004; + config->ts = 0.016; + config->nc = 33; + config->bps = 4; + config->txtbits = 0; + config->nuwbits = 42 * 4; + assert(config->nuwbits <= MAX_UW_BITS); + config->bad_uw_errors = 50; + config->ftwindowwidth = 80; + config->state_machine = "data"; + config->amp_est_mode = 1; + config->tx_bpf_en = false; + config->clip_en = false; + config->data_mode = "streaming"; + config->amp_scale = 135E3; + config->rx_bpf_en = false; + + uint8_t uw[] = {1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0}; + memset(config->tx_uw, 0, config->nuwbits); + memcpy(config->tx_uw, uw, sizeof(uw)); + memcpy(&config->tx_uw[config->nuwbits - sizeof(uw)], uw, sizeof(uw)); + + config->EsNodB = 10; + config->codename = "H_16200_9720"; + config->tx_bpf_proto = filtP1100S1300; + config->tx_bpf_proto_n = sizeof(filtP1100S1300) / sizeof(float); } else if (strcmp(mode, "datac0") == 0) { config->ns = 5; config->np = 4; diff --git a/src/reliable_text.c b/src/reliable_text.c index 8a5ba219..ff7c5c0e 100644 --- a/src/reliable_text.c +++ b/src/reliable_text.c @@ -182,7 +182,8 @@ static int reliable_text_ldpc_decode(reliable_text_impl_t* obj, char* dest) { float EsNo = 3.0; // note: constant from freedv_700.c symbols_to_llrs(llr, (COMP*)deinterleavedSyms, deinterleavedAmps, EsNo, - obj->fdv->ofdm->mean_amp, Npayloadsymsperpacket); + obj->fdv->ofdm->mean_amp, obj->fdv->ofdm->bps, + Npayloadsymsperpacket); } else { // Deinterlace the received bits. gp_deinterleave_bits(deinterleavedBits, src, LDPC_TOTAL_SIZE_BITS / 2); diff --git a/stm32/unittest/src/tst_ofdm_demod.c b/stm32/unittest/src/tst_ofdm_demod.c index 54cc027b..51978eac 100644 --- a/stm32/unittest/src/tst_ofdm_demod.c +++ b/stm32/unittest/src/tst_ofdm_demod.c @@ -249,7 +249,9 @@ int main(int argc, char *argv[]) { if (config_profile) PROFILE_SAMPLE_AND_LOG2(ofdm_demod_demod, " ofdm_demod_demod"); if (config_profile) PROFILE_SAMPLE(ofdm_demod_diss); ofdm_extract_uw(ofdm, ofdm->rx_np, ofdm->rx_amp, rx_uw); - ofdm_disassemble_qpsk_modem_packet(ofdm, ofdm->rx_np, ofdm->rx_amp, payload_syms, payload_amps, txt_bits); + ofdm_disassemble_psk_modem_packet(ofdm, ofdm->rx_np, ofdm->rx_amp, + payload_syms, payload_amps, + txt_bits); if (config_profile) PROFILE_SAMPLE_AND_LOG2(ofdm_demod_diss, " ofdm_demod_diss"); log_payload_syms_flag = 1; @@ -290,12 +292,16 @@ int main(int argc, char *argv[]) { uint8_t out_char[coded_bits_per_frame]; if (config_testframes) { - Terrs += count_uncoded_errors(&ldpc, ofdm_config, codeword_symbols_de, 0); + Terrs += count_uncoded_errors( + &ldpc, ofdm_config, codeword_symbols_de, + codeword_amps_de, 0); Tbits += coded_bits_per_frame; } - symbols_to_llrs(llr, codeword_symbols_de, codeword_amps_de, - EsNo, ofdm->mean_amp, coded_syms_per_frame); + symbols_to_llrs(llr, codeword_symbols_de, + codeword_amps_de, EsNo, + ofdm->mean_amp, ofdm->bps, + coded_syms_per_frame); iter = run_ldpc_decoder(&ldpc, out_char, llr, &parityCheckCount); //fprintf(stderr,"iter: %d pcc: %d\n", iter, parityCheckCount); @@ -313,7 +319,9 @@ int main(int argc, char *argv[]) { fwrite(out_char, sizeof(char), data_bits_per_frame, fout); } else { /* lpdc_en == 0, external LDPC decoder, so output LLRs */ - symbols_to_llrs(llr, codeword_symbols_de, codeword_amps_de, EsNo, ofdm->mean_amp, coded_syms_per_frame); + symbols_to_llrs(llr, codeword_symbols_de, codeword_amps_de, + EsNo, ofdm->mean_amp, ofdm->bps, + coded_syms_per_frame); fwrite(llr, sizeof(double), coded_bits_per_frame, fout); } } else { // !llrs_en (or ldpc_en) @@ -356,7 +364,8 @@ int main(int argc, char *argv[]) { txt_bits[i] = 0; } - ofdm_assemble_qpsk_modem_packet(ofdm, tx_bits, payload_bits, txt_bits); + ofdm_assemble_psk_modem_packet(ofdm, tx_bits, payload_bits, + txt_bits); Nerrs = 0; for(i=0; irx_amp[(ofdm_nuwbits + ofdm_ntxtbits) / ofdm_bps]; - Demod2D(symbol_likelihood, ldpc_codeword_symbols, S_matrix, EsNo, - ldpc_codeword_symbol_amps, ofdm->mean_amp, + Demod2D(symbol_likelihood, ldpc_codeword_symbols, S_matrix, 1 << ofdm_bps, + EsNo, ldpc_codeword_symbol_amps, ofdm->mean_amp, CODED_BITSPERFRAME / ofdm_bps); Somap(bit_likelihood, symbol_likelihood, 1 << ofdm_bps, ofdm_bps, CODED_BITSPERFRAME / ofdm_bps); diff --git a/unittest/tqam16.c b/unittest/tqam16.c index 9e4c8d02..08c03054 100644 --- a/unittest/tqam16.c +++ b/unittest/tqam16.c @@ -19,8 +19,8 @@ int main(void) { int tx_bits[4], rx_bits[4]; for (int i = 0; i < 4; i++) tx_bits[i] = (c >> (3 - i)) & 0x1; complex float symbol = qam16_mod(tx_bits); - qam16_demod(symbol, rx_bits); - if (memcmp(tx_bits, rx_bits, 4)) { + qam16_demod(symbol, rx_bits, 1.0); + if (memcmp(tx_bits, rx_bits, sizeof(tx_bits))) { fprintf(stderr, "FAIL on %d!\ntx_bits: ", c); for (int i = 0; i < 4; i++) fprintf(stderr, "%d ", tx_bits[i]); fprintf(stderr, "%f %f\nrx_bits: ", creal(symbol), cimag(symbol));