From a56466c08fb1b911c7658b647928174858c5c437 Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Thu, 25 Jan 2024 10:33:13 +0100 Subject: [PATCH] added support for multiple input and output files --- Makefile | 4 +- README.md | 6 +- decode.cc | 480 ++++++++++++++++++++++++++---------------------------- encode.cc | 125 +++++++------- 4 files changed, 306 insertions(+), 309 deletions(-) diff --git a/Makefile b/Makefile index 31e6cb9..a41d32b 100644 --- a/Makefile +++ b/Makefile @@ -14,8 +14,8 @@ CXX = clang++ -stdlib=libc++ -march=native all: encode decode test: encode decode - $(QEMU) ./encode encoded.wav 8000 8 1 /dev/urandom 25 - $(QEMU) ./decode /dev/null encoded.wav + $(QEMU) ./encode audio.wav 8000 8 1 1500 25 ANONYMOUS /dev/urandom + $(QEMU) ./decode audio.wav /dev/null encode: encode.cc $(CXX) $(CXXFLAGS) $< -o $@ diff --git a/README.md b/README.md index 5309cee..3cff464 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ dd if=/dev/urandom of=uncoded.dat bs=1 count=256 Encode file ```uncoded.dat``` to ```encoded.wav``` [WAV](https://en.wikipedia.org/wiki/WAV) audio file with 8000 Hz sample rate, 16 bits and only 1 (real) channel: ``` -./encode encoded.wav 8000 16 1 uncoded.dat 23 +./encode encoded.wav 8000 16 1 1500 23 CALLSIGN uncoded.dat ``` Start recording to ```recorded.wav``` audio file and stop after 5 seconds: @@ -30,7 +30,7 @@ aplay encoded.wav Decode ```recorded.wav``` audio file to ```decoded.dat``` file: ``` -./decode decoded.dat recorded.wav +./decode recorded.wav decoded.dat ``` Compare original ```uncoded.dat``` with ```decoded.dat```: @@ -61,7 +61,7 @@ Prerequisite: [disorders](https://github.com/aicodix/disorders) Encode ```uncoded.dat``` to [analytic](https://en.wikipedia.org/wiki/Analytic_signal) audio signal, add [multipath](https://en.wikipedia.org/wiki/Multipath_propagation), [CFO, SFO](https://en.wikipedia.org/wiki/Carrier_frequency_offset), [AWGN](https://en.wikipedia.org/wiki/Additive_white_Gaussian_noise), decode and compare: ``` -./encode - 8000 16 2 uncoded.dat 23 | ../disorders/multipath - - ../disorders/multipath.txt 10 | ../disorders/cfo - - 234.567 | ../disorders/sfo - - 147 | ../disorders/awgn - - -30 | ./decode - - | diff -q -s uncoded.dat - +./encode - 8000 16 2 1500 23 CALLSIGN uncoded.dat | ../disorders/multipath - - ../disorders/multipath.txt 10 | ../disorders/cfo - - 234.567 | ../disorders/sfo - - 147 | ../disorders/awgn - - -30 | ./decode - - | diff -q -s uncoded.dat - ``` ### Reading diff --git a/decode.cc b/decode.cc index 5747b30..dba0b66 100644 --- a/decode.cc +++ b/decode.cc @@ -178,6 +178,7 @@ struct Decoder static const int extended_len = symbol_len + guard_len; static const int code_max = 14; static const int bits_max = 1 << code_max; + static const int data_max = 1024; static const int cols_max = 273 + 16; static const int rows_max = 32; static const int cons_max = cols_max * rows_max; @@ -203,6 +204,7 @@ struct Decoder CODE::ReverseFisherYatesShuffle<4096> shuffle_4096; CODE::ReverseFisherYatesShuffle<8192> shuffle_8192; CODE::ReverseFisherYatesShuffle<16384> shuffle_16384; + uint8_t output_data[data_max]; int8_t genmat[255*71]; mesg_type mesg[bits_max]; code_type code[bits_max]; @@ -299,7 +301,7 @@ struct Decoder tmp = hilbert(blockdc(tmp.real())); return input_hist(tmp); } - Decoder(uint8_t *out, int *len, DSP::ReadPCM *pcm, int skip_count) : + Decoder(DSP::ReadPCM *pcm, const char *const *output_names, int output_count) : pcm(pcm), correlator(mls0_seq()), crc0(0xA8F4), crc1(0x8F6E37A0) { CODE::BoseChaudhuriHocquenghemGenerator<255, 71>::matrix(genmat, true, { @@ -313,9 +315,8 @@ struct Decoder blockdc.samples(filter_len); DSP::Phasor osc; const cmplx *buf; - bool okay; - do { - okay = false; + int output_index = 0; + while (output_index < output_count) { do { if (!pcm->good()) return; @@ -371,258 +372,264 @@ struct Decoder base37_decoder(call_sign, md>>8, 9); call_sign[9] = 0; std::cerr << "call sign: " << call_sign << std::endl; - okay = true; - } while (skip_count--); + if (!oper_mode) + continue; + int parity_stride = 0; + int first_parity = 0; + int data_bits = 0; + int cons_rows = 0; + int comb_cols = 0; + int code_cols = 0; + switch (oper_mode) { + case 23: + mod_bits = 2; + cons_rows = 8; + comb_cols = 0; + code_order = 12; + code_cols = 256; + data_bits = 2048; + parity_stride = 31; + first_parity = 3; + frozen_bits = frozen_4096_2147; + break; + case 24: + mod_bits = 2; + cons_rows = 16; + comb_cols = 0; + code_order = 13; + code_cols = 256; + data_bits = 4096; + parity_stride = 31; + first_parity = 5; + frozen_bits = frozen_8192_4261; + break; + case 25: + mod_bits = 2; + cons_rows = 32; + comb_cols = 0; + code_order = 14; + code_cols = 256; + data_bits = 8192; + parity_stride = 31; + first_parity = 9; + frozen_bits = frozen_16384_8489; + break; + case 26: + mod_bits = 4; + cons_rows = 4; + comb_cols = 8; + code_order = 12; + code_cols = 256; + data_bits = 2048; + parity_stride = 31; + first_parity = 3; + frozen_bits = frozen_4096_2147; + break; + case 27: + mod_bits = 4; + cons_rows = 8; + comb_cols = 8; + code_order = 13; + code_cols = 256; + data_bits = 4096; + parity_stride = 31; + first_parity = 5; + frozen_bits = frozen_8192_4261; + break; + case 28: + mod_bits = 4; + cons_rows = 16; + comb_cols = 8; + code_order = 14; + code_cols = 256; + data_bits = 8192; + parity_stride = 31; + first_parity = 9; + frozen_bits = frozen_16384_8489; + break; + case 29: + mod_bits = 6; + cons_rows = 5; + comb_cols = 16; + code_order = 13; + code_cols = 273; + data_bits = 4096; + parity_stride = 31; + first_parity = 5; + frozen_bits = frozen_8192_4261; + break; + case 30: + mod_bits = 6; + cons_rows = 10; + comb_cols = 16; + code_order = 14; + code_cols = 273; + data_bits = 8192; + parity_stride = 31; + first_parity = 9; + frozen_bits = frozen_16384_8489; + break; + default: + return; + } + int data_bytes = data_bits / 8; + int cons_cols = code_cols + comb_cols; + int comb_dist = comb_cols ? cons_cols / comb_cols : 1; + int comb_off = comb_cols ? comb_dist / 2 : 1; + int code_off = - cons_cols / 2; - *len = 0; - if (!okay || !oper_mode) - return; - - int parity_stride = 0; - int first_parity = 0; - int data_bits = 0; - int cons_rows = 0; - int comb_cols = 0; - int code_cols = 0; - switch (oper_mode) { - case 23: - mod_bits = 2; - cons_rows = 8; - comb_cols = 0; - code_order = 12; - code_cols = 256; - data_bits = 2048; - parity_stride = 31; - first_parity = 3; - frozen_bits = frozen_4096_2147; - break; - case 24: - mod_bits = 2; - cons_rows = 16; - comb_cols = 0; - code_order = 13; - code_cols = 256; - data_bits = 4096; - parity_stride = 31; - first_parity = 5; - frozen_bits = frozen_8192_4261; - break; - case 25: - mod_bits = 2; - cons_rows = 32; - comb_cols = 0; - code_order = 14; - code_cols = 256; - data_bits = 8192; - parity_stride = 31; - first_parity = 9; - frozen_bits = frozen_16384_8489; - break; - case 26: - mod_bits = 4; - cons_rows = 4; - comb_cols = 8; - code_order = 12; - code_cols = 256; - data_bits = 2048; - parity_stride = 31; - first_parity = 3; - frozen_bits = frozen_4096_2147; - break; - case 27: - mod_bits = 4; - cons_rows = 8; - comb_cols = 8; - code_order = 13; - code_cols = 256; - data_bits = 4096; - parity_stride = 31; - first_parity = 5; - frozen_bits = frozen_8192_4261; - break; - case 28: - mod_bits = 4; - cons_rows = 16; - comb_cols = 8; - code_order = 14; - code_cols = 256; - data_bits = 8192; - parity_stride = 31; - first_parity = 9; - frozen_bits = frozen_16384_8489; - break; - case 29: - mod_bits = 6; - cons_rows = 5; - comb_cols = 16; - code_order = 13; - code_cols = 273; - data_bits = 4096; - parity_stride = 31; - first_parity = 5; - frozen_bits = frozen_8192_4261; - break; - case 30: - mod_bits = 6; - cons_rows = 10; - comb_cols = 16; - code_order = 14; - code_cols = 273; - data_bits = 8192; - parity_stride = 31; - first_parity = 9; - frozen_bits = frozen_16384_8489; - break; - default: - return; - } - int cons_cols = code_cols + comb_cols; - int comb_dist = comb_cols ? cons_cols / comb_cols : 1; - int comb_off = comb_cols ? comb_dist / 2 : 1; - int code_off = - cons_cols / 2; - - for (int i = 0; i < symbol_pos+extended_len; ++i) - buf = next_sample(); - for (int i = 0; i < symbol_len; ++i) - tdom[i] = buf[i] * osc(); - for (int i = 0; i < guard_len; ++i) - osc(); - fwd(fdom, tdom); - for (int i = 0; i < cons_cols; ++i) - prev[i] = fdom[bin(i+code_off)]; - std::cerr << "demod "; - CODE::MLS seq0(mls0_poly); - for (int j = 0; j < cons_rows; ++j) { - for (int i = 0; i < extended_len; ++i) - buf = next_sample(); + for (int i = 0; i < symbol_pos+extended_len; ++i) + correlator(buf = next_sample()); for (int i = 0; i < symbol_len; ++i) tdom[i] = buf[i] * osc(); for (int i = 0; i < guard_len; ++i) osc(); fwd(fdom, tdom); for (int i = 0; i < cons_cols; ++i) - cons[cons_cols*j+i] = demod_or_erase(fdom[bin(i+code_off)], prev[i]); - if (oper_mode > 25) { - for (int i = 0; i < comb_cols; ++i) - cons[cons_cols*j+comb_dist*i+comb_off] *= nrz(seq0()); - for (int i = 0; i < comb_cols; ++i) { - index[i] = code_off + comb_dist * i + comb_off; - phase[i] = arg(cons[cons_cols*j+comb_dist*i+comb_off]); + prev[i] = fdom[bin(i+code_off)]; + std::cerr << "demod "; + CODE::MLS seq0(mls0_poly); + for (int j = 0; j < cons_rows; ++j) { + for (int i = 0; i < extended_len; ++i) + correlator(buf = next_sample()); + for (int i = 0; i < symbol_len; ++i) + tdom[i] = buf[i] * osc(); + for (int i = 0; i < guard_len; ++i) + osc(); + fwd(fdom, tdom); + for (int i = 0; i < cons_cols; ++i) + cons[cons_cols*j+i] = demod_or_erase(fdom[bin(i+code_off)], prev[i]); + if (oper_mode > 25) { + for (int i = 0; i < comb_cols; ++i) + cons[cons_cols*j+comb_dist*i+comb_off] *= nrz(seq0()); + for (int i = 0; i < comb_cols; ++i) { + index[i] = code_off + comb_dist * i + comb_off; + phase[i] = arg(cons[cons_cols*j+comb_dist*i+comb_off]); + } + tse.compute(index, phase, comb_cols); + //std::cerr << "Theil-Sen slope = " << tse.slope() << std::endl; + //std::cerr << "Theil-Sen yint = " << tse.yint() << std::endl; + for (int i = 0; i < cons_cols; ++i) + cons[cons_cols*j+i] *= DSP::polar(1, -tse(i+code_off)); + for (int i = 0; i < cons_cols; ++i) + if (i % comb_dist == comb_off) + prev[i] = fdom[bin(i+code_off)]; + else + prev[i] *= DSP::polar(1, tse(i+code_off)); + for (int i = 0; i < cons_cols; ++i) { + index[i] = code_off + i; + if (i % comb_dist == comb_off) { + phase[i] = arg(cons[cons_cols*j+i]); + } else { + code_type tmp[mod_bits]; + mod_hard(tmp, cons[cons_cols*j+i]); + phase[i] = arg(cons[cons_cols*j+i] * conj(mod_map(tmp))); + } + } } - tse.compute(index, phase, comb_cols); + tse.compute(index, phase, cons_cols); //std::cerr << "Theil-Sen slope = " << tse.slope() << std::endl; //std::cerr << "Theil-Sen yint = " << tse.yint() << std::endl; for (int i = 0; i < cons_cols; ++i) cons[cons_cols*j+i] *= DSP::polar(1, -tse(i+code_off)); - for (int i = 0; i < cons_cols; ++i) - if (i % comb_dist == comb_off) + if (oper_mode > 25) { + for (int i = 0; i < cons_cols; ++i) + if (i % comb_dist != comb_off) + prev[i] *= DSP::polar(1, tse(i+code_off)); + } else { + for (int i = 0; i < cons_cols; ++i) prev[i] = fdom[bin(i+code_off)]; - else - prev[i] *= DSP::polar(1, tse(i+code_off)); - for (int i = 0; i < cons_cols; ++i) { - index[i] = code_off + i; - if (i % comb_dist == comb_off) { - phase[i] = arg(cons[cons_cols*j+i]); - } else { + } + std::cerr << "."; + } + std::cerr << " done" << std::endl; + std::cerr << "Es/N0 (dB):"; + value sp = 0, np = 0; + for (int j = 0, k = 0; j < cons_rows; ++j) { + if (oper_mode > 25) { + for (int i = 0; i < comb_cols; ++i) { + cmplx hard(1, 0); + cmplx error = cons[cons_cols*j+comb_dist*i+comb_off] - hard; + sp += norm(hard); + np += norm(error); + } + } else { + for (int i = 0; i < cons_cols; ++i) { code_type tmp[mod_bits]; mod_hard(tmp, cons[cons_cols*j+i]); - phase[i] = arg(cons[cons_cols*j+i] * conj(mod_map(tmp))); + cmplx hard = mod_map(tmp); + cmplx error = cons[cons_cols*j+i] - hard; + sp += norm(hard); + np += norm(error); } } - } - tse.compute(index, phase, cons_cols); - //std::cerr << "Theil-Sen slope = " << tse.slope() << std::endl; - //std::cerr << "Theil-Sen yint = " << tse.yint() << std::endl; - for (int i = 0; i < cons_cols; ++i) - cons[cons_cols*j+i] *= DSP::polar(1, -tse(i+code_off)); - if (oper_mode > 25) { - for (int i = 0; i < cons_cols; ++i) - if (i % comb_dist != comb_off) - prev[i] *= DSP::polar(1, tse(i+code_off)); - } else { - for (int i = 0; i < cons_cols; ++i) - prev[i] = fdom[bin(i+code_off)]; - } - std::cerr << "."; - } - std::cerr << " done" << std::endl; - std::cerr << "Es/N0 (dB):"; - value sp = 0, np = 0; - for (int j = 0, k = 0; j < cons_rows; ++j) { - if (oper_mode > 25) { - for (int i = 0; i < comb_cols; ++i) { - cmplx hard(1, 0); - cmplx error = cons[cons_cols*j+comb_dist*i+comb_off] - hard; - sp += norm(hard); - np += norm(error); - } - } else { + value precision = sp / np; + // precision = 8; + value snr = DSP::decibel(precision); + std::cerr << " " << snr; for (int i = 0; i < cons_cols; ++i) { - code_type tmp[mod_bits]; - mod_hard(tmp, cons[cons_cols*j+i]); - cmplx hard = mod_map(tmp); - cmplx error = cons[cons_cols*j+i] - hard; - sp += norm(hard); - np += norm(error); + if (oper_mode > 25 && i % comb_dist == comb_off) + continue; + mod_soft(code+k, cons[cons_cols*j+i], precision); + k += mod_bits; } } - value precision = sp / np; - // precision = 8; - value snr = DSP::decibel(precision); - std::cerr << " " << snr; - for (int i = 0; i < cons_cols; ++i) { - if (oper_mode > 25 && i % comb_dist == comb_off) - continue; - mod_soft(code+k, cons[cons_cols*j+i], precision); - k += mod_bits; + std::cerr << std::endl; + crc_bits = data_bits + 32; + CODE::PolarHelper::PATH metric[mesg_type::SIZE]; + for (int i = code_cols * cons_rows * mod_bits; i < bits_max; ++i) + code[i] = 0; + shuffle(code); + polardec(metric, mesg, code, frozen_bits, code_order, parity_stride, first_parity); + int order[mesg_type::SIZE]; + for (int k = 0; k < mesg_type::SIZE; ++k) + order[k] = k; + std::sort(order, order+mesg_type::SIZE, [metric](int a, int b){ return metric[a] < metric[b]; }); + int best = -1; + for (int k = 0; k < mesg_type::SIZE; ++k) { + crc1.reset(); + for (int i = 0; i < crc_bits; ++i) + crc1(mesg[i].v[order[k]] < 0); + if (crc1() == 0) { + best = order[k]; + break; + } } - } - std::cerr << std::endl; - *len = data_bits / 8; - crc_bits = data_bits + 32; - CODE::PolarHelper::PATH metric[mesg_type::SIZE]; - for (int i = code_cols * cons_rows * mod_bits; i < bits_max; ++i) - code[i] = 0; - shuffle(code); - polardec(metric, mesg, code, frozen_bits, code_order, parity_stride, first_parity); - int order[mesg_type::SIZE]; - for (int k = 0; k < mesg_type::SIZE; ++k) - order[k] = k; - std::sort(order, order+mesg_type::SIZE, [metric](int a, int b){ return metric[a] < metric[b]; }); - int best = -1; - for (int k = 0; k < mesg_type::SIZE; ++k) { - crc1.reset(); - for (int i = 0; i < crc_bits; ++i) - crc1(mesg[i].v[order[k]] < 0); - if (crc1() == 0) { - best = order[k]; - break; + if (best < 0) { + std::cerr << "payload decoding error." << std::endl; + continue; } + for (int i = 0; i < data_bits; ++i) + CODE::set_le_bit(output_data, i, mesg[i].v[best] < 0); + + const char *output_name = output_names[output_index++]; + if (output_count == 1 && output_name[0] == '-' && output_name[1] == 0) + output_name = "/dev/stdout"; + std::ofstream output_file(output_name, std::ios::binary | std::ios::trunc); + if (output_file.bad()) { + std::cerr << "Couldn't open file \"" << output_name << "\" for writing." << std::endl; + continue; + } + CODE::Xorshift32 scrambler; + for (int i = 0; i < data_bytes; ++i) + output_data[i] ^= scrambler(); + for (int i = 0; i < data_bytes; ++i) + output_file.put(output_data[i]); } - if (best < 0) { - std::cerr << "payload decoding error." << std::endl; - *len = 0; - return; - } - for (int i = 0; i < data_bits; ++i) - CODE::set_le_bit(out, i, mesg[i].v[best] < 0); } }; int main(int argc, char **argv) { - if (argc < 3 || argc > 4) { - std::cerr << "usage: " << argv[0] << " OUTPUT INPUT [SKIP]" << std::endl; + if (argc < 3) { + std::cerr << "usage: " << argv[0] << " INPUT OUTPUT.." << std::endl; return 1; } typedef float value; typedef DSP::Complex cmplx; - const char *output_name = argv[1]; - if (output_name[0] == '-' && output_name[1] == 0) - output_name = "/dev/stdout"; - const char *input_name = argv[2]; + const char *input_name = argv[1]; if (input_name[0] == '-' && input_name[1] == 0) input_name = "/dev/stdin"; @@ -633,43 +640,24 @@ int main(int argc, char **argv) return 1; } - int skip_count = 0; - if (argc > 3) - skip_count = std::atoi(argv[3]); - - const int data_max = 1024; - uint8_t *output_data = new uint8_t[data_max]; - int data_len = 0; - + int output_count = argc - 2; switch (input_file.rate()) { case 8000: - delete new Decoder(output_data, &data_len, &input_file, skip_count); + delete new Decoder(&input_file, argv+2, output_count); break; case 16000: - delete new Decoder(output_data, &data_len, &input_file, skip_count); + delete new Decoder(&input_file, argv+2, output_count); break; case 44100: - delete new Decoder(output_data, &data_len, &input_file, skip_count); + delete new Decoder(&input_file, argv+2, output_count); break; case 48000: - delete new Decoder(output_data, &data_len, &input_file, skip_count); + delete new Decoder(&input_file, argv+2, output_count); break; default: std::cerr << "Unsupported sample rate." << std::endl; return 1; } - - std::ofstream output_file(output_name, std::ios::binary | std::ios::trunc); - if (output_file.bad()) { - std::cerr << "Couldn't open file \"" << output_name << "\" for writing." << std::endl; - return 1; - } - CODE::Xorshift32 scrambler; - for (int i = 0; i < data_len; ++i) - output_data[i] ^= scrambler(); - for (int i = 0; i < data_len; ++i) - output_file.put(output_data[i]); - delete []output_data; return 0; } diff --git a/encode.cc b/encode.cc index bec4172..c791904 100644 --- a/encode.cc +++ b/encode.cc @@ -32,6 +32,7 @@ struct Encoder static const int symbol_len = (1280 * rate) / 8000; static const int guard_len = symbol_len / 8; static const int bits_max = 16384; + static const int data_max = 1024; static const int cols_max = 273 + 16; static const int mls0_len = 127; static const int mls0_poly = 0b10001001; @@ -48,6 +49,7 @@ struct Encoder CODE::FisherYatesShuffle<4096> shuffle_4096; CODE::FisherYatesShuffle<8192> shuffle_8192; CODE::FisherYatesShuffle<16384> shuffle_16384; + uint8_t input_data[data_max]; code_type code[bits_max], mesg[bits_max]; cmplx fdom[symbol_len]; cmplx tdom[symbol_len]; @@ -239,7 +241,7 @@ struct Encoder break; } } - Encoder(DSP::WritePCM *pcm, const uint8_t *inp, int freq_off, uint64_t call_sign, int oper_mode) : + Encoder(DSP::WritePCM *pcm, const char *const *input_names, int input_count, int freq_off, uint64_t call_sign, int oper_mode) : pcm(pcm), crc0(0xA8F4), crc1(0x8F6E37A0), bchenc({ 0b100011101, 0b101110111, 0b111110011, 0b101101001, 0b110111101, 0b111100111, 0b100101011, 0b111010111, @@ -271,8 +273,8 @@ struct Encoder data_bits = 2048; parity_stride = 31; first_parity = 3; - reserved_tones = 0; frozen_bits = frozen_4096_2147; + reserved_tones = 0; break; case 24: mod_bits = 2; @@ -283,8 +285,8 @@ struct Encoder data_bits = 4096; parity_stride = 31; first_parity = 5; - reserved_tones = 0; frozen_bits = frozen_8192_4261; + reserved_tones = 0; break; case 25: mod_bits = 2; @@ -295,8 +297,8 @@ struct Encoder data_bits = 8192; parity_stride = 31; first_parity = 9; - reserved_tones = 0; frozen_bits = frozen_16384_8489; + reserved_tones = 0; break; case 26: mod_bits = 4; @@ -307,8 +309,8 @@ struct Encoder data_bits = 2048; parity_stride = 31; first_parity = 3; - reserved_tones = 8; frozen_bits = frozen_4096_2147; + reserved_tones = 8; break; case 27: mod_bits = 4; @@ -319,8 +321,8 @@ struct Encoder data_bits = 4096; parity_stride = 31; first_parity = 5; - reserved_tones = 8; frozen_bits = frozen_8192_4261; + reserved_tones = 8; break; case 28: mod_bits = 4; @@ -331,8 +333,8 @@ struct Encoder data_bits = 8192; parity_stride = 31; first_parity = 9; - reserved_tones = 8; frozen_bits = frozen_16384_8489; + reserved_tones = 8; break; case 29: mod_bits = 6; @@ -343,8 +345,8 @@ struct Encoder data_bits = 4096; parity_stride = 31; first_parity = 5; - reserved_tones = 15; frozen_bits = frozen_8192_4261; + reserved_tones = 15; break; case 30: mod_bits = 6; @@ -355,12 +357,13 @@ struct Encoder data_bits = 8192; parity_stride = 31; first_parity = 9; - reserved_tones = 15; frozen_bits = frozen_16384_8489; + reserved_tones = 15; break; default: return; } + int data_bytes = data_bits / 8; int offset = (freq_off * symbol_len) / rate; mls0_off = offset - mls0_len + 1; mls1_off = offset - mls1_len / 2; @@ -381,14 +384,31 @@ struct Encoder } papr_min = 1000, papr_max = -1000; pilot_block(); - schmidl_cox(); - meta_data((call_sign << 8) | oper_mode); - if (oper_mode > 0) { + if (!oper_mode) { + schmidl_cox(); + meta_data(call_sign << 8); + } + for (int input_index = 0; input_index < input_count; ++input_index) { + const char *input_name = input_names[input_index]; + if (input_count == 1 && input_name[0] == '-' && input_name[1] == 0) + input_name = "/dev/stdin"; + std::ifstream input_file(input_name, std::ios::binary); + if (input_file.bad()) { + std::cerr << "Couldn't open file \"" << input_name << "\" for reading." << std::endl; + continue; + } + for (int i = 0; i < data_bytes; ++i) + input_data[i] = std::max(input_file.get(), 0); + CODE::Xorshift32 scrambler; + for (int i = 0; i < data_bytes; ++i) + input_data[i] ^= scrambler(); + schmidl_cox(); + meta_data((call_sign << 8) | oper_mode); for (int i = 0; i < data_bits; ++i) - mesg[i] = nrz(CODE::get_le_bit(inp, i)); + mesg[i] = nrz(CODE::get_le_bit(input_data, i)); crc1.reset(); - for (int i = 0; i < data_bits / 8; ++i) - crc1(inp[i]); + for (int i = 0; i < data_bytes; ++i) + crc1(input_data[i]); for (int i = 0; i < 32; ++i) mesg[i+data_bits] = nrz((crc1()>>i)&1); polarenc(code, mesg, frozen_bits, code_order, parity_stride, first_parity); @@ -439,8 +459,8 @@ long long int base37_encoder(const char *str) int main(int argc, char **argv) { - if (argc < 7 || argc > 8) { - std::cerr << "usage: " << argv[0] << " OUTPUT RATE BITS CHANNELS INPUT MODE [CALLSIGN]" << std::endl; + if (argc < 8) { + std::cerr << "usage: " << argv[0] << " OUTPUT RATE BITS CHANNELS OFFSET MODE CALLSIGN INPUT.." << std::endl; return 1; } @@ -450,94 +470,83 @@ int main(int argc, char **argv) int output_rate = std::atoi(argv[2]); int output_bits = std::atoi(argv[3]); int output_chan = std::atoi(argv[4]); - const char *input_name = argv[5]; - if (input_name[0] == '-' && input_name[1] == 0) - input_name = "/dev/stdin"; + int freq_off = std::atoi(argv[5]); + if (freq_off % 50) { + std::cerr << "Frequency offset must be divisible by 50." << std::endl; + return 1; + } + int input_count = argc - 8; int oper_mode = std::atoi(argv[6]); - - long long int call_sign = base37_encoder("ANONYMOUS"); - if (argc >= 8) - call_sign = base37_encoder(argv[7]); - + if (!oper_mode != !input_count) { + std::cerr << "Using operation mode " << oper_mode << " but " << input_count << " input file" << (input_count == 1 ? "" : "s") << " provided." << std::endl; + return 1; + } + long long int call_sign = base37_encoder(argv[7]); if (call_sign <= 0 || call_sign >= 129961739795077L) { std::cerr << "Unsupported call sign." << std::endl; return 1; } - typedef float value; - typedef DSP::Complex cmplx; - - std::ifstream input_file(input_name, std::ios::binary); - if (input_file.bad()) { - std::cerr << "Couldn't open file \"" << input_name << "\" for reading." << std::endl; - return 1; - } - int data_bits; + int band_width; switch (oper_mode) { case 0: - data_bits = 0; + band_width = 1600; break; case 23: - data_bits = 2048; + band_width = 1600; break; case 24: - data_bits = 4096; + band_width = 1600; break; case 25: - data_bits = 8192; + band_width = 1600; break; case 26: - data_bits = 2048; + band_width = 1700; break; case 27: - data_bits = 4096; + band_width = 1700; break; case 28: - data_bits = 8192; + band_width = 1700; break; case 29: - data_bits = 4096; + band_width = 1900; break; case 30: - data_bits = 8192; + band_width = 1900; break; default: std::cerr << "Unsupported operation mode." << std::endl; return 1; } - uint8_t *input_data = nullptr; - if (oper_mode) { - int data_len = data_bits / 8; - input_data = new uint8_t[data_len]; - for (int i = 0; i < data_len; ++i) - input_data[i] = std::max(input_file.get(), 0); - CODE::Xorshift32 scrambler; - for (int i = 0; i < data_len; ++i) - input_data[i] ^= scrambler(); + 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; } - int freq_off = 1500; + typedef float value; + typedef DSP::Complex cmplx; DSP::WriteWAV output_file(output_name, output_rate, output_bits, output_chan); output_file.silence(output_rate); switch (output_rate) { case 8000: - delete new Encoder(&output_file, input_data, freq_off, call_sign, oper_mode); + delete new Encoder(&output_file, argv+8, input_count, freq_off, call_sign, oper_mode); break; case 16000: - delete new Encoder(&output_file, input_data, freq_off, call_sign, oper_mode); + delete new Encoder(&output_file, argv+8, input_count, freq_off, call_sign, oper_mode); break; case 44100: - delete new Encoder(&output_file, input_data, freq_off, call_sign, oper_mode); + delete new Encoder(&output_file, argv+8, input_count, freq_off, call_sign, oper_mode); break; case 48000: - delete new Encoder(&output_file, input_data, freq_off, call_sign, oper_mode); + delete new Encoder(&output_file, argv+8, input_count, freq_off, call_sign, oper_mode); break; default: std::cerr << "Unsupported sample rate." << std::endl; return 1; } output_file.silence(output_rate); - delete []input_data; return 0; }