diff --git a/decode.cc b/decode.cc index 43ff1c2..296de66 100644 --- a/decode.cc +++ b/decode.cc @@ -152,19 +152,21 @@ void base37_decoder(char *str, long long int val, int len) str[i] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[val%37]; } -template +template struct Decoder { typedef DSP::Const Const; - typedef PhaseShiftKeying<8, cmplx, int8_t> Mod; static const int symbol_len = (1280 * rate) / 8000; static const int filter_len = (((21 * rate) / 8000) & ~3) | 1; static const int guard_len = symbol_len / 8; static const int ldpc_bits = 64800; static const int bch_bits = ldpc_bits - 21600; static const int data_bits = bch_bits - 12 * 16; - static const int cons_cnt = ldpc_bits / Mod::BITS; - static const int rows_max = cons_cnt / cols_min; + static const int mod_min = 2; + static const int mod_max = 3; + static const int cons_max = ldpc_bits / mod_min; + static const int cols_min = 360; + static const int rows_max = cons_max / cols_min; static const int mls0_len = 127; static const int mls0_off = - mls0_len + 1; static const int mls0_poly = 0b10001001; @@ -192,11 +194,14 @@ struct Decoder int8_t genmat[255*71]; int8_t code[ldpc_bits], bint[ldpc_bits]; uint16_t erasures[24]; - cmplx head[symbol_len], tail[symbol_len], cons[cons_cnt]; + cmplx head[symbol_len], tail[symbol_len], cons[cons_max]; cmplx fdom[symbol_len], tdom[buffer_len], resam[buffer_len]; value phase[symbol_len/2]; value cfo_rad, sfo_rad; int symbol_pos; + int oper_mode; + int mod_bits; + int cons_cnt; static int bin(int carrier) { @@ -253,16 +258,54 @@ struct Decoder void deinterleave() { for (int i = 0; i < cons_cnt; ++i) - for (int k = 0; k < Mod::BITS; ++k) - code[cons_cnt*k+i] = bint[Mod::BITS*i+k]; + for (int k = 0; k < mod_bits; ++k) + code[cons_cnt*k+i] = bint[mod_bits*i+k]; } void interleave() { for (int i = 0; i < cons_cnt; ++i) - for (int k = 0; k < Mod::BITS; ++k) - bint[Mod::BITS*i+k] = code[cons_cnt*k+i]; + for (int k = 0; k < mod_bits; ++k) + bint[mod_bits*i+k] = code[cons_cnt*k+i]; } - Decoder(uint8_t *out, DSP::ReadPCM *pcm, int skip_count, int code_cols) : + cmplx mod_map(int8_t *b) + { + switch (oper_mode) { + case 2: + case 3: + return PhaseShiftKeying<8, cmplx, int8_t>::map(b); + case 4: + case 5: + return PhaseShiftKeying<4, cmplx, int8_t>::map(b); + } + return 0; + } + void mod_hard(int8_t *b, cmplx c) + { + switch (oper_mode) { + case 2: + case 3: + PhaseShiftKeying<8, cmplx, int8_t>::hard(b, c); + break; + case 4: + case 5: + PhaseShiftKeying<4, cmplx, int8_t>::hard(b, c); + break; + } + } + void mod_soft(int8_t *b, cmplx c, value precision) + { + switch (oper_mode) { + case 2: + case 3: + PhaseShiftKeying<8, cmplx, int8_t>::soft(b, c, precision); + break; + case 4: + case 5: + PhaseShiftKeying<4, cmplx, int8_t>::soft(b, c, precision); + break; + } + } + Decoder(uint8_t *out, DSP::ReadPCM *pcm, int skip_count) : pcm(pcm), resample(rate, (rate * 19) / 40, 2), correlator(mls0_seq()), crc0(0xA8F4) { CODE::BoseChaudhuriHocquenghemGenerator<255, 71>::matrix(genmat, true, { @@ -323,10 +366,31 @@ struct Decoder std::cerr << "header CRC error." << std::endl; return; } - if ((md&255) != 2) { - std::cerr << "operation mode unsupported." << std::endl; + oper_mode = md & 255; + int code_cols; + switch (oper_mode) { + case 2: + code_cols = 432; + mod_bits = 3; + break; + case 3: + code_cols = 400; + mod_bits = 3; + break; + case 4: + code_cols = 400; + mod_bits = 2; + break; + case 5: + code_cols = 360; + mod_bits = 2; + break; + default: + std::cerr << "operation mode " << oper_mode << " unsupported." << std::endl; return; } + cons_cnt = ldpc_bits / mod_bits; + std::cerr << "oper mode: " << oper_mode << std::endl; if ((md>>8) == 0 || (md>>8) >= 129961739795077L) { std::cerr << "call sign unsupported." << std::endl; return; @@ -374,9 +438,9 @@ struct Decoder if (1) { value sp = 0, np = 0; for (int i = 0; i < cons_cnt; ++i) { - int8_t tmp[Mod::BITS]; - Mod::hard(tmp, cons[i]); - cmplx hard = Mod::map(tmp); + int8_t tmp[mod_max]; + mod_hard(tmp, cons[i]); + cmplx hard = mod_map(tmp); cmplx error = cons[i] - hard; sp += norm(hard); np += norm(error); @@ -389,7 +453,7 @@ struct Decoder precision = 1 / (sigma * sigma); } for (int i = 0; i < cons_cnt; ++i) - Mod::soft(bint+Mod::BITS*i, cons[i], precision); + mod_soft(bint+mod_bits*i, cons[i], precision); deinterleave(); int count = ldpcdec(code, code + bch_bits); if (count < 0) @@ -398,10 +462,10 @@ struct Decoder interleave(); value sp = 0, np = 0; for (int i = 0; i < cons_cnt; ++i) { - int8_t tmp[Mod::BITS]; - for (int k = 0; k < Mod::BITS; ++k) - tmp[k] = nrz(bint[Mod::BITS*i+k] < 0); - cmplx hard = Mod::map(tmp); + int8_t tmp[mod_max]; + for (int k = 0; k < mod_bits; ++k) + tmp[k] = nrz(bint[mod_bits*i+k] < 0); + cmplx hard = mod_map(tmp); cmplx error = cons[i] - hard; sp += norm(hard); np += norm(error); @@ -462,21 +526,21 @@ int main(int argc, char **argv) if (argc > 3) skip_count = std::atoi(argv[3]); - const int code_len = 64800 / 8, code_cols = 432, cols_min = 360; + const int code_len = 64800 / 8; uint8_t *output_data = new uint8_t[code_len]; switch (input_file.rate()) { case 8000: - delete new Decoder(output_data, &input_file, skip_count, code_cols); + delete new Decoder(output_data, &input_file, skip_count); break; case 16000: - delete new Decoder(output_data, &input_file, skip_count, code_cols); + delete new Decoder(output_data, &input_file, skip_count); break; case 44100: - delete new Decoder(output_data, &input_file, skip_count, code_cols); + delete new Decoder(output_data, &input_file, skip_count); break; case 48000: - delete new Decoder(output_data, &input_file, skip_count, code_cols); + delete new Decoder(output_data, &input_file, skip_count); break; default: std::cerr << "Unsupported sample rate." << std::endl; diff --git a/encode.cc b/encode.cc index ecdf38a..30fc31a 100644 --- a/encode.cc +++ b/encode.cc @@ -25,13 +25,11 @@ Copyright 2021 Ahmet Inan template struct Encoder { - typedef PhaseShiftKeying<8, cmplx, int8_t> Mod; static const int symbol_len = (1280 * rate) / 8000; static const int guard_len = symbol_len / 8; static const int ldpc_bits = 64800; static const int bch_bits = ldpc_bits - 21600; static const int data_bits = bch_bits - 12 * 16; - static const int cons_cnt = ldpc_bits / Mod::BITS; static const int mls0_len = 127; static const int mls0_poly = 0b10001001; static const int mls1_len = 255; @@ -53,6 +51,9 @@ struct Encoder cmplx temp[symbol_len]; cmplx guard[guard_len]; cmplx papr_min, papr_max; + int oper_mode; + int mod_bits; + int cons_cnt; int code_cols; int code_rows; int code_off; @@ -170,13 +171,25 @@ struct Encoder fdom[bin(i+mls1_off)] *= nrz(seq4()); symbol(); } + cmplx mod_map(int8_t *b) + { + switch (oper_mode) { + case 2: + case 3: + return PhaseShiftKeying<8, cmplx, int8_t>::map(b); + case 4: + case 5: + return PhaseShiftKeying<4, cmplx, int8_t>::map(b); + } + return 0; + } void interleave() { for (int i = 0; i < cons_cnt; ++i) - for (int k = 0; k < Mod::BITS; ++k) - bint[Mod::BITS*i+k] = code[cons_cnt*k+i]; + for (int k = 0; k < mod_bits; ++k) + bint[mod_bits*i+k] = code[cons_cnt*k+i]; } - Encoder(DSP::WritePCM *pcm, uint8_t *inp, int freq_off, uint64_t call_sign, int code_cols) : + Encoder(DSP::WritePCM *pcm, uint8_t *inp, int freq_off, uint64_t call_sign, int oper_mode) : pcm(pcm), crc0(0xA8F4), bchenc0({ 0b100011101, 0b101110111, 0b111110011, 0b101101001, 0b110111101, 0b111100111, 0b100101011, 0b111010111, @@ -188,8 +201,30 @@ struct Encoder 0b10101101001010101, 0b10001111100101111, 0b11111011110110101, 0b11010111101100101, 0b10111001101100111, 0b10000111010100001, 0b10111010110100111, 0b10011101000101101, 0b10001101011100011}), - code_cols(code_cols), code_rows(cons_cnt / code_cols) + oper_mode(oper_mode) { + switch (oper_mode) { + case 2: + code_cols = 432; + mod_bits = 3; + break; + case 3: + code_cols = 400; + mod_bits = 3; + break; + case 4: + code_cols = 400; + mod_bits = 2; + break; + case 5: + code_cols = 360; + mod_bits = 2; + break; + default: + return; + } + cons_cnt = ldpc_bits / mod_bits; + code_rows = cons_cnt / code_cols; int offset = (freq_off * symbol_len) / rate; code_off = offset - code_cols / 2; mls0_off = offset - mls0_len + 1; @@ -197,7 +232,7 @@ struct Encoder papr_min = cmplx(1000, 1000), papr_max = cmplx(-1000, -1000); pilot_block(); schmidl_cox(); - meta_data((call_sign << 8) | 2); + meta_data((call_sign << 8) | oper_mode); pilot_block(); bchenc1(inp, inp+data_bits/8, data_bits); for (int i = 0; i < bch_bits; ++i) @@ -207,14 +242,14 @@ struct Encoder CODE::MLS seq3(mls3_poly), seq4(mls4_poly); for (int j = 0; j < code_rows; ++j) { for (int i = 0; i < code_cols; ++i) { - cmplx con = Mod::map(bint+Mod::BITS*(code_cols*j+i)); + cmplx con = mod_map(bint+mod_bits*(code_cols*j+i)); con = cmplx(con.real() * nrz(seq3()), con.imag() * nrz(seq4())); fdom[bin(i+code_off)] *= con; } symbol(); } schmidl_cox(); - meta_data((call_sign << 8) | 2); + meta_data((call_sign << 8) | oper_mode); pilot_block(); for (int i = 0; i < symbol_len; ++i) fdom[i] = 0; @@ -244,8 +279,8 @@ long long int base37_encoder(const char *str) int main(int argc, char **argv) { - if (argc < 6 || argc > 8) { - std::cerr << "usage: " << argv[0] << " OUTPUT RATE BITS CHANNELS INPUT [OFFSET] [CALLSIGN]" << std::endl; + if (argc < 6 || argc > 9) { + std::cerr << "usage: " << argv[0] << " OUTPUT RATE BITS CHANNELS INPUT [OFFSET] [CALLSIGN] [MODE]" << std::endl; return 1; } @@ -259,11 +294,6 @@ int main(int argc, char **argv) if (argc >= 7) freq_off = std::atoi(argv[6]); - if ((output_chan == 1 && freq_off < 1350) || freq_off < 1350 - output_rate / 2 || freq_off > output_rate / 2 - 1350) { - std::cerr << "Unsupported frequency offset." << std::endl; - return 1; - } - long long int call_sign = base37_encoder("ANONYMOUS"); if (argc >= 8) call_sign = base37_encoder(argv[7]); @@ -273,6 +303,35 @@ int main(int argc, char **argv) return 1; } + int oper_mode = 2; + if (argc >= 9) + oper_mode = std::atoi(argv[8]); + if (oper_mode < 2 || oper_mode > 5) { + std::cerr << "Unsupported operation mode." << std::endl; + return 1; + } + + int band_width; + switch (oper_mode) { + case 2: + band_width = 2700; + break; + case 3: + case 4: + band_width = 2500; + break; + case 5: + band_width = 2250; + break; + default: + return 1; + } + + if ((output_chan == 1 && freq_off < band_width / 2) || freq_off < band_width / 2 - output_rate / 2 || freq_off > output_rate / 2 - band_width / 2) { + std::cerr << "Unsupported frequency offset." << std::endl; + return 1; + } + typedef float value; typedef DSP::Complex cmplx; @@ -281,7 +340,7 @@ int main(int argc, char **argv) std::cerr << "Couldn't open file \"" << input_name << "\" for reading." << std::endl; return 1; } - const int code_len = 64800 / 8, code_cols = 432; + const int code_len = 64800 / 8; const int data_len = code_len - (12 * 16 + 21600) / 8; uint8_t *input_data = new uint8_t[code_len]; for (int i = 0; i < data_len; ++i) @@ -291,16 +350,16 @@ int main(int argc, char **argv) output_file.silence(output_rate); switch (output_rate) { case 8000: - delete new Encoder(&output_file, input_data, freq_off, call_sign, code_cols); + delete new Encoder(&output_file, input_data, freq_off, call_sign, oper_mode); break; case 16000: - delete new Encoder(&output_file, input_data, freq_off, call_sign, code_cols); + delete new Encoder(&output_file, input_data, freq_off, call_sign, oper_mode); break; case 44100: - delete new Encoder(&output_file, input_data, freq_off, call_sign, code_cols); + delete new Encoder(&output_file, input_data, freq_off, call_sign, oper_mode); break; case 48000: - delete new Encoder(&output_file, input_data, freq_off, call_sign, code_cols); + delete new Encoder(&output_file, input_data, freq_off, call_sign, oper_mode); break; default: std::cerr << "Unsupported sample rate." << std::endl;