From 2c05efe8cd745ce9bd8713f0fd6d0e7d6b0bfbfe Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Thu, 7 Aug 2025 00:14:22 +0200 Subject: [PATCH 01/15] preparing to play with partial transmit sequence --- common.hh | 11 ++++----- decode.cc | 45 +++++++++++++++---------------------- encode.cc | 67 +++++++++++++++++++++++++------------------------------ 3 files changed, 53 insertions(+), 70 deletions(-) diff --git a/common.hh b/common.hh index da36a1d..16cc2cd 100644 --- a/common.hh +++ b/common.hh @@ -8,7 +8,6 @@ Copyright 2025 Ahmet Inan #include "crc.hh" #include "polar_tables.hh" -#include "hadamard_encoder.hh" struct Common { @@ -22,15 +21,13 @@ struct Common static const int mls1_poly = 0x43; static const int mls2_poly = 0x163; static const int data_tones = 256; - static const int seed_tones = 64; - static const int tone_count = data_tones + seed_tones; + static const int pilot_tones = 64; + static const int tone_count = data_tones + pilot_tones; static const int block_length = 5; static const int block_skew = 3; - static const int first_seed = 4; + static const int first_pilot = 4; CODE::CRC crc0; CODE::CRC crc1; - CODE::HadamardEncoder<7> hadamard_encoder; - int8_t seed[seed_tones]; uint8_t data[data_max]; const uint32_t *frozen_bits; int mod_bits; @@ -39,7 +36,7 @@ struct Common int code_order; int oper_mode; int tone_off; - int seed_off; + int pilot_off; int symbol_count; Common() : crc0(0xA8F4), crc1(0x8F6E37A0) {} diff --git a/decode.cc b/decode.cc index fd31b76..339d878 100644 --- a/decode.cc +++ b/decode.cc @@ -29,7 +29,6 @@ namespace DSP { using std::abs; using std::min; using std::cos; using std::sin; #include "psk.hh" #include "qam.hh" #include "polar_list_decoder.hh" -#include "hadamard_decoder.hh" template struct Decoder : Common @@ -51,7 +50,6 @@ struct Decoder : Common DSP::BipBuffer input_hist; DSP::TheilSenEstimator tse; SchmidlCox correlator; - CODE::HadamardDecoder<7> hadamard_decoder; CODE::PolarListDecoder polar_decoder; mesg_type mesg[bits_max]; code_type code[bits_max], perm[bits_max]; @@ -278,12 +276,11 @@ struct Decoder : Common osc(); fwd(fdom, tdom); CODE::MLS seq1(mls1_poly); - auto clamp = [](int v){ return v < -127 ? -127 : v > 127 ? 127 : v; }; mod_bits = 1; oper_mode = -1; symbol_count = 0; for (int j = 0, k = 0; j < symbol_count + 1; ++j) { - seed_off = (block_skew * j + first_seed) % block_length; + pilot_off = (block_skew * j + first_pilot) % block_length; if (j) { for (int i = 0; i < extended_len; ++i) correlator(buf = next_sample()); @@ -295,44 +292,38 @@ struct Decoder : Common } for (int i = 0; i < tone_count; ++i) tone[i] = fdom[bin(i+tone_off)]; - for (int i = seed_off; i < tone_count; i += block_length) + for (int i = pilot_off; i < tone_count; i += block_length) tone[i] *= nrz(seq1()); for (int i = 0; i < tone_count; ++i) demod[i] = demod_or_erase(tone[i], chan[i]); - for (int i = 0; i < seed_tones; ++i) - seed[i] = clamp(std::nearbyint(127 * demod[i*block_length+seed_off].real())); - int seed_value = hadamard_decoder(seed); - if (seed_value < 0) { - std::cerr << "seed value damaged" << std::endl; + value pilot_sum = 0; + for (int i = 0; i < pilot_tones; ++i) + pilot_sum += demod[i*block_length+pilot_off].real(); + int pilot_phase = DSP::signum(pilot_sum); + if (std::abs(pilot_sum) < pilot_tones / 4) { + std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; } - hadamard_encoder(seed, seed_value); - for (int i = 0; i < seed_tones; ++i) { - tone[block_length*i+seed_off] *= seed[i]; - demod[block_length*i+seed_off] *= seed[i]; + for (int i = 0; i < tone_count; ++i) { + tone[i] *= pilot_phase; + demod[i] *= pilot_phase; } - for (int i = 0; i < seed_tones; ++i) { - index[i] = tone_off + block_length * i + seed_off; - phase[i] = arg(demod[block_length*i+seed_off]); + for (int i = 0; i < pilot_tones; ++i) { + index[i] = tone_off + block_length * i + pilot_off; + phase[i] = arg(demod[block_length*i+pilot_off]); } - tse.compute(index, phase, seed_tones); + tse.compute(index, phase, pilot_tones); //std::cerr << "Theil-Sen slope = " << tse.slope() << std::endl; //std::cerr << "Theil-Sen yint = " << tse.yint() << std::endl; for (int i = 0; i < tone_count; ++i) demod[i] *= DSP::polar(1, -tse(i+tone_off)); for (int i = 0; i < tone_count; ++i) chan[i] *= DSP::polar(1, tse(i+tone_off)); - if (seed_value) { - CODE::MLS seq(mls2_poly, seed_value); - for (int i = 0; i < tone_count; ++i) - if (i % block_length != seed_off) - demod[i] *= nrz(seq()); - } value sp = 0, np = 0; for (int i = 0, l = k; i < tone_count; ++i) { cmplx hard(1, 0); - if (i % block_length != seed_off) { + if (i % block_length != pilot_off) { int bits = mod_bits; if (mod_bits == 3 && l % 32 == 30) bits = 2; @@ -354,7 +345,7 @@ struct Decoder : Common snr[j] = precision; precision = std::min(precision, value(1023)); for (int i = 0; i < tone_count; ++i) { - if (i % block_length != seed_off) { + if (i % block_length != pilot_off) { int bits = mod_bits; if (mod_bits == 3 && k % 32 == 30) bits = 2; @@ -391,7 +382,7 @@ struct Decoder : Common correlator(buf = next_sample()); std::cerr << "oper mode: " << oper_mode << std::endl; } - for (int i = seed_off; i < tone_count; i += block_length) + for (int i = pilot_off; i < tone_count; i += block_length) chan[i] = DSP::lerp(chan[i], tone[i], value(0.5)); } if (oper_mode < 0) diff --git a/encode.cc b/encode.cc index fc97f63..4834563 100644 --- a/encode.cc +++ b/encode.cc @@ -38,11 +38,11 @@ struct Encoder : public Common code_type code[bits_max], perm[bits_max], mesg[bits_max], meta[data_tones]; cmplx fdom[symbol_len]; cmplx tdom[symbol_len]; + cmplx temp[symbol_len]; cmplx best[symbol_len]; cmplx kern[symbol_len]; cmplx guard[guard_len]; cmplx tone[tone_count]; - cmplx temp[tone_count]; value weight[guard_len]; value papr[symbols_max]; @@ -79,47 +79,42 @@ struct Encoder : public Common void symbol(int symbol_number) { value scale = value(0.5) / std::sqrt(value(tone_count)); - value best_papr = 1000; - for (int seed_value = 0; seed_value < 128; ++seed_value) { - for (int i = 0; i < tone_count; ++i) - temp[i] = tone[i]; - if (symbol_number >= 0) { - hadamard_encoder(seed, seed_value); - for (int i = 0; i < seed_tones; ++i) - temp[i*block_length+seed_off] *= seed[i]; - if (seed_value) { - CODE::MLS seq(mls2_poly, seed_value); - for (int i = 0; i < tone_count; ++i) - if (i % block_length != seed_off) - temp[i] *= nrz(seq()); - } - } + if (symbol_number < 0) { for (int i = 0; i < symbol_len; ++i) fdom[i] = 0; for (int i = 0; i < tone_count; ++i) - fdom[bin(i+tone_off)] = temp[i]; + fdom[bin(i+tone_off)] = tone[i]; bwd(tdom, fdom); for (int i = 0; i < symbol_len; ++i) tdom[i] *= scale; - if (symbol_number < 0) - break; - value peak = 0, mean = 0; - for (int i = 0; i < symbol_len; ++i) { - value power(norm(tdom[i])); - peak = std::max(peak, power); - mean += power; - } - mean /= symbol_len; - value cand_papr(peak / mean); - if (cand_papr < best_papr) { - best_papr = cand_papr; + } else { + for (int i = 0; i < symbol_len; ++i) + fdom[i] = 0; + for (int i = 0; i < tone_count; ++i) + fdom[bin(i+tone_off)] = tone[i]; + bwd(temp, fdom); + for (int i = 0; i < symbol_len; ++i) + temp[i] *= scale; + value best_papr = 1000; + for (value pilot_phase = -1; pilot_phase < 2; pilot_phase += 2) { for (int i = 0; i < symbol_len; ++i) - best[i] = tdom[i]; - if (cand_papr < 5) - break; + tdom[i] = pilot_phase * temp[i]; + value peak = 0, mean = 0; + for (int i = 0; i < symbol_len; ++i) { + value power(norm(tdom[i])); + peak = std::max(peak, power); + mean += power; + } + mean /= symbol_len; + value cand_papr(peak / mean); + if (cand_papr < best_papr) { + best_papr = cand_papr; + for (int i = 0; i < symbol_len; ++i) + best[i] = tdom[i]; + if (cand_papr < 5) + break; + } } - } - if (symbol_number >= 0) { for (int i = 0; i < symbol_len; ++i) tdom[i] = best[i]; papr[symbol_number] = best_papr; @@ -299,9 +294,9 @@ struct Encoder : public Common shuffle(perm, code, code_order); CODE::MLS seq1(mls1_poly); for (int j = 0, k = 0, m = 0; j < symbol_count + 1; ++j) { - seed_off = (block_skew * j + first_seed) % block_length; + pilot_off = (block_skew * j + first_pilot) % block_length; for (int i = 0; i < tone_count; ++i) { - if (i % block_length == seed_off) { + if (i % block_length == pilot_off) { tone[i] = nrz(seq1()); } else if (j) { int bits = mod_bits; From 64f01501cc76bf7b9e403ba2fc412471da256cdd Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Thu, 7 Aug 2025 00:50:10 +0200 Subject: [PATCH 02/15] do pts on even and odd subcarrier sets --- decode.cc | 29 +++++++++++++++++++++-------- encode.cc | 47 +++++++++++++++++++++++++++++------------------ 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/decode.cc b/decode.cc index 339d878..2b4fb30 100644 --- a/decode.cc +++ b/decode.cc @@ -296,18 +296,31 @@ struct Decoder : Common tone[i] *= nrz(seq1()); for (int i = 0; i < tone_count; ++i) demod[i] = demod_or_erase(tone[i], chan[i]); - value pilot_sum = 0; - for (int i = 0; i < pilot_tones; ++i) - pilot_sum += demod[i*block_length+pilot_off].real(); - int pilot_phase = DSP::signum(pilot_sum); - if (std::abs(pilot_sum) < pilot_tones / 4) { + value ptsa_sum = 0; + for (int i = 0; i < pilot_tones; i += 2) + ptsa_sum += demod[i*block_length+pilot_off].real(); + int ptsa_phase = DSP::signum(ptsa_sum); + if (std::abs(ptsa_sum) < pilot_tones / 4) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; } - for (int i = 0; i < tone_count; ++i) { - tone[i] *= pilot_phase; - demod[i] *= pilot_phase; + for (int i = 0; i < tone_count; i += 2) { + tone[i] *= ptsa_phase; + demod[i] *= ptsa_phase; + } + value ptsb_sum = 0; + for (int i = 1; i < pilot_tones; i += 2) + ptsb_sum += demod[i*block_length+pilot_off].real(); + int ptsb_phase = DSP::signum(ptsb_sum); + if (std::abs(ptsb_sum) < pilot_tones / 4) { + std::cerr << "pilot phase damaged" << std::endl; + oper_mode = -1; + break; + } + for (int i = 1; i < tone_count; i += 2) { + tone[i] *= ptsb_phase; + demod[i] *= ptsb_phase; } for (int i = 0; i < pilot_tones; ++i) { index[i] = tone_off + block_length * i + pilot_off; diff --git a/encode.cc b/encode.cc index 4834563..3f7051b 100644 --- a/encode.cc +++ b/encode.cc @@ -39,6 +39,8 @@ struct Encoder : public Common cmplx fdom[symbol_len]; cmplx tdom[symbol_len]; cmplx temp[symbol_len]; + cmplx ptsa[symbol_len]; + cmplx ptsb[symbol_len]; cmplx best[symbol_len]; cmplx kern[symbol_len]; cmplx guard[guard_len]; @@ -90,29 +92,38 @@ struct Encoder : public Common } else { for (int i = 0; i < symbol_len; ++i) fdom[i] = 0; - for (int i = 0; i < tone_count; ++i) + for (int i = 0; i < tone_count; i += 2) fdom[bin(i+tone_off)] = tone[i]; - bwd(temp, fdom); + bwd(ptsa, fdom); for (int i = 0; i < symbol_len; ++i) - temp[i] *= scale; + ptsa[i] *= scale; + for (int i = 0; i < symbol_len; ++i) + fdom[i] = 0; + for (int i = 1; i < tone_count; i += 2) + fdom[bin(i+tone_off)] = tone[i]; + bwd(ptsb, fdom); + for (int i = 0; i < symbol_len; ++i) + ptsb[i] *= scale; value best_papr = 1000; - for (value pilot_phase = -1; pilot_phase < 2; pilot_phase += 2) { + for (value ptsa_phase = -1; ptsa_phase < 2; ptsa_phase += 2) { for (int i = 0; i < symbol_len; ++i) - tdom[i] = pilot_phase * temp[i]; - value peak = 0, mean = 0; - for (int i = 0; i < symbol_len; ++i) { - value power(norm(tdom[i])); - peak = std::max(peak, power); - mean += power; - } - mean /= symbol_len; - value cand_papr(peak / mean); - if (cand_papr < best_papr) { - best_papr = cand_papr; + temp[i] = ptsa_phase * ptsa[i]; + for (value ptsb_phase = -1; ptsb_phase < 2; ptsb_phase += 2) { for (int i = 0; i < symbol_len; ++i) - best[i] = tdom[i]; - if (cand_papr < 5) - break; + tdom[i] = ptsb_phase * ptsb[i] + temp[i]; + value peak = 0, mean = 0; + for (int i = 0; i < symbol_len; ++i) { + value power(norm(tdom[i])); + peak = std::max(peak, power); + mean += power; + } + mean /= symbol_len; + value cand_papr(peak / mean); + if (cand_papr < best_papr) { + best_papr = cand_papr; + for (int i = 0; i < symbol_len; ++i) + best[i] = tdom[i]; + } } } for (int i = 0; i < symbol_len; ++i) From bfd4d726d6819ec2e251f5d0bc514664906b734b Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Thu, 7 Aug 2025 07:27:15 +0200 Subject: [PATCH 03/15] minor cleanup --- encode.cc | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/encode.cc b/encode.cc index 3f7051b..23b4450 100644 --- a/encode.cc +++ b/encode.cc @@ -38,10 +38,10 @@ struct Encoder : public Common code_type code[bits_max], perm[bits_max], mesg[bits_max], meta[data_tones]; cmplx fdom[symbol_len]; cmplx tdom[symbol_len]; - cmplx temp[symbol_len]; cmplx ptsa[symbol_len]; cmplx ptsb[symbol_len]; - cmplx best[symbol_len]; + cmplx tmpa[symbol_len]; + cmplx test[symbol_len]; cmplx kern[symbol_len]; cmplx guard[guard_len]; cmplx tone[tone_count]; @@ -107,28 +107,26 @@ struct Encoder : public Common value best_papr = 1000; for (value ptsa_phase = -1; ptsa_phase < 2; ptsa_phase += 2) { for (int i = 0; i < symbol_len; ++i) - temp[i] = ptsa_phase * ptsa[i]; + tmpa[i] = ptsa_phase * ptsa[i]; for (value ptsb_phase = -1; ptsb_phase < 2; ptsb_phase += 2) { for (int i = 0; i < symbol_len; ++i) - tdom[i] = ptsb_phase * ptsb[i] + temp[i]; + test[i] = ptsb_phase * ptsb[i] + tmpa[i]; value peak = 0, mean = 0; for (int i = 0; i < symbol_len; ++i) { - value power(norm(tdom[i])); + value power(norm(test[i])); peak = std::max(peak, power); mean += power; } mean /= symbol_len; - value cand_papr(peak / mean); - if (cand_papr < best_papr) { - best_papr = cand_papr; + value test_papr(peak / mean); + if (test_papr < best_papr) { + best_papr = test_papr; + papr[symbol_number] = best_papr; for (int i = 0; i < symbol_len; ++i) - best[i] = tdom[i]; + tdom[i] = test[i]; } } } - for (int i = 0; i < symbol_len; ++i) - tdom[i] = best[i]; - papr[symbol_number] = best_papr; } clipping_and_filtering(scale); if (symbol_number != -1) { From 83ba7eb43dd919797b7e1c682fe3b2691392b169 Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Thu, 7 Aug 2025 12:29:21 +0200 Subject: [PATCH 04/15] oops --- decode.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/decode.cc b/decode.cc index 2b4fb30..3a6bae8 100644 --- a/decode.cc +++ b/decode.cc @@ -297,7 +297,7 @@ struct Decoder : Common for (int i = 0; i < tone_count; ++i) demod[i] = demod_or_erase(tone[i], chan[i]); value ptsa_sum = 0; - for (int i = 0; i < pilot_tones; i += 2) + for (int i = pilot_off & 1; i < pilot_tones; i += 2) ptsa_sum += demod[i*block_length+pilot_off].real(); int ptsa_phase = DSP::signum(ptsa_sum); if (std::abs(ptsa_sum) < pilot_tones / 4) { @@ -310,7 +310,7 @@ struct Decoder : Common demod[i] *= ptsa_phase; } value ptsb_sum = 0; - for (int i = 1; i < pilot_tones; i += 2) + for (int i = !(pilot_off & 1); i < pilot_tones; i += 2) ptsb_sum += demod[i*block_length+pilot_off].real(); int ptsb_phase = DSP::signum(ptsb_sum); if (std::abs(ptsb_sum) < pilot_tones / 4) { From 8d526422f6265a82b723e6a875549f3c85808d8f Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Thu, 7 Aug 2025 13:00:48 +0200 Subject: [PATCH 05/15] use four subsets --- decode.cc | 46 ++++++++++++++++++++++++++++++++------- encode.cc | 64 ++++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 83 insertions(+), 27 deletions(-) diff --git a/decode.cc b/decode.cc index 3a6bae8..a56b4d6 100644 --- a/decode.cc +++ b/decode.cc @@ -297,31 +297,61 @@ struct Decoder : Common for (int i = 0; i < tone_count; ++i) demod[i] = demod_or_erase(tone[i], chan[i]); value ptsa_sum = 0; - for (int i = pilot_off & 1; i < pilot_tones; i += 2) - ptsa_sum += demod[i*block_length+pilot_off].real(); + for (int i = 0; i < tone_count; i += 4) + if (i % block_length == pilot_off) + ptsa_sum += demod[i].real(); int ptsa_phase = DSP::signum(ptsa_sum); - if (std::abs(ptsa_sum) < pilot_tones / 4) { + if (std::abs(ptsa_sum) < pilot_tones / 8) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; } - for (int i = 0; i < tone_count; i += 2) { + for (int i = 0; i < tone_count; i += 4) { tone[i] *= ptsa_phase; demod[i] *= ptsa_phase; } value ptsb_sum = 0; - for (int i = !(pilot_off & 1); i < pilot_tones; i += 2) - ptsb_sum += demod[i*block_length+pilot_off].real(); + for (int i = 1; i < tone_count; i += 4) + if (i % block_length == pilot_off) + ptsb_sum += demod[i].real(); int ptsb_phase = DSP::signum(ptsb_sum); - if (std::abs(ptsb_sum) < pilot_tones / 4) { + if (std::abs(ptsb_sum) < pilot_tones / 8) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; } - for (int i = 1; i < tone_count; i += 2) { + for (int i = 1; i < tone_count; i += 4) { tone[i] *= ptsb_phase; demod[i] *= ptsb_phase; } + value ptsc_sum = 0; + for (int i = 2; i < tone_count; i += 4) + if (i % block_length == pilot_off) + ptsc_sum += demod[i].real(); + int ptsc_phase = DSP::signum(ptsc_sum); + if (std::abs(ptsc_sum) < pilot_tones / 8) { + std::cerr << "pilot phase damaged" << std::endl; + oper_mode = -1; + break; + } + for (int i = 2; i < tone_count; i += 4) { + tone[i] *= ptsc_phase; + demod[i] *= ptsc_phase; + } + value ptsd_sum = 0; + for (int i = 3; i < tone_count; i += 4) + if (i % block_length == pilot_off) + ptsd_sum += demod[i].real(); + int ptsd_phase = DSP::signum(ptsd_sum); + if (std::abs(ptsd_sum) < pilot_tones / 8) { + std::cerr << "pilot phase damaged" << std::endl; + oper_mode = -1; + break; + } + for (int i = 3; i < tone_count; i += 4) { + tone[i] *= ptsd_phase; + demod[i] *= ptsd_phase; + } for (int i = 0; i < pilot_tones; ++i) { index[i] = tone_off + block_length * i + pilot_off; phase[i] = arg(demod[block_length*i+pilot_off]); diff --git a/encode.cc b/encode.cc index 23b4450..0bcf9bc 100644 --- a/encode.cc +++ b/encode.cc @@ -40,8 +40,12 @@ struct Encoder : public Common cmplx tdom[symbol_len]; cmplx ptsa[symbol_len]; cmplx ptsb[symbol_len]; - cmplx tmpa[symbol_len]; + cmplx ptsc[symbol_len]; + cmplx ptsd[symbol_len]; cmplx test[symbol_len]; + cmplx tmpb[symbol_len]; + cmplx tmpc[symbol_len]; + cmplx tmpd[symbol_len]; cmplx kern[symbol_len]; cmplx guard[guard_len]; cmplx tone[tone_count]; @@ -92,38 +96,60 @@ struct Encoder : public Common } else { for (int i = 0; i < symbol_len; ++i) fdom[i] = 0; - for (int i = 0; i < tone_count; i += 2) + for (int i = 0; i < tone_count; i += 4) fdom[bin(i+tone_off)] = tone[i]; bwd(ptsa, fdom); for (int i = 0; i < symbol_len; ++i) ptsa[i] *= scale; for (int i = 0; i < symbol_len; ++i) fdom[i] = 0; - for (int i = 1; i < tone_count; i += 2) + for (int i = 1; i < tone_count; i += 4) fdom[bin(i+tone_off)] = tone[i]; bwd(ptsb, fdom); for (int i = 0; i < symbol_len; ++i) ptsb[i] *= scale; + for (int i = 0; i < symbol_len; ++i) + fdom[i] = 0; + for (int i = 2; i < tone_count; i += 4) + fdom[bin(i+tone_off)] = tone[i]; + bwd(ptsc, fdom); + for (int i = 0; i < symbol_len; ++i) + ptsc[i] *= scale; + for (int i = 0; i < symbol_len; ++i) + fdom[i] = 0; + for (int i = 3; i < tone_count; i += 4) + fdom[bin(i+tone_off)] = tone[i]; + bwd(ptsd, fdom); + for (int i = 0; i < symbol_len; ++i) + ptsd[i] *= scale; value best_papr = 1000; - for (value ptsa_phase = -1; ptsa_phase < 2; ptsa_phase += 2) { + for (value ptsd_phase = -1; ptsd_phase < 2; ptsd_phase += 2) { for (int i = 0; i < symbol_len; ++i) - tmpa[i] = ptsa_phase * ptsa[i]; - for (value ptsb_phase = -1; ptsb_phase < 2; ptsb_phase += 2) { + tmpd[i] = ptsd_phase * ptsd[i]; + for (value ptsc_phase = -1; ptsc_phase < 2; ptsc_phase += 2) { for (int i = 0; i < symbol_len; ++i) - test[i] = ptsb_phase * ptsb[i] + tmpa[i]; - value peak = 0, mean = 0; - for (int i = 0; i < symbol_len; ++i) { - value power(norm(test[i])); - peak = std::max(peak, power); - mean += power; - } - mean /= symbol_len; - value test_papr(peak / mean); - if (test_papr < best_papr) { - best_papr = test_papr; - papr[symbol_number] = best_papr; + tmpc[i] = ptsc_phase * ptsc[i] + tmpd[i]; + for (value ptsb_phase = -1; ptsb_phase < 2; ptsb_phase += 2) { for (int i = 0; i < symbol_len; ++i) - tdom[i] = test[i]; + tmpb[i] = ptsb_phase * ptsb[i] + tmpc[i]; + for (value ptsa_phase = -1; ptsa_phase < 2; ptsa_phase += 2) { + for (int i = 0; i < symbol_len; ++i) + test[i] = ptsa_phase * ptsa[i] + tmpb[i]; + value peak = 0, mean = 0; + for (int i = 0; i < symbol_len; ++i) { + value power(norm(test[i])); + peak = std::max(peak, power); + mean += power; + } + mean /= symbol_len; + value test_papr(peak / mean); + if (test_papr < best_papr) { + best_papr = test_papr; + papr[symbol_number] = best_papr; + for (int i = 0; i < symbol_len; ++i) + tdom[i] = test[i]; + } + } } } } From c9831283a2ab047b8b1a408d08ee6f69cf52a4a7 Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Thu, 7 Aug 2025 13:23:20 +0200 Subject: [PATCH 06/15] doubled number of subsets for pts again --- decode.cc | 80 ++++++++++++++++++++++++++++++++++++------- encode.cc | 100 +++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 144 insertions(+), 36 deletions(-) diff --git a/decode.cc b/decode.cc index a56b4d6..de52b50 100644 --- a/decode.cc +++ b/decode.cc @@ -297,61 +297,117 @@ struct Decoder : Common for (int i = 0; i < tone_count; ++i) demod[i] = demod_or_erase(tone[i], chan[i]); value ptsa_sum = 0; - for (int i = 0; i < tone_count; i += 4) + for (int i = 0; i < tone_count; i += 8) if (i % block_length == pilot_off) ptsa_sum += demod[i].real(); int ptsa_phase = DSP::signum(ptsa_sum); - if (std::abs(ptsa_sum) < pilot_tones / 8) { + if (std::abs(ptsa_sum) < pilot_tones / (8 * 2)) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; } - for (int i = 0; i < tone_count; i += 4) { + for (int i = 0; i < tone_count; i += 8) { tone[i] *= ptsa_phase; demod[i] *= ptsa_phase; } value ptsb_sum = 0; - for (int i = 1; i < tone_count; i += 4) + for (int i = 1; i < tone_count; i += 8) if (i % block_length == pilot_off) ptsb_sum += demod[i].real(); int ptsb_phase = DSP::signum(ptsb_sum); - if (std::abs(ptsb_sum) < pilot_tones / 8) { + if (std::abs(ptsb_sum) < pilot_tones / (8 * 2)) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; } - for (int i = 1; i < tone_count; i += 4) { + for (int i = 1; i < tone_count; i += 8) { tone[i] *= ptsb_phase; demod[i] *= ptsb_phase; } value ptsc_sum = 0; - for (int i = 2; i < tone_count; i += 4) + for (int i = 2; i < tone_count; i += 8) if (i % block_length == pilot_off) ptsc_sum += demod[i].real(); int ptsc_phase = DSP::signum(ptsc_sum); - if (std::abs(ptsc_sum) < pilot_tones / 8) { + if (std::abs(ptsc_sum) < pilot_tones / (8 * 2)) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; } - for (int i = 2; i < tone_count; i += 4) { + for (int i = 2; i < tone_count; i += 8) { tone[i] *= ptsc_phase; demod[i] *= ptsc_phase; } value ptsd_sum = 0; - for (int i = 3; i < tone_count; i += 4) + for (int i = 3; i < tone_count; i += 8) if (i % block_length == pilot_off) ptsd_sum += demod[i].real(); int ptsd_phase = DSP::signum(ptsd_sum); - if (std::abs(ptsd_sum) < pilot_tones / 8) { + if (std::abs(ptsd_sum) < pilot_tones / (8 * 2)) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; } - for (int i = 3; i < tone_count; i += 4) { + for (int i = 3; i < tone_count; i += 8) { tone[i] *= ptsd_phase; demod[i] *= ptsd_phase; } + value ptse_sum = 0; + for (int i = 4; i < tone_count; i += 8) + if (i % block_length == pilot_off) + ptse_sum += demod[i].real(); + int ptse_phase = DSP::signum(ptse_sum); + if (std::abs(ptse_sum) < pilot_tones / (8 * 2)) { + std::cerr << "pilot phase damaged" << std::endl; + oper_mode = -1; + break; + } + for (int i = 4; i < tone_count; i += 8) { + tone[i] *= ptse_phase; + demod[i] *= ptse_phase; + } + value ptsf_sum = 0; + for (int i = 5; i < tone_count; i += 8) + if (i % block_length == pilot_off) + ptsf_sum += demod[i].real(); + int ptsf_phase = DSP::signum(ptsf_sum); + if (std::abs(ptsf_sum) < pilot_tones / (8 * 2)) { + std::cerr << "pilot phase damaged" << std::endl; + oper_mode = -1; + break; + } + for (int i = 5; i < tone_count; i += 8) { + tone[i] *= ptsf_phase; + demod[i] *= ptsf_phase; + } + value ptsg_sum = 0; + for (int i = 6; i < tone_count; i += 8) + if (i % block_length == pilot_off) + ptsg_sum += demod[i].real(); + int ptsg_phase = DSP::signum(ptsg_sum); + if (std::abs(ptsg_sum) < pilot_tones / (8 * 2)) { + std::cerr << "pilot phase damaged" << std::endl; + oper_mode = -1; + break; + } + for (int i = 6; i < tone_count; i += 8) { + tone[i] *= ptsg_phase; + demod[i] *= ptsg_phase; + } + value ptsh_sum = 0; + for (int i = 7; i < tone_count; i += 8) + if (i % block_length == pilot_off) + ptsh_sum += demod[i].real(); + int ptsh_phase = DSP::signum(ptsh_sum); + if (std::abs(ptsh_sum) < pilot_tones / (8 * 2)) { + std::cerr << "pilot phase damaged" << std::endl; + oper_mode = -1; + break; + } + for (int i = 7; i < tone_count; i += 8) { + tone[i] *= ptsh_phase; + demod[i] *= ptsh_phase; + } for (int i = 0; i < pilot_tones; ++i) { index[i] = tone_off + block_length * i + pilot_off; phase[i] = arg(demod[block_length*i+pilot_off]); diff --git a/encode.cc b/encode.cc index 0bcf9bc..094814a 100644 --- a/encode.cc +++ b/encode.cc @@ -42,10 +42,18 @@ struct Encoder : public Common cmplx ptsb[symbol_len]; cmplx ptsc[symbol_len]; cmplx ptsd[symbol_len]; + cmplx ptse[symbol_len]; + cmplx ptsf[symbol_len]; + cmplx ptsg[symbol_len]; + cmplx ptsh[symbol_len]; cmplx test[symbol_len]; cmplx tmpb[symbol_len]; cmplx tmpc[symbol_len]; cmplx tmpd[symbol_len]; + cmplx tmpe[symbol_len]; + cmplx tmpf[symbol_len]; + cmplx tmpg[symbol_len]; + cmplx tmph[symbol_len]; cmplx kern[symbol_len]; cmplx guard[guard_len]; cmplx tone[tone_count]; @@ -96,58 +104,102 @@ struct Encoder : public Common } else { for (int i = 0; i < symbol_len; ++i) fdom[i] = 0; - for (int i = 0; i < tone_count; i += 4) + for (int i = 0; i < tone_count; i += 8) fdom[bin(i+tone_off)] = tone[i]; bwd(ptsa, fdom); for (int i = 0; i < symbol_len; ++i) ptsa[i] *= scale; for (int i = 0; i < symbol_len; ++i) fdom[i] = 0; - for (int i = 1; i < tone_count; i += 4) + for (int i = 1; i < tone_count; i += 8) fdom[bin(i+tone_off)] = tone[i]; bwd(ptsb, fdom); for (int i = 0; i < symbol_len; ++i) ptsb[i] *= scale; for (int i = 0; i < symbol_len; ++i) fdom[i] = 0; - for (int i = 2; i < tone_count; i += 4) + for (int i = 2; i < tone_count; i += 8) fdom[bin(i+tone_off)] = tone[i]; bwd(ptsc, fdom); for (int i = 0; i < symbol_len; ++i) ptsc[i] *= scale; for (int i = 0; i < symbol_len; ++i) fdom[i] = 0; - for (int i = 3; i < tone_count; i += 4) + for (int i = 3; i < tone_count; i += 8) fdom[bin(i+tone_off)] = tone[i]; bwd(ptsd, fdom); for (int i = 0; i < symbol_len; ++i) ptsd[i] *= scale; + for (int i = 0; i < symbol_len; ++i) + fdom[i] = 0; + for (int i = 4; i < tone_count; i += 8) + fdom[bin(i+tone_off)] = tone[i]; + bwd(ptse, fdom); + for (int i = 0; i < symbol_len; ++i) + ptse[i] *= scale; + for (int i = 0; i < symbol_len; ++i) + fdom[i] = 0; + for (int i = 5; i < tone_count; i += 8) + fdom[bin(i+tone_off)] = tone[i]; + bwd(ptsf, fdom); + for (int i = 0; i < symbol_len; ++i) + ptsf[i] *= scale; + for (int i = 0; i < symbol_len; ++i) + fdom[i] = 0; + for (int i = 6; i < tone_count; i += 8) + fdom[bin(i+tone_off)] = tone[i]; + bwd(ptsg, fdom); + for (int i = 0; i < symbol_len; ++i) + ptsg[i] *= scale; + for (int i = 0; i < symbol_len; ++i) + fdom[i] = 0; + for (int i = 7; i < tone_count; i += 8) + fdom[bin(i+tone_off)] = tone[i]; + bwd(ptsh, fdom); + for (int i = 0; i < symbol_len; ++i) + ptsh[i] *= scale; value best_papr = 1000; - for (value ptsd_phase = -1; ptsd_phase < 2; ptsd_phase += 2) { + for (value ptsh_phase = -1; ptsh_phase < 2; ptsh_phase += 2) { for (int i = 0; i < symbol_len; ++i) - tmpd[i] = ptsd_phase * ptsd[i]; - for (value ptsc_phase = -1; ptsc_phase < 2; ptsc_phase += 2) { + tmph[i] = ptsh_phase * ptsh[i]; + for (value ptsg_phase = -1; ptsg_phase < 2; ptsg_phase += 2) { for (int i = 0; i < symbol_len; ++i) - tmpc[i] = ptsc_phase * ptsc[i] + tmpd[i]; - for (value ptsb_phase = -1; ptsb_phase < 2; ptsb_phase += 2) { + tmpg[i] = ptsg_phase * ptsg[i] + tmph[i]; + for (value ptsf_phase = -1; ptsf_phase < 2; ptsf_phase += 2) { for (int i = 0; i < symbol_len; ++i) - tmpb[i] = ptsb_phase * ptsb[i] + tmpc[i]; - for (value ptsa_phase = -1; ptsa_phase < 2; ptsa_phase += 2) { + tmpf[i] = ptsf_phase * ptsf[i] + tmpg[i]; + for (value ptse_phase = -1; ptse_phase < 2; ptse_phase += 2) { for (int i = 0; i < symbol_len; ++i) - test[i] = ptsa_phase * ptsa[i] + tmpb[i]; - value peak = 0, mean = 0; - for (int i = 0; i < symbol_len; ++i) { - value power(norm(test[i])); - peak = std::max(peak, power); - mean += power; - } - mean /= symbol_len; - value test_papr(peak / mean); - if (test_papr < best_papr) { - best_papr = test_papr; - papr[symbol_number] = best_papr; + tmpe[i] = ptse_phase * ptse[i] + tmpf[i]; + for (value ptsd_phase = -1; ptsd_phase < 2; ptsd_phase += 2) { for (int i = 0; i < symbol_len; ++i) - tdom[i] = test[i]; + tmpd[i] = ptsd_phase * ptsd[i] + tmpe[i]; + for (value ptsc_phase = -1; ptsc_phase < 2; ptsc_phase += 2) { + for (int i = 0; i < symbol_len; ++i) + tmpc[i] = ptsc_phase * ptsc[i] + tmpd[i]; + for (value ptsb_phase = -1; ptsb_phase < 2; ptsb_phase += 2) { + for (int i = 0; i < symbol_len; ++i) + tmpb[i] = ptsb_phase * ptsb[i] + tmpc[i]; + for (value ptsa_phase = -1; ptsa_phase < 2; ptsa_phase += 2) { + for (int i = 0; i < symbol_len; ++i) + test[i] = ptsa_phase * ptsa[i] + tmpb[i]; + value peak = 0, mean = 0; + for (int i = 0; i < symbol_len; ++i) { + value power(norm(test[i])); + peak = std::max(peak, power); + mean += power; + } + mean /= symbol_len; + value test_papr(peak / mean); + if (test_papr < best_papr) { + best_papr = test_papr; + papr[symbol_number] = best_papr; + for (int i = 0; i < symbol_len; ++i) + tdom[i] = test[i]; + } + } + } + } } } } From b3b8a2703c39e50bc867766828ba054315341a80 Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Thu, 7 Aug 2025 14:28:28 +0200 Subject: [PATCH 07/15] check four phases --- decode.cc | 65 ++++++++++++++++++++++++++++--------------------------- encode.cc | 34 +++++++++++++++-------------- 2 files changed, 51 insertions(+), 48 deletions(-) diff --git a/decode.cc b/decode.cc index de52b50..e51ba4e 100644 --- a/decode.cc +++ b/decode.cc @@ -296,12 +296,13 @@ struct Decoder : Common tone[i] *= nrz(seq1()); for (int i = 0; i < tone_count; ++i) demod[i] = demod_or_erase(tone[i], chan[i]); - value ptsa_sum = 0; + auto rot = [](cmplx p){ return std::abs(p.real()) < std::abs(p.imag()) ? cmplx(0, p.imag() < 0 ? 1 : -1) : cmplx(p.real() < 0 ? -1 : 1); }; + cmplx ptsa_sum; for (int i = 0; i < tone_count; i += 8) if (i % block_length == pilot_off) - ptsa_sum += demod[i].real(); - int ptsa_phase = DSP::signum(ptsa_sum); - if (std::abs(ptsa_sum) < pilot_tones / (8 * 2)) { + ptsa_sum += demod[i]; + cmplx ptsa_phase = rot(ptsa_sum); + if (abs(ptsa_sum) < pilot_tones / (8 * 2)) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; @@ -310,12 +311,12 @@ struct Decoder : Common tone[i] *= ptsa_phase; demod[i] *= ptsa_phase; } - value ptsb_sum = 0; + cmplx ptsb_sum; for (int i = 1; i < tone_count; i += 8) if (i % block_length == pilot_off) - ptsb_sum += demod[i].real(); - int ptsb_phase = DSP::signum(ptsb_sum); - if (std::abs(ptsb_sum) < pilot_tones / (8 * 2)) { + ptsb_sum += demod[i]; + cmplx ptsb_phase = rot(ptsb_sum); + if (abs(ptsb_sum) < pilot_tones / (8 * 2)) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; @@ -324,12 +325,12 @@ struct Decoder : Common tone[i] *= ptsb_phase; demod[i] *= ptsb_phase; } - value ptsc_sum = 0; + cmplx ptsc_sum; for (int i = 2; i < tone_count; i += 8) if (i % block_length == pilot_off) - ptsc_sum += demod[i].real(); - int ptsc_phase = DSP::signum(ptsc_sum); - if (std::abs(ptsc_sum) < pilot_tones / (8 * 2)) { + ptsc_sum += demod[i]; + cmplx ptsc_phase = rot(ptsc_sum); + if (abs(ptsc_sum) < pilot_tones / (8 * 2)) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; @@ -338,12 +339,12 @@ struct Decoder : Common tone[i] *= ptsc_phase; demod[i] *= ptsc_phase; } - value ptsd_sum = 0; + cmplx ptsd_sum; for (int i = 3; i < tone_count; i += 8) if (i % block_length == pilot_off) - ptsd_sum += demod[i].real(); - int ptsd_phase = DSP::signum(ptsd_sum); - if (std::abs(ptsd_sum) < pilot_tones / (8 * 2)) { + ptsd_sum += demod[i]; + cmplx ptsd_phase = rot(ptsd_sum); + if (abs(ptsd_sum) < pilot_tones / (8 * 2)) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; @@ -352,12 +353,12 @@ struct Decoder : Common tone[i] *= ptsd_phase; demod[i] *= ptsd_phase; } - value ptse_sum = 0; + cmplx ptse_sum; for (int i = 4; i < tone_count; i += 8) if (i % block_length == pilot_off) - ptse_sum += demod[i].real(); - int ptse_phase = DSP::signum(ptse_sum); - if (std::abs(ptse_sum) < pilot_tones / (8 * 2)) { + ptse_sum += demod[i]; + cmplx ptse_phase = rot(ptse_sum); + if (abs(ptse_sum) < pilot_tones / (8 * 2)) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; @@ -366,12 +367,12 @@ struct Decoder : Common tone[i] *= ptse_phase; demod[i] *= ptse_phase; } - value ptsf_sum = 0; + cmplx ptsf_sum; for (int i = 5; i < tone_count; i += 8) if (i % block_length == pilot_off) - ptsf_sum += demod[i].real(); - int ptsf_phase = DSP::signum(ptsf_sum); - if (std::abs(ptsf_sum) < pilot_tones / (8 * 2)) { + ptsf_sum += demod[i]; + cmplx ptsf_phase = rot(ptsf_sum); + if (abs(ptsf_sum) < pilot_tones / (8 * 2)) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; @@ -380,12 +381,12 @@ struct Decoder : Common tone[i] *= ptsf_phase; demod[i] *= ptsf_phase; } - value ptsg_sum = 0; + cmplx ptsg_sum; for (int i = 6; i < tone_count; i += 8) if (i % block_length == pilot_off) - ptsg_sum += demod[i].real(); - int ptsg_phase = DSP::signum(ptsg_sum); - if (std::abs(ptsg_sum) < pilot_tones / (8 * 2)) { + ptsg_sum += demod[i]; + cmplx ptsg_phase = rot(ptsg_sum); + if (abs(ptsg_sum) < pilot_tones / (8 * 2)) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; @@ -394,12 +395,12 @@ struct Decoder : Common tone[i] *= ptsg_phase; demod[i] *= ptsg_phase; } - value ptsh_sum = 0; + cmplx ptsh_sum; for (int i = 7; i < tone_count; i += 8) if (i % block_length == pilot_off) - ptsh_sum += demod[i].real(); - int ptsh_phase = DSP::signum(ptsh_sum); - if (std::abs(ptsh_sum) < pilot_tones / (8 * 2)) { + ptsh_sum += demod[i]; + cmplx ptsh_phase = rot(ptsh_sum); + if (abs(ptsh_sum) < pilot_tones / (8 * 2)) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; break; diff --git a/encode.cc b/encode.cc index 094814a..f42e2c2 100644 --- a/encode.cc +++ b/encode.cc @@ -159,30 +159,32 @@ struct Encoder : public Common for (int i = 0; i < symbol_len; ++i) ptsh[i] *= scale; value best_papr = 1000; - for (value ptsh_phase = -1; ptsh_phase < 2; ptsh_phase += 2) { + int phase_max = 4; + auto rot = [](int i){ return i&2 ? cmplx(0, nrz(i&1)) : cmplx(nrz(i&1)); }; + for (int ptsh_phase = 0; ptsh_phase < phase_max; ++ptsh_phase) { for (int i = 0; i < symbol_len; ++i) - tmph[i] = ptsh_phase * ptsh[i]; - for (value ptsg_phase = -1; ptsg_phase < 2; ptsg_phase += 2) { + tmph[i] = rot(ptsh_phase) * ptsh[i]; + for (int ptsg_phase = 0; ptsg_phase < phase_max; ++ptsg_phase) { for (int i = 0; i < symbol_len; ++i) - tmpg[i] = ptsg_phase * ptsg[i] + tmph[i]; - for (value ptsf_phase = -1; ptsf_phase < 2; ptsf_phase += 2) { + tmpg[i] = rot(ptsg_phase) * ptsg[i] + tmph[i]; + for (int ptsf_phase = 0; ptsf_phase < phase_max; ++ptsf_phase) { for (int i = 0; i < symbol_len; ++i) - tmpf[i] = ptsf_phase * ptsf[i] + tmpg[i]; - for (value ptse_phase = -1; ptse_phase < 2; ptse_phase += 2) { + tmpf[i] = rot(ptsf_phase) * ptsf[i] + tmpg[i]; + for (int ptse_phase = 0; ptse_phase < phase_max; ++ptse_phase) { for (int i = 0; i < symbol_len; ++i) - tmpe[i] = ptse_phase * ptse[i] + tmpf[i]; - for (value ptsd_phase = -1; ptsd_phase < 2; ptsd_phase += 2) { + tmpe[i] = rot(ptse_phase) * ptse[i] + tmpf[i]; + for (int ptsd_phase = 0; ptsd_phase < phase_max; ++ptsd_phase) { for (int i = 0; i < symbol_len; ++i) - tmpd[i] = ptsd_phase * ptsd[i] + tmpe[i]; - for (value ptsc_phase = -1; ptsc_phase < 2; ptsc_phase += 2) { + tmpd[i] = rot(ptsd_phase) * ptsd[i] + tmpe[i]; + for (int ptsc_phase = 0; ptsc_phase < phase_max; ++ptsc_phase) { for (int i = 0; i < symbol_len; ++i) - tmpc[i] = ptsc_phase * ptsc[i] + tmpd[i]; - for (value ptsb_phase = -1; ptsb_phase < 2; ptsb_phase += 2) { + tmpc[i] = rot(ptsc_phase) * ptsc[i] + tmpd[i]; + for (int ptsb_phase = 0; ptsb_phase < phase_max; ++ptsb_phase) { for (int i = 0; i < symbol_len; ++i) - tmpb[i] = ptsb_phase * ptsb[i] + tmpc[i]; - for (value ptsa_phase = -1; ptsa_phase < 2; ptsa_phase += 2) { + tmpb[i] = rot(ptsb_phase) * ptsb[i] + tmpc[i]; + for (int ptsa_phase = 0; ptsa_phase < phase_max; ++ptsa_phase) { for (int i = 0; i < symbol_len; ++i) - test[i] = ptsa_phase * ptsa[i] + tmpb[i]; + test[i] = rot(ptsa_phase) * ptsa[i] + tmpb[i]; value peak = 0, mean = 0; for (int i = 0; i < symbol_len; ++i) { value power(norm(test[i])); From e5302188f2620a5def09df6319e81c72691e5a09 Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Thu, 7 Aug 2025 20:26:50 +0200 Subject: [PATCH 08/15] abort search if PAPR is better than 7 dB --- encode.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/encode.cc b/encode.cc index f42e2c2..81f795d 100644 --- a/encode.cc +++ b/encode.cc @@ -198,6 +198,8 @@ struct Encoder : public Common papr[symbol_number] = best_papr; for (int i = 0; i < symbol_len; ++i) tdom[i] = test[i]; + if (test_papr < 5) + goto end; } } } @@ -208,6 +210,7 @@ struct Encoder : public Common } } } +end: clipping_and_filtering(scale); if (symbol_number != -1) { for (int i = 0; i < guard_len; ++i) From 40e14d4fdbd69741a4d3a8eff2109813cf495c19 Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Fri, 8 Aug 2025 07:16:03 +0200 Subject: [PATCH 09/15] added partial_transmit_sequence() helper --- decode.cc | 134 +++++++++--------------------------------------------- 1 file changed, 21 insertions(+), 113 deletions(-) diff --git a/decode.cc b/decode.cc index e51ba4e..42ec6e3 100644 --- a/decode.cc +++ b/decode.cc @@ -223,6 +223,24 @@ struct Decoder : Common md |= uint64_t(mesg[i].v[best] < 0) << i; return md; } + bool partial_transmit_sequence(int offset, int stride) + { + cmplx sum; + for (int i = offset; i < tone_count; i += stride) + if (i % block_length == pilot_off) + sum += demod[i]; + if (abs(sum) < pilot_tones / (2 * stride)) { + std::cerr << "pilot phase damaged" << std::endl; + oper_mode = -1; + return false; + } + cmplx rot = std::abs(sum.real()) < std::abs(sum.imag()) ? cmplx(0, sum.imag() < 0 ? 1 : -1) : cmplx(sum.real() < 0 ? -1 : 1); + for (int i = offset; i < tone_count; i += stride) { + tone[i] *= rot; + demod[i] *= rot; + } + return true; + } Decoder(DSP::ReadPCM *pcm, const char *const *output_names, int output_count) : pcm(pcm), correlator(mls0_seq()) { blockdc.samples(filter_len); @@ -296,119 +314,9 @@ struct Decoder : Common tone[i] *= nrz(seq1()); for (int i = 0; i < tone_count; ++i) demod[i] = demod_or_erase(tone[i], chan[i]); - auto rot = [](cmplx p){ return std::abs(p.real()) < std::abs(p.imag()) ? cmplx(0, p.imag() < 0 ? 1 : -1) : cmplx(p.real() < 0 ? -1 : 1); }; - cmplx ptsa_sum; - for (int i = 0; i < tone_count; i += 8) - if (i % block_length == pilot_off) - ptsa_sum += demod[i]; - cmplx ptsa_phase = rot(ptsa_sum); - if (abs(ptsa_sum) < pilot_tones / (8 * 2)) { - std::cerr << "pilot phase damaged" << std::endl; - oper_mode = -1; - break; - } - for (int i = 0; i < tone_count; i += 8) { - tone[i] *= ptsa_phase; - demod[i] *= ptsa_phase; - } - cmplx ptsb_sum; - for (int i = 1; i < tone_count; i += 8) - if (i % block_length == pilot_off) - ptsb_sum += demod[i]; - cmplx ptsb_phase = rot(ptsb_sum); - if (abs(ptsb_sum) < pilot_tones / (8 * 2)) { - std::cerr << "pilot phase damaged" << std::endl; - oper_mode = -1; - break; - } - for (int i = 1; i < tone_count; i += 8) { - tone[i] *= ptsb_phase; - demod[i] *= ptsb_phase; - } - cmplx ptsc_sum; - for (int i = 2; i < tone_count; i += 8) - if (i % block_length == pilot_off) - ptsc_sum += demod[i]; - cmplx ptsc_phase = rot(ptsc_sum); - if (abs(ptsc_sum) < pilot_tones / (8 * 2)) { - std::cerr << "pilot phase damaged" << std::endl; - oper_mode = -1; - break; - } - for (int i = 2; i < tone_count; i += 8) { - tone[i] *= ptsc_phase; - demod[i] *= ptsc_phase; - } - cmplx ptsd_sum; - for (int i = 3; i < tone_count; i += 8) - if (i % block_length == pilot_off) - ptsd_sum += demod[i]; - cmplx ptsd_phase = rot(ptsd_sum); - if (abs(ptsd_sum) < pilot_tones / (8 * 2)) { - std::cerr << "pilot phase damaged" << std::endl; - oper_mode = -1; - break; - } - for (int i = 3; i < tone_count; i += 8) { - tone[i] *= ptsd_phase; - demod[i] *= ptsd_phase; - } - cmplx ptse_sum; - for (int i = 4; i < tone_count; i += 8) - if (i % block_length == pilot_off) - ptse_sum += demod[i]; - cmplx ptse_phase = rot(ptse_sum); - if (abs(ptse_sum) < pilot_tones / (8 * 2)) { - std::cerr << "pilot phase damaged" << std::endl; - oper_mode = -1; - break; - } - for (int i = 4; i < tone_count; i += 8) { - tone[i] *= ptse_phase; - demod[i] *= ptse_phase; - } - cmplx ptsf_sum; - for (int i = 5; i < tone_count; i += 8) - if (i % block_length == pilot_off) - ptsf_sum += demod[i]; - cmplx ptsf_phase = rot(ptsf_sum); - if (abs(ptsf_sum) < pilot_tones / (8 * 2)) { - std::cerr << "pilot phase damaged" << std::endl; - oper_mode = -1; - break; - } - for (int i = 5; i < tone_count; i += 8) { - tone[i] *= ptsf_phase; - demod[i] *= ptsf_phase; - } - cmplx ptsg_sum; - for (int i = 6; i < tone_count; i += 8) - if (i % block_length == pilot_off) - ptsg_sum += demod[i]; - cmplx ptsg_phase = rot(ptsg_sum); - if (abs(ptsg_sum) < pilot_tones / (8 * 2)) { - std::cerr << "pilot phase damaged" << std::endl; - oper_mode = -1; - break; - } - for (int i = 6; i < tone_count; i += 8) { - tone[i] *= ptsg_phase; - demod[i] *= ptsg_phase; - } - cmplx ptsh_sum; - for (int i = 7; i < tone_count; i += 8) - if (i % block_length == pilot_off) - ptsh_sum += demod[i]; - cmplx ptsh_phase = rot(ptsh_sum); - if (abs(ptsh_sum) < pilot_tones / (8 * 2)) { - std::cerr << "pilot phase damaged" << std::endl; - oper_mode = -1; - break; - } - for (int i = 7; i < tone_count; i += 8) { - tone[i] *= ptsh_phase; - demod[i] *= ptsh_phase; - } + for (int i = 0; i < 8; ++i) + if (!partial_transmit_sequence(i, 8)) + break; for (int i = 0; i < pilot_tones; ++i) { index[i] = tone_off + block_length * i + pilot_off; phase[i] = arg(demod[block_length*i+pilot_off]); From f9c29575b56d15aefce44f0e0a55d82650d8c353 Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Fri, 8 Aug 2025 07:54:53 +0200 Subject: [PATCH 10/15] reject phases too close to diagonals --- decode.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/decode.cc b/decode.cc index 42ec6e3..3681f1e 100644 --- a/decode.cc +++ b/decode.cc @@ -229,12 +229,16 @@ struct Decoder : Common for (int i = offset; i < tone_count; i += stride) if (i % block_length == pilot_off) sum += demod[i]; - if (abs(sum) < pilot_tones / (2 * stride)) { + value half = pilot_tones / (2 * stride); + value magr = std::abs(sum.real()); + value magi = std::abs(sum.imag()); + bool real = magr > magi; + if (real ? (magr < half || 2 * magi > magr) : (magi < half || 2 * magr > magi)) { std::cerr << "pilot phase damaged" << std::endl; oper_mode = -1; return false; } - cmplx rot = std::abs(sum.real()) < std::abs(sum.imag()) ? cmplx(0, sum.imag() < 0 ? 1 : -1) : cmplx(sum.real() < 0 ? -1 : 1); + cmplx rot = real ? cmplx(sum.real() < 0 ? -1 : 1) : cmplx(0, sum.imag() < 0 ? 1 : -1); for (int i = offset; i < tone_count; i += stride) { tone[i] *= rot; demod[i] *= rot; From 15268aaf8853f59ecda4e48491679d0bd9e52f24 Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Fri, 8 Aug 2025 16:26:42 +0200 Subject: [PATCH 11/15] added transform() helper --- encode.cc | 82 +++++++++++++------------------------------------------ 1 file changed, 19 insertions(+), 63 deletions(-) diff --git a/encode.cc b/encode.cc index 81f795d..8b92059 100644 --- a/encode.cc +++ b/encode.cc @@ -90,74 +90,30 @@ struct Encoder : public Common for (int i = 0; i < symbol_len; ++i) tdom[i] = cmplx(clamp(tdom[i].real()), clamp(tdom[i].imag())); } + void transform(cmplx *output, value scale, int offset, int stride) + { + for (int i = 0; i < symbol_len; ++i) + fdom[i] = 0; + for (int i = offset; i < tone_count; i += stride) + fdom[bin(i+tone_off)] = tone[i]; + bwd(output, fdom); + for (int i = 0; i < symbol_len; ++i) + output[i] *= scale; + } void symbol(int symbol_number) { value scale = value(0.5) / std::sqrt(value(tone_count)); if (symbol_number < 0) { - for (int i = 0; i < symbol_len; ++i) - fdom[i] = 0; - for (int i = 0; i < tone_count; ++i) - fdom[bin(i+tone_off)] = tone[i]; - bwd(tdom, fdom); - for (int i = 0; i < symbol_len; ++i) - tdom[i] *= scale; + transform(tdom, scale, 0, 1); } else { - for (int i = 0; i < symbol_len; ++i) - fdom[i] = 0; - for (int i = 0; i < tone_count; i += 8) - fdom[bin(i+tone_off)] = tone[i]; - bwd(ptsa, fdom); - for (int i = 0; i < symbol_len; ++i) - ptsa[i] *= scale; - for (int i = 0; i < symbol_len; ++i) - fdom[i] = 0; - for (int i = 1; i < tone_count; i += 8) - fdom[bin(i+tone_off)] = tone[i]; - bwd(ptsb, fdom); - for (int i = 0; i < symbol_len; ++i) - ptsb[i] *= scale; - for (int i = 0; i < symbol_len; ++i) - fdom[i] = 0; - for (int i = 2; i < tone_count; i += 8) - fdom[bin(i+tone_off)] = tone[i]; - bwd(ptsc, fdom); - for (int i = 0; i < symbol_len; ++i) - ptsc[i] *= scale; - for (int i = 0; i < symbol_len; ++i) - fdom[i] = 0; - for (int i = 3; i < tone_count; i += 8) - fdom[bin(i+tone_off)] = tone[i]; - bwd(ptsd, fdom); - for (int i = 0; i < symbol_len; ++i) - ptsd[i] *= scale; - for (int i = 0; i < symbol_len; ++i) - fdom[i] = 0; - for (int i = 4; i < tone_count; i += 8) - fdom[bin(i+tone_off)] = tone[i]; - bwd(ptse, fdom); - for (int i = 0; i < symbol_len; ++i) - ptse[i] *= scale; - for (int i = 0; i < symbol_len; ++i) - fdom[i] = 0; - for (int i = 5; i < tone_count; i += 8) - fdom[bin(i+tone_off)] = tone[i]; - bwd(ptsf, fdom); - for (int i = 0; i < symbol_len; ++i) - ptsf[i] *= scale; - for (int i = 0; i < symbol_len; ++i) - fdom[i] = 0; - for (int i = 6; i < tone_count; i += 8) - fdom[bin(i+tone_off)] = tone[i]; - bwd(ptsg, fdom); - for (int i = 0; i < symbol_len; ++i) - ptsg[i] *= scale; - for (int i = 0; i < symbol_len; ++i) - fdom[i] = 0; - for (int i = 7; i < tone_count; i += 8) - fdom[bin(i+tone_off)] = tone[i]; - bwd(ptsh, fdom); - for (int i = 0; i < symbol_len; ++i) - ptsh[i] *= scale; + transform(ptsa, scale, 0, 8); + transform(ptsb, scale, 1, 8); + transform(ptsc, scale, 2, 8); + transform(ptsd, scale, 3, 8); + transform(ptse, scale, 4, 8); + transform(ptsf, scale, 5, 8); + transform(ptsg, scale, 6, 8); + transform(ptsh, scale, 7, 8); value best_papr = 1000; int phase_max = 4; auto rot = [](int i){ return i&2 ? cmplx(0, nrz(i&1)) : cmplx(nrz(i&1)); }; From fab9498fef0900a9b336b3a1f319a2d4a89c67b0 Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Fri, 8 Aug 2025 18:07:12 +0200 Subject: [PATCH 12/15] search recursively --- common.hh | 1 + decode.cc | 4 +- encode.cc | 118 +++++++++++++++++++----------------------------------- 3 files changed, 45 insertions(+), 78 deletions(-) diff --git a/common.hh b/common.hh index 16cc2cd..b9bbea3 100644 --- a/common.hh +++ b/common.hh @@ -26,6 +26,7 @@ struct Common static const int block_length = 5; static const int block_skew = 3; static const int first_pilot = 4; + static const int pts_count = 8; CODE::CRC crc0; CODE::CRC crc1; uint8_t data[data_max]; diff --git a/decode.cc b/decode.cc index 3681f1e..48d6840 100644 --- a/decode.cc +++ b/decode.cc @@ -318,8 +318,8 @@ struct Decoder : Common tone[i] *= nrz(seq1()); for (int i = 0; i < tone_count; ++i) demod[i] = demod_or_erase(tone[i], chan[i]); - for (int i = 0; i < 8; ++i) - if (!partial_transmit_sequence(i, 8)) + for (int i = 0; i < pts_count; ++i) + if (!partial_transmit_sequence(i, pts_count)) break; for (int i = 0; i < pilot_tones; ++i) { index[i] = tone_off + block_length * i + pilot_off; diff --git a/encode.cc b/encode.cc index 8b92059..4bc71d4 100644 --- a/encode.cc +++ b/encode.cc @@ -38,27 +38,14 @@ struct Encoder : public Common code_type code[bits_max], perm[bits_max], mesg[bits_max], meta[data_tones]; cmplx fdom[symbol_len]; cmplx tdom[symbol_len]; - cmplx ptsa[symbol_len]; - cmplx ptsb[symbol_len]; - cmplx ptsc[symbol_len]; - cmplx ptsd[symbol_len]; - cmplx ptse[symbol_len]; - cmplx ptsf[symbol_len]; - cmplx ptsg[symbol_len]; - cmplx ptsh[symbol_len]; - cmplx test[symbol_len]; - cmplx tmpb[symbol_len]; - cmplx tmpc[symbol_len]; - cmplx tmpd[symbol_len]; - cmplx tmpe[symbol_len]; - cmplx tmpf[symbol_len]; - cmplx tmpg[symbol_len]; - cmplx tmph[symbol_len]; + cmplx part[symbol_len*pts_count]; + cmplx temp[symbol_len*pts_count]; cmplx kern[symbol_len]; cmplx guard[guard_len]; cmplx tone[tone_count]; value weight[guard_len]; value papr[symbols_max]; + value best_papr; static int bin(int carrier) { @@ -100,73 +87,52 @@ struct Encoder : public Common for (int i = 0; i < symbol_len; ++i) output[i] *= scale; } + bool partial_transmit_sequence(int level) + { + int phase_max = 4; + for (int i = 0; i < phase_max; ++i) { + cmplx rot = i&2 ? cmplx(0, nrz(i&1)) : cmplx(nrz(i&1)); + if (level == pts_count - 1) + for (int i = 0; i < symbol_len; ++i) + temp[symbol_len*level+i] = rot * part[symbol_len*level+i]; + else + for (int i = 0; i < symbol_len; ++i) + temp[symbol_len*level+i] = rot * part[symbol_len*level+i] + temp[symbol_len*(level+1)+i]; + if (level) { + if (partial_transmit_sequence(level - 1)) + return true; + } else { + value peak = 0, mean = 0; + for (int i = 0; i < symbol_len; ++i) { + value power(norm(temp[i])); + peak = std::max(peak, power); + mean += power; + } + mean /= symbol_len; + value test_papr(peak / mean); + if (test_papr < best_papr) { + best_papr = test_papr; + for (int i = 0; i < symbol_len; ++i) + tdom[i] = temp[i]; + if (test_papr < 5) + return true; + } + } + } + return false; + } void symbol(int symbol_number) { value scale = value(0.5) / std::sqrt(value(tone_count)); if (symbol_number < 0) { transform(tdom, scale, 0, 1); } else { - transform(ptsa, scale, 0, 8); - transform(ptsb, scale, 1, 8); - transform(ptsc, scale, 2, 8); - transform(ptsd, scale, 3, 8); - transform(ptse, scale, 4, 8); - transform(ptsf, scale, 5, 8); - transform(ptsg, scale, 6, 8); - transform(ptsh, scale, 7, 8); - value best_papr = 1000; - int phase_max = 4; - auto rot = [](int i){ return i&2 ? cmplx(0, nrz(i&1)) : cmplx(nrz(i&1)); }; - for (int ptsh_phase = 0; ptsh_phase < phase_max; ++ptsh_phase) { - for (int i = 0; i < symbol_len; ++i) - tmph[i] = rot(ptsh_phase) * ptsh[i]; - for (int ptsg_phase = 0; ptsg_phase < phase_max; ++ptsg_phase) { - for (int i = 0; i < symbol_len; ++i) - tmpg[i] = rot(ptsg_phase) * ptsg[i] + tmph[i]; - for (int ptsf_phase = 0; ptsf_phase < phase_max; ++ptsf_phase) { - for (int i = 0; i < symbol_len; ++i) - tmpf[i] = rot(ptsf_phase) * ptsf[i] + tmpg[i]; - for (int ptse_phase = 0; ptse_phase < phase_max; ++ptse_phase) { - for (int i = 0; i < symbol_len; ++i) - tmpe[i] = rot(ptse_phase) * ptse[i] + tmpf[i]; - for (int ptsd_phase = 0; ptsd_phase < phase_max; ++ptsd_phase) { - for (int i = 0; i < symbol_len; ++i) - tmpd[i] = rot(ptsd_phase) * ptsd[i] + tmpe[i]; - for (int ptsc_phase = 0; ptsc_phase < phase_max; ++ptsc_phase) { - for (int i = 0; i < symbol_len; ++i) - tmpc[i] = rot(ptsc_phase) * ptsc[i] + tmpd[i]; - for (int ptsb_phase = 0; ptsb_phase < phase_max; ++ptsb_phase) { - for (int i = 0; i < symbol_len; ++i) - tmpb[i] = rot(ptsb_phase) * ptsb[i] + tmpc[i]; - for (int ptsa_phase = 0; ptsa_phase < phase_max; ++ptsa_phase) { - for (int i = 0; i < symbol_len; ++i) - test[i] = rot(ptsa_phase) * ptsa[i] + tmpb[i]; - value peak = 0, mean = 0; - for (int i = 0; i < symbol_len; ++i) { - value power(norm(test[i])); - peak = std::max(peak, power); - mean += power; - } - mean /= symbol_len; - value test_papr(peak / mean); - if (test_papr < best_papr) { - best_papr = test_papr; - papr[symbol_number] = best_papr; - for (int i = 0; i < symbol_len; ++i) - tdom[i] = test[i]; - if (test_papr < 5) - goto end; - } - } - } - } - } - } - } - } - } + for (int i = 0; i < pts_count; ++i) + transform(part + symbol_len * i, scale, pts_count - 1 - i, pts_count); + best_papr = 1000; + partial_transmit_sequence(pts_count - 1); + papr[symbol_number] = best_papr; } -end: clipping_and_filtering(scale); if (symbol_number != -1) { for (int i = 0; i < guard_len; ++i) From aa47a2f9e3b0416f41f1d7afaf076335666ea9fb Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Fri, 8 Aug 2025 18:37:55 +0200 Subject: [PATCH 13/15] need to break out of the other loop as well --- decode.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/decode.cc b/decode.cc index 48d6840..464237a 100644 --- a/decode.cc +++ b/decode.cc @@ -318,9 +318,12 @@ struct Decoder : Common tone[i] *= nrz(seq1()); for (int i = 0; i < tone_count; ++i) demod[i] = demod_or_erase(tone[i], chan[i]); + bool pts_fail = false; for (int i = 0; i < pts_count; ++i) - if (!partial_transmit_sequence(i, pts_count)) + if ((pts_fail = !partial_transmit_sequence(i, pts_count))) break; + if (pts_fail) + break; for (int i = 0; i < pilot_tones; ++i) { index[i] = tone_off + block_length * i + pilot_off; phase[i] = arg(demod[block_length*i+pilot_off]); From 49c20124a8615b05b0781ba47b2eb137ae0adf58 Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Fri, 8 Aug 2025 19:35:45 +0200 Subject: [PATCH 14/15] pilot indexing needs work for flexible subblock count --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index ff3099f..cbb4b3b 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,5 @@ ### TODO +* make pts work for other subblock numbers than 2, 4 and 8 * implement analog mode (most significant mode bit is 1), where only complex values are the payload * re-encode preamble to use entire symbol to update the channel estimation * compute SNR per subchannel using sync and preamble and update with each symbol From f6977efb91b023c3c9a9d45ea5b581e3b53a08ec Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Fri, 8 Aug 2025 22:59:42 +0200 Subject: [PATCH 15/15] unroll innermost two levels --- encode.cc | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/encode.cc b/encode.cc index 4bc71d4..db6b428 100644 --- a/encode.cc +++ b/encode.cc @@ -98,24 +98,34 @@ struct Encoder : public Common else for (int i = 0; i < symbol_len; ++i) temp[symbol_len*level+i] = rot * part[symbol_len*level+i] + temp[symbol_len*(level+1)+i]; - if (level) { + if (level > 2) { if (partial_transmit_sequence(level - 1)) return true; } else { - value peak = 0, mean = 0; - for (int i = 0; i < symbol_len; ++i) { - value power(norm(temp[i])); - peak = std::max(peak, power); - mean += power; - } - mean /= symbol_len; - value test_papr(peak / mean); - if (test_papr < best_papr) { - best_papr = test_papr; + for (int i = 0; i < phase_max; ++i) { + cmplx rot = i&2 ? cmplx(0, nrz(i&1)) : cmplx(nrz(i&1)); for (int i = 0; i < symbol_len; ++i) - tdom[i] = temp[i]; - if (test_papr < 5) - return true; + temp[symbol_len+i] = rot * part[symbol_len+i] + temp[symbol_len*2+i]; + for (int i = 0; i < phase_max; ++i) { + cmplx rot = i&2 ? cmplx(0, nrz(i&1)) : cmplx(nrz(i&1)); + for (int i = 0; i < symbol_len; ++i) + temp[i] = rot * part[i] + temp[symbol_len+i]; + value peak = 0, mean = 0; + for (int i = 0; i < symbol_len; ++i) { + value power(norm(temp[i])); + peak = std::max(peak, power); + mean += power; + } + mean /= symbol_len; + value test_papr(peak / mean); + if (test_papr < best_papr) { + best_papr = test_papr; + for (int i = 0; i < symbol_len; ++i) + tdom[i] = temp[i]; + if (test_papr < 5) + return true; + } + } } } }