[webftr-js8-lab] command=source-message174-crc12-contract [webftr-js8-lab] root=/decoders/js8_decoder [webftr-js8-lab] log=/decoders/js8_decoder/logs/20260527T125938Z_source-message174-crc12-contract.log [webftr-js8-lab] manifest=/decoders/js8_decoder/logs/20260527T125938Z_source-message174-crc12-contract_manifest.json [webftr-js8-lab] utc=20260527T125938Z [webftr-js8-lab] rx-only guard: no TX / no PTT / no Tune / no Send [webftr-js8-lab] source message174 CRC12 contract source: /decoders/js8_decoder/runtime/src/JS8Call-improved [webftr-js8-lab] JSON output: /decoders/js8_decoder/logs/20260527T125938Z_source_message174_crc12_contract_output.json [webftr-js8-lab] JSON timeout guard: 180s { "ok": true, "tool": "webftr-js8-source-message174-crc12-contract", "tool_version": "step55-source-message174-crc12-contract", "source_dir": "/decoders/js8_decoder/runtime/src/JS8Call-improved", "target_file": "JS8_Mode/JS8.cpp", "target_exists": true, "rx_only_guard": { "tx": false, "ptt": false, "tune": false, "send": false, "js8call_runtime_control": false }, "no_gui_runtime_started": true, "purpose": "Extract the source-confirmed JS8Call-Improved message174 CRC12 contract before patching JS8Lab bit/message validation.", "contract": { "crc_width": 12, "crc_poly_hex": "0xc06", "crc_xor_decimal": 42, "crc_xor_hex": "0x02a", "received_crc_extraction_detected": true, "crc_clear_detected": true, "message_reserve_12_detected": true, "word_loop_12_detected": true, "source_inference": { "payload_data_bits": 72, "crc_bits": 12, "message_words": 12, "word_bits": 6, "legacy_message91_crc14_assumption_valid_for_this_path": false, "why": "JS8Call-Improved source path shows extractmessage174/checkCRC12 with 12x6 data bits plus CRC12, not the earlier diagnostic 77+14 Message91 split." } }, "keyword_lines": [ { "line": 5, "text": " * (C) 2025 Allan Bazinet - All Rights Reserved" }, { "line": 80, "text": "// 6. The Fortran version normalized `s1` by dividing by the median in" }, { "line": 102, "text": " constexpr auto RAD_360 = std::numbers::pi * 2;" }, { "line": 108, "text": " // itself is only 1-e16, so within the domain of doubles," }, { "line": 115, "text": " 0.041666666666666664, // Coefficient for x^4" }, { "line": 116, "text": " -0.001388888888888889, // Coefficient for x^6" }, { "line": 118, "text": " -0.00000027557319223986, // Coefficient for x^10" }, { "line": 119, "text": " 0.00000000208767569878681, // Coefficient for x^12" }, { "line": 120, "text": " -0.00000000001147074513875176, // Coefficient for x^14" }, { "line": 121, "text": " 0.0000000000000477947733238733 // Coefficient for x^16" }, { "line": 126, "text": " auto const x6 = x4 * x2;" }, { "line": 129, "text": " auto const x12 = x8 * x4;" }, { "line": 130, "text": " auto const x14 = x12 * x2;" }, { "line": 131, "text": " auto const x16 = x8 * x8;" }, { "line": 134, "text": " coefficients[3] * x6 + coefficients[4] * x8 +" }, { "line": 135, "text": " coefficients[5] * x10 + coefficients[6] * x12 +" }, { "line": 136, "text": " coefficients[7] * x14 + coefficients[8] * x16;" }, { "line": 139, "text": " // Reduce x to [0, RAD_360)" }, { "line": 141, "text": " x -= static_cast(x / RAD_360) * RAD_360;" }, { "line": 146, "text": " x = RAD_360 - x;" }, { "line": 164, "text": "// parameter (KK=87) !Information bits (75 + CRC12)" }, { "line": 174, "text": "// KK\t87\tNumber of information bits (75 message bits + 12 CRC bits)." }, { "line": 185, "text": "constexpr int KK = 87; // Information bits (75 + CRC12)" }, { "line": 197, "text": "constexpr int NP2 = 2812;" }, { "line": 225, "text": " inline static constexpr int JZ = 62;" }, { "line": 230, "text": " inline static constexpr float AZ = (12000.0f / NSPS) * 0.64f;" }, { "line": 240, "text": " inline static constexpr float TSTEP = NSTEP / 12000.0f;" }, { "line": 242, "text": " inline static constexpr float DF = 12000.0f / NFFT1;" }, { "line": 260, "text": " inline static constexpr float AZ = (12000.0f / NSPS) * 0.8f;" }, { "line": 270, "text": " inline static constexpr float TSTEP = NSTEP / 12000.0f;" }, { "line": 272, "text": " inline static constexpr float DF = 12000.0f / NFFT1;" }, { "line": 283, "text": " inline static constexpr int NDOWNSPS = 12;" }, { "line": 284, "text": " inline static constexpr int NDD = 120;" }, { "line": 290, "text": " inline static constexpr float AZ = (12000.0f / NSPS) * 0.6f;" }, { "line": 300, "text": " inline static constexpr float TSTEP = NSTEP / 12000.0f;" }, { "line": 302, "text": " inline static constexpr float DF = 12000.0f / NFFT1;" }, { "line": 325, "text": " inline static constexpr float AZ = (12000.0f / NSPS) * 0.64f;" }, { "line": 335, "text": " inline static constexpr float TSTEP = NSTEP / 12000.0f;" }, { "line": 337, "text": " inline static constexpr float DF = 12000.0f / NFFT1;" }, { "line": 348, "text": " inline static constexpr int NDOWNSPS = 12;" }, { "line": 349, "text": " inline static constexpr int NDD = 125;" }, { "line": 352, "text": " inline static constexpr float BASESUB = 36.0f;" }, { "line": 355, "text": " inline static constexpr float AZ = (12000.0f / NSPS) * 0.64f;" }, { "line": 365, "text": " inline static constexpr float TSTEP = NSTEP / 12000.0f;" }, { "line": 367, "text": " inline static constexpr float DF = 12000.0f / NFFT1;" }, { "line": 440, "text": "// a0=0.3635819" }, { "line": 442, "text": "// a2=0.1365995;" }, { "line": 443, "text": "// a3=-0.0106411;" }, { "line": 447, "text": "// a3*cos(6*pi*(i-1)/(n))" }, { "line": 566, "text": "// and the 12 bytes that result from decoding a message." }, { "line": 581, "text": " return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));" }, { "line": 598, "text": " {{0, 24, 68}, {1, 4, 72}, {2, 31, 67}, {3, 50, 60}, {5, 62, 69}," }, { "line": 599, "text": " {6, 32, 78}, {7, 49, 85}, {8, 36, 42}, {9, 40, 64}, {10, 13, 63}," }, { "line": 600, "text": " {11, 74, 76}, {12, 22, 80}, {14, 15, 81}, {16, 55, 65}, {17, 52, 59}," }, { "line": 601, "text": " {18, 30, 51}, {19, 66, 83}, {20, 28, 71}, {21, 23, 43}, {25, 34, 75}," }, { "line": 602, "text": " {26, 35, 37}, {27, 39, 41}, {29, 53, 54}, {33, 48, 86}, {38, 56, 57}," }, { "line": 603, "text": " {44, 73, 82}, {45, 61, 79}, {46, 47, 84}, {58, 70, 77}, {0, 49, 52}," }, { "line": 604, "text": " {1, 46, 83}, {2, 24, 78}, {3, 5, 13}, {4, 6, 79}, {7, 33, 54}," }, { "line": 605, "text": " {8, 35, 68}, {9, 42, 82}, {10, 22, 73}, {11, 16, 43}, {12, 56, 75}," }, { "line": 606, "text": " {14, 26, 55}, {15, 27, 28}, {17, 18, 58}, {19, 39, 62}, {20, 34, 51}," }, { "line": 607, "text": " {21, 53, 63}, {23, 61, 77}, {25, 31, 76}, {29, 71, 84}, {30, 64, 86}," }, { "line": 608, "text": " {32, 38, 50}, {36, 47, 74}, {37, 69, 70}, {40, 41, 67}, {44, 66, 85}," }, { "line": 609, "text": " {45, 80, 81}, {48, 65, 72}, {57, 59, 65}, {60, 64, 84}, {0, 13, 20}," }, { "line": 610, "text": " {1, 12, 58}, {2, 66, 81}, {3, 31, 72}, {4, 35, 53}, {5, 42, 45}," }, { "line": 611, "text": " {6, 27, 74}, {7, 32, 70}, {8, 48, 75}, {9, 57, 63}, {10, 47, 67}," }, { "line": 612, "text": " {11, 18, 44}, {14, 49, 60}, {15, 21, 25}, {16, 71, 79}, {17, 39, 54}," }, { "line": 613, "text": " {19, 34, 50}, {22, 24, 33}, {23, 62, 86}, {26, 38, 73}, {28, 77, 82}," }, { "line": 614, "text": " {29, 69, 76}, {30, 68, 83}, {21, 36, 85}, {37, 40, 80}, {41, 43, 56}," }, { "line": 615, "text": " {46, 52, 61}, {51, 55, 78}, {59, 74, 80}, {0, 38, 76}, {1, 15, 40}," }, { "line": 616, "text": " {2, 30, 53}, {3, 35, 77}, {4, 44, 64}, {5, 56, 84}, {6, 13, 48}," }, { "line": 617, "text": " {7, 20, 45}, {8, 14, 71}, {9, 19, 61}, {10, 16, 70}, {11, 33, 46}," }, { "line": 618, "text": " {12, 67, 85}, {17, 22, 42}, {18, 63, 72}, {23, 47, 78}, {24, 69, 82}," }, { "line": 619, "text": " {25, 79, 86}, {26, 31, 39}, {27, 55, 68}, {28, 62, 65}, {29, 41, 49}," }, { "line": 620, "text": " {32, 36, 81}, {34, 59, 73}, {37, 54, 83}, {43, 51, 60}, {50, 52, 71}," }, { "line": 621, "text": " {57, 58, 66}, {46, 55, 75}, {0, 18, 36}, {1, 60, 74}, {2, 7, 65}," }, { "line": 622, "text": " {3, 59, 83}, {4, 33, 38}, {5, 25, 52}, {6, 31, 56}, {8, 51, 66}," }, { "line": 623, "text": " {9, 11, 14}, {10, 50, 68}, {12, 13, 64}, {15, 30, 42}, {16, 19, 35}," }, { "line": 624, "text": " {17, 79, 85}, {20, 47, 58}, {21, 39, 45}, {22, 32, 61}, {23, 29, 73}," }, { "line": 625, "text": " {24, 41, 63}, {26, 48, 84}, {27, 37, 72}, {28, 43, 80}, {34, 67, 69}," }, { "line": 626, "text": " {40, 62, 75}, {44, 48, 70}, {49, 57, 86}, {47, 53, 82}, {12, 54, 78}," }, { "line": 627, "text": " {76, 77, 81}, {0, 1, 23}, {2, 5, 74}, {3, 55, 86}, {4, 43, 52}," }, { "line": 628, "text": " {6, 49, 82}, {7, 9, 27}, {8, 54, 61}, {10, 28, 66}, {11, 32, 39}," }, { "line": 629, "text": " {13, 15, 19}, {14, 34, 72}, {16, 30, 38}, {17, 35, 56}, {18, 45, 75}," }, { "line": 630, "text": " {20, 41, 83}, {21, 33, 58}, {22, 25, 60}, {24, 59, 64}, {26, 63, 79}," }, { "line": 631, "text": " {29, 36, 65}, {31, 44, 71}, {37, 50, 85}, {40, 76, 78}, {42, 55, 67}," }, { "line": 632, "text": " {46, 73, 81}, {39, 51, 77}, {53, 60, 70}, {45, 57, 68}}};" }, { "line": 639, "text": "constexpr std::array Nm = {{{6, {0, 29, 59, 88, 117, 146, 0}}," }, { "line": 640, "text": " {6, {1, 30, 60, 89, 118, 146, 0}}," }, { "line": 641, "text": " {6, {2, 31, 61, 90, 119, 147, 0}}," }, { "line": 642, "text": " {6, {3, 32, 62, 91, 120, 148, 0}}," }, { "line": 643, "text": " {6, {1, 33, 63, 92, 121, 149, 0}}," }, { "line": 644, "text": " {6, {4, 32, 64, 93, 122, 147, 0}}," }, { "line": 645, "text": " {6, {5, 33, 65, 94, 123, 150, 0}}," }, { "line": 646, "text": " {6, {6, 34, 66, 95, 119, 151, 0}}," }, { "line": 647, "text": " {6, {7, 35, 67, 96, 124, 152, 0}}," }, { "line": 648, "text": " {6, {8, 36, 68, 97, 125, 151, 0}}," }, { "line": 649, "text": " {6, {9, 37, 69, 98, 126, 153, 0}}," }, { "line": 650, "text": " {6, {10, 38, 70, 99, 125, 154, 0}}," }, { "line": 651, "text": " {6, {11, 39, 60, 100, 127, 144, 0}}," }, { "line": 652, "text": " {6, {9, 32, 59, 94, 127, 155, 0}}," }, { "line": 653, "text": " {6, {12, 40, 71, 96, 125, 156, 0}}," }, { "line": 654, "text": " {6, {12, 41, 72, 89, 128, 155, 0}}," }, { "line": 655, "text": " {6, {13, 38, 73, 98, 129, 157, 0}}," }, { "line": 656, "text": " {6, {14, 42, 74, 101, 130, 158, 0}}," }, { "line": 657, "text": " {6, {15, 42, 70, 102, 117, 159, 0}}," }, { "line": 658, "text": " {6, {16, 43, 75, 97, 129, 155, 0}}," }, { "line": 659, "text": " {6, {17, 44, 59, 95, 131, 160, 0}}," }, { "line": 660, "text": " {6, {18, 45, 72, 82, 132, 161, 0}}," }, { "line": 661, "text": " {6, {11, 37, 76, 101, 133, 162, 0}}," }, { "line": 662, "text": " {6, {18, 46, 77, 103, 134, 146, 0}}," }, { "line": 663, "text": " {6, {0, 31, 76, 104, 135, 163, 0}}," }, { "line": 664, "text": " {6, {19, 47, 72, 105, 122, 162, 0}}," }, { "line": 665, "text": " {6, {20, 40, 78, 106, 136, 164, 0}}," }, { "line": 666, "text": " {6, {21, 41, 65, 107, 137, 151, 0}}," }, { "line": 667, "text": " {6, {17, 41, 79, 108, 138, 153, 0}}," }, { "line": 668, "text": " {6, {22, 48, 80, 109, 134, 165, 0}}," }, { "line": 669, "text": " {6, {15, 49, 81, 90, 128, 157, 0}}," }, { "line": 670, "text": " {6, {2, 47, 62, 106, 123, 166, 0}}," }, { "line": 671, "text": " {6, {5, 50, 66, 110, 133, 154, 0}}," }, { "line": 672, "text": " {6, {23, 34, 76, 99, 121, 161, 0}}," } ], "snippets": [ { "needle": "CRC12", "start_line": 156, "end_line": 186, "lines": [ { "line": 156, "text": "// Constants" }, { "line": 157, "text": "/******************************************************************************/" }, { "line": 158, "text": "" }, { "line": 159, "text": "namespace {" }, { "line": 160, "text": "/* COMMON PARAMETERS */" }, { "line": 161, "text": "" }, { "line": 162, "text": "// !Common" }, { "line": 163, "text": "//" }, { "line": 164, "text": "// parameter (KK=87) !Information bits (75 + CRC12)" }, { "line": 165, "text": "// parameter (ND=58) !Data symbols" }, { "line": 166, "text": "// parameter (NS=21) !Sync symbols (3 @ Costas 7x7)" }, { "line": 167, "text": "// parameter (NN=NS+ND) !Total channel symbols (79)" }, { "line": 168, "text": "// parameter (ASYNCMIN=1.5) !Minimum Sync" }, { "line": 169, "text": "// parameter (NFSRCH=5) !Search frequency range in Hz (i.e.," }, { "line": 170, "text": "// +/- 2.5 Hz) parameter (NMAXCAND=300) !Maximum number of" }, { "line": 171, "text": "// candidate signals" }, { "line": 172, "text": "" }, { "line": 173, "text": "// Parameter\tValue\tDescription" }, { "line": 174, "text": "// KK\t87\tNumber of information bits (75 message bits + 12 CRC bits)." }, { "line": 175, "text": "// ND\t58\tNumber of data symbols in the JS8 transmission." }, { "line": 176, "text": "// NS\t21\tNumber of synchronization symbols (3 Costas arrays of size 7)." }, { "line": 177, "text": "// NN\t79\tTotal number of channel symbols (NN = NS + ND)." }, { "line": 178, "text": "// ASYNCMIN\t1.5\tMinimum sync value for successful decoding." }, { "line": 179, "text": "// NFSRCH\t5\tSearch frequency range in Hz (±2.5 Hz)." }, { "line": 180, "text": "// NMAXCAND\t300\tMaximum number of candidate signals." }, { "line": 181, "text": "" }, { "line": 182, "text": "constexpr int N = 174; // Total bits" }, { "line": 183, "text": "constexpr int K = 87; // Message bits" }, { "line": 184, "text": "constexpr int M = N - K; // Check bits" }, { "line": 185, "text": "constexpr int KK = 87; // Information bits (75 + CRC12)" }, { "line": 186, "text": "constexpr int ND = 58; // Data symbols" } ] }, { "needle": "checkCRC12", "start_line": 887, "end_line": 917, "lines": [ { "line": 887, "text": "static_assert(alphabetWord('a') == 36);" }, { "line": 888, "text": "static_assert(alphabetWord('-') == 62);" }, { "line": 889, "text": "static_assert(alphabetWord('+') == 63);" }, { "line": 890, "text": "" }, { "line": 891, "text": "template std::uint16_t CRC12(T const &range) {" }, { "line": 892, "text": " return boost::augmented_crc<12, 0xc06>(range.data(), range.size()) ^ 42;" }, { "line": 893, "text": "}" }, { "line": 894, "text": "" }, { "line": 895, "text": "bool checkCRC12(std::array const &decoded) {" }, { "line": 896, "text": " std::array bits = {};" }, { "line": 897, "text": "" }, { "line": 898, "text": " for (std::size_t i = 0; i < decoded.size(); ++i) {" }, { "line": 899, "text": " if (decoded[i])" }, { "line": 900, "text": " bits[i / 8] |= (1 << (7 - (i % 8)));" }, { "line": 901, "text": " }" }, { "line": 902, "text": "" }, { "line": 903, "text": " // Extract the received CRC-12." }, { "line": 904, "text": "" }, { "line": 905, "text": " uint16_t crc = (static_cast(bits[9] & 0x1F) << 7) |" }, { "line": 906, "text": " (static_cast(bits[10]) >> 1);" }, { "line": 907, "text": "" }, { "line": 908, "text": " // Clear bits that correspond to the CRC in the last bytes." }, { "line": 909, "text": "" }, { "line": 910, "text": " bits[9] &= 0xE0;" }, { "line": 911, "text": " bits[10] = 0x00;" }, { "line": 912, "text": "" }, { "line": 913, "text": " // Compute CRC and indicate if we have a match." }, { "line": 914, "text": "" }, { "line": 915, "text": " return crc == CRC12(bits);" }, { "line": 916, "text": "}" }, { "line": 917, "text": "" } ] }, { "needle": "extractmessage174", "start_line": 910, "end_line": 940, "lines": [ { "line": 910, "text": " bits[9] &= 0xE0;" }, { "line": 911, "text": " bits[10] = 0x00;" }, { "line": 912, "text": "" }, { "line": 913, "text": " // Compute CRC and indicate if we have a match." }, { "line": 914, "text": "" }, { "line": 915, "text": " return crc == CRC12(bits);" }, { "line": 916, "text": "}" }, { "line": 917, "text": "" }, { "line": 918, "text": "std::string extractmessage174(std::array const &decoded) {" }, { "line": 919, "text": " std::string message;" }, { "line": 920, "text": "" }, { "line": 921, "text": " // Ensure received CRC matches computed CRC." }, { "line": 922, "text": "" }, { "line": 923, "text": " if (checkCRC12(decoded)) {" }, { "line": 924, "text": " message.reserve(12);" }, { "line": 925, "text": "" }, { "line": 926, "text": " // Decode the message from the 72 data bits" }, { "line": 927, "text": "" }, { "line": 928, "text": " std::array words;" }, { "line": 929, "text": "" }, { "line": 930, "text": " for (std::size_t i = 0; i < 12; ++i) {" }, { "line": 931, "text": " words[i] = (decoded[i * 6 + 0] << 5) | (decoded[i * 6 + 1] << 4) |" }, { "line": 932, "text": " (decoded[i * 6 + 2] << 3) | (decoded[i * 6 + 3] << 2) |" }, { "line": 933, "text": " (decoded[i * 6 + 4] << 1) | (decoded[i * 6 + 5] << 0);" }, { "line": 934, "text": " }" }, { "line": 935, "text": "" }, { "line": 936, "text": " // Map 6-bit words to the alphabet" }, { "line": 937, "text": "" }, { "line": 938, "text": " for (auto const word : words)" }, { "line": 939, "text": " message += alphabet[word];" }, { "line": 940, "text": " }" } ] }, { "needle": "for (std::size_t i = 0; i < 12", "start_line": 922, "end_line": 952, "lines": [ { "line": 922, "text": "" }, { "line": 923, "text": " if (checkCRC12(decoded)) {" }, { "line": 924, "text": " message.reserve(12);" }, { "line": 925, "text": "" }, { "line": 926, "text": " // Decode the message from the 72 data bits" }, { "line": 927, "text": "" }, { "line": 928, "text": " std::array words;" }, { "line": 929, "text": "" }, { "line": 930, "text": " for (std::size_t i = 0; i < 12; ++i) {" }, { "line": 931, "text": " words[i] = (decoded[i * 6 + 0] << 5) | (decoded[i * 6 + 1] << 4) |" }, { "line": 932, "text": " (decoded[i * 6 + 2] << 3) | (decoded[i * 6 + 3] << 2) |" }, { "line": 933, "text": " (decoded[i * 6 + 4] << 1) | (decoded[i * 6 + 5] << 0);" }, { "line": 934, "text": " }" }, { "line": 935, "text": "" }, { "line": 936, "text": " // Map 6-bit words to the alphabet" }, { "line": 937, "text": "" }, { "line": 938, "text": " for (auto const word : words)" }, { "line": 939, "text": " message += alphabet[word];" }, { "line": 940, "text": " }" }, { "line": 941, "text": "" }, { "line": 942, "text": " return message;" }, { "line": 943, "text": "}" }, { "line": 944, "text": "" }, { "line": 945, "text": "// Parity matrix for JS8 message generation." }, { "line": 946, "text": "//" }, { "line": 947, "text": "// This should be 952 bytes in size; to store an 87x87 matrix of bits," }, { "line": 948, "text": "// you need 7569 bits, which requires 119 64-bit values, or 952 bytes." }, { "line": 949, "text": "//" }, { "line": 950, "text": "// Background here is that this is a low-density parity check code (LDPC)," }, { "line": 951, "text": "// generated using the PEG algorithm. In short, true values in a row i of" }, { "line": 952, "text": "// the matrix define which of the 87 message bits must be summed, modulo" } ] } ], "decodes": [], "warnings": [ "This step does not decode JS8 text and does not start JS8Call GUI/Qt.", "The previous Message91/CRC14 probes are now treated as diagnostic-only for this JS8Call-Improved source path." ], "next_action": "Implement a source-aligned message174 CRC12 candidate probe: pack decoded bits MSB-first into bytes, extract CRC from bits[9]&0x1F and bits[10]>>1, clear bits[9]/bits[10], compute CRC12 poly 0xc06 xor 42, then test source-derived dewhitening/deinterleaver candidates.", "outputs": { "source_message174_crc12_contract_json": "/decoders/js8_decoder/logs/step55_source_message174_crc12_contract.json" } } [webftr-js8-lab] OK [webftr-js8-lab] log file: /decoders/js8_decoder/logs/20260527T125938Z_source-message174-crc12-contract.log [webftr-js8-lab] manifest: /decoders/js8_decoder/logs/20260527T125938Z_source-message174-crc12-contract_manifest.json