diff --git a/CMakeLists.txt b/CMakeLists.txt
index 19e72db17..83b451918 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -494,6 +494,7 @@ set (qra_CSRCS
lib/qra/qracodes/qra12_63_64_irr_b.c
lib/qra/qracodes/qra13_64_64_irr_e.c
lib/qra/qracodes/qracodes.c
+ lib/qra/qracodes/normrnd.c
)
set (wsjt_CSRCS
diff --git a/lib/qra/qra64/fadampgauss.c b/lib/qra/qra64/fadampgauss.c
new file mode 100644
index 000000000..36093260a
--- /dev/null
+++ b/lib/qra/qra64/fadampgauss.c
@@ -0,0 +1,327 @@
+/*
+fadampgauss.c
+Gaussian fading tables for QRA64 modes
+
+(c) 2016 - Nico Palermo, IV3NWV
+
+This file is part of the qracodes project, a Forward Error Control
+encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes.
+-------------------------------------------------------------------------------
+
+ qracodes is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ qracodes is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with qracodes source distribution.
+ If not, see .
+
+-----------------------------------------------------------------------------
+
+*/
+static const int hlen_tab_gauss[64] = {
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 5, 5, 5, 6,
+ 6, 6, 7, 7, 8, 8, 9, 10,
+ 10, 11, 12, 13, 14, 15, 17, 18,
+ 19, 21, 23, 25, 27, 29, 32, 34,
+ 37, 41, 44, 48, 52, 57, 62, 65
+};
+static const float hgauss1[2] = {
+0.1722f, 0.9540f
+};
+static const float hgauss2[2] = {
+0.1870f, 0.9463f
+};
+static const float hgauss3[2] = {
+0.2028f, 0.9374f
+};
+static const float hgauss4[2] = {
+0.2198f, 0.9273f
+};
+static const float hgauss5[2] = {
+0.2378f, 0.9158f
+};
+static const float hgauss6[2] = {
+0.2569f, 0.9030f
+};
+static const float hgauss7[2] = {
+0.2769f, 0.8887f
+};
+static const float hgauss8[2] = {
+0.2977f, 0.8730f
+};
+static const float hgauss9[2] = {
+0.3190f, 0.8559f
+};
+static const float hgauss10[2] = {
+0.3405f, 0.8374f
+};
+static const float hgauss11[2] = {
+0.3619f, 0.8177f
+};
+static const float hgauss12[2] = {
+0.3828f, 0.7970f
+};
+static const float hgauss13[2] = {
+0.4026f, 0.7755f
+};
+static const float hgauss14[2] = {
+0.4208f, 0.7533f
+};
+static const float hgauss15[2] = {
+0.4371f, 0.7307f
+};
+static const float hgauss16[2] = {
+0.4510f, 0.7078f
+};
+static const float hgauss17[3] = {
+0.1729f, 0.4621f, 0.6849f
+};
+static const float hgauss18[3] = {
+0.1920f, 0.4704f, 0.6620f
+};
+static const float hgauss19[3] = {
+0.2131f, 0.4757f, 0.6393f
+};
+static const float hgauss20[3] = {
+0.2350f, 0.4782f, 0.6170f
+};
+static const float hgauss21[3] = {
+0.2565f, 0.4779f, 0.5949f
+};
+static const float hgauss22[3] = {
+0.2767f, 0.4752f, 0.5733f
+};
+static const float hgauss23[3] = {
+0.2948f, 0.4703f, 0.5522f
+};
+static const float hgauss24[3] = {
+0.3102f, 0.4635f, 0.5316f
+};
+static const float hgauss25[4] = {
+0.1874f, 0.3226f, 0.4551f, 0.5115f
+};
+static const float hgauss26[4] = {
+0.2071f, 0.3319f, 0.4454f, 0.4919f
+};
+static const float hgauss27[4] = {
+0.2253f, 0.3383f, 0.4347f, 0.4730f
+};
+static const float hgauss28[4] = {
+0.2413f, 0.3419f, 0.4232f, 0.4546f
+};
+static const float hgauss29[5] = {
+0.1701f, 0.2546f, 0.3429f, 0.4110f, 0.4368f
+};
+static const float hgauss30[5] = {
+0.1874f, 0.2652f, 0.3417f, 0.3985f, 0.4196f
+};
+static const float hgauss31[5] = {
+0.2028f, 0.2729f, 0.3386f, 0.3857f, 0.4029f
+};
+static const float hgauss32[6] = {
+0.1569f, 0.2158f, 0.2780f, 0.3338f, 0.3728f, 0.3868f
+};
+static const float hgauss33[6] = {
+0.1723f, 0.2263f, 0.2807f, 0.3278f, 0.3599f, 0.3713f
+};
+static const float hgauss34[6] = {
+0.1857f, 0.2343f, 0.2812f, 0.3207f, 0.3471f, 0.3564f
+};
+static const float hgauss35[7] = {
+0.1550f, 0.1968f, 0.2398f, 0.2799f, 0.3128f, 0.3344f, 0.3420f
+};
+static const float hgauss36[7] = {
+0.1677f, 0.2055f, 0.2430f, 0.2770f, 0.3043f, 0.3220f, 0.3281f
+};
+static const float hgauss37[8] = {
+0.1456f, 0.1783f, 0.2118f, 0.2442f, 0.2728f, 0.2953f, 0.3098f, 0.3147f
+};
+static const float hgauss38[8] = {
+0.1572f, 0.1866f, 0.2160f, 0.2436f, 0.2675f, 0.2861f, 0.2979f, 0.3019f
+};
+static const float hgauss39[9] = {
+0.1410f, 0.1667f, 0.1928f, 0.2182f, 0.2416f, 0.2615f, 0.2767f, 0.2863f,
+0.2895f
+};
+static const float hgauss40[10] = {
+0.1288f, 0.1511f, 0.1741f, 0.1970f, 0.2187f, 0.2383f, 0.2547f, 0.2672f,
+0.2750f, 0.2776f
+};
+static const float hgauss41[10] = {
+0.1391f, 0.1592f, 0.1795f, 0.1992f, 0.2176f, 0.2340f, 0.2476f, 0.2578f,
+0.2641f, 0.2662f
+};
+static const float hgauss42[11] = {
+0.1298f, 0.1475f, 0.1654f, 0.1830f, 0.1999f, 0.2153f, 0.2289f, 0.2401f,
+0.2484f, 0.2535f, 0.2552f
+};
+static const float hgauss43[12] = {
+0.1227f, 0.1382f, 0.1540f, 0.1696f, 0.1848f, 0.1991f, 0.2120f, 0.2232f,
+0.2324f, 0.2391f, 0.2433f, 0.2447f
+};
+static const float hgauss44[13] = {
+0.1173f, 0.1309f, 0.1448f, 0.1587f, 0.1722f, 0.1851f, 0.1971f, 0.2078f,
+0.2171f, 0.2246f, 0.2301f, 0.2334f, 0.2346f
+};
+static const float hgauss45[14] = {
+0.1132f, 0.1253f, 0.1375f, 0.1497f, 0.1617f, 0.1732f, 0.1841f, 0.1941f,
+0.2030f, 0.2106f, 0.2167f, 0.2212f, 0.2239f, 0.2248f
+};
+static const float hgauss46[15] = {
+0.1102f, 0.1208f, 0.1315f, 0.1423f, 0.1529f, 0.1632f, 0.1730f, 0.1821f,
+0.1904f, 0.1978f, 0.2040f, 0.2089f, 0.2126f, 0.2148f, 0.2155f
+};
+static const float hgauss47[17] = {
+0.0987f, 0.1079f, 0.1173f, 0.1267f, 0.1362f, 0.1455f, 0.1546f, 0.1634f,
+0.1716f, 0.1792f, 0.1861f, 0.1921f, 0.1972f, 0.2012f, 0.2042f, 0.2059f,
+0.2065f
+};
+static const float hgauss48[18] = {
+0.0980f, 0.1062f, 0.1145f, 0.1228f, 0.1311f, 0.1393f, 0.1474f, 0.1551f,
+0.1624f, 0.1693f, 0.1756f, 0.1813f, 0.1862f, 0.1904f, 0.1936f, 0.1960f,
+0.1975f, 0.1979f
+};
+static const float hgauss49[19] = {
+0.0976f, 0.1049f, 0.1122f, 0.1195f, 0.1268f, 0.1341f, 0.1411f, 0.1479f,
+0.1544f, 0.1606f, 0.1663f, 0.1715f, 0.1762f, 0.1802f, 0.1836f, 0.1862f,
+0.1881f, 0.1893f, 0.1897f
+};
+static const float hgauss50[21] = {
+0.0911f, 0.0974f, 0.1038f, 0.1103f, 0.1167f, 0.1232f, 0.1295f, 0.1357f,
+0.1417f, 0.1474f, 0.1529f, 0.1580f, 0.1627f, 0.1670f, 0.1708f, 0.1741f,
+0.1768f, 0.1790f, 0.1805f, 0.1815f, 0.1818f
+};
+static const float hgauss51[23] = {
+0.0861f, 0.0916f, 0.0973f, 0.1029f, 0.1086f, 0.1143f, 0.1199f, 0.1255f,
+0.1309f, 0.1361f, 0.1412f, 0.1460f, 0.1505f, 0.1548f, 0.1587f, 0.1622f,
+0.1653f, 0.1679f, 0.1702f, 0.1719f, 0.1732f, 0.1739f, 0.1742f
+};
+static const float hgauss52[25] = {
+0.0823f, 0.0872f, 0.0922f, 0.0971f, 0.1021f, 0.1071f, 0.1121f, 0.1170f,
+0.1219f, 0.1266f, 0.1312f, 0.1356f, 0.1398f, 0.1438f, 0.1476f, 0.1511f,
+0.1543f, 0.1572f, 0.1597f, 0.1619f, 0.1637f, 0.1651f, 0.1661f, 0.1667f,
+0.1669f
+};
+static const float hgauss53[27] = {
+0.0795f, 0.0838f, 0.0882f, 0.0925f, 0.0969f, 0.1013f, 0.1057f, 0.1101f,
+0.1144f, 0.1186f, 0.1227f, 0.1267f, 0.1306f, 0.1343f, 0.1378f, 0.1411f,
+0.1442f, 0.1471f, 0.1497f, 0.1520f, 0.1541f, 0.1558f, 0.1573f, 0.1585f,
+0.1593f, 0.1598f, 0.1599f
+};
+static const float hgauss54[29] = {
+0.0774f, 0.0812f, 0.0850f, 0.0889f, 0.0928f, 0.0966f, 0.1005f, 0.1043f,
+0.1081f, 0.1119f, 0.1155f, 0.1191f, 0.1226f, 0.1259f, 0.1292f, 0.1322f,
+0.1351f, 0.1379f, 0.1404f, 0.1428f, 0.1449f, 0.1468f, 0.1485f, 0.1499f,
+0.1511f, 0.1520f, 0.1527f, 0.1531f, 0.1532f
+};
+static const float hgauss55[32] = {
+0.0726f, 0.0759f, 0.0792f, 0.0826f, 0.0860f, 0.0894f, 0.0928f, 0.0962f,
+0.0996f, 0.1029f, 0.1062f, 0.1094f, 0.1126f, 0.1157f, 0.1187f, 0.1217f,
+0.1245f, 0.1271f, 0.1297f, 0.1321f, 0.1343f, 0.1364f, 0.1383f, 0.1401f,
+0.1416f, 0.1430f, 0.1442f, 0.1451f, 0.1459f, 0.1464f, 0.1467f, 0.1468f
+};
+static const float hgauss56[34] = {
+0.0718f, 0.0747f, 0.0777f, 0.0807f, 0.0836f, 0.0866f, 0.0896f, 0.0926f,
+0.0956f, 0.0985f, 0.1014f, 0.1043f, 0.1071f, 0.1098f, 0.1125f, 0.1151f,
+0.1176f, 0.1201f, 0.1224f, 0.1246f, 0.1267f, 0.1287f, 0.1305f, 0.1322f,
+0.1338f, 0.1352f, 0.1365f, 0.1376f, 0.1385f, 0.1393f, 0.1399f, 0.1403f,
+0.1406f, 0.1407f
+};
+static const float hgauss57[37] = {
+0.0687f, 0.0712f, 0.0738f, 0.0765f, 0.0791f, 0.0817f, 0.0843f, 0.0870f,
+0.0896f, 0.0922f, 0.0948f, 0.0973f, 0.0998f, 0.1023f, 0.1047f, 0.1071f,
+0.1094f, 0.1117f, 0.1138f, 0.1159f, 0.1179f, 0.1199f, 0.1217f, 0.1234f,
+0.1250f, 0.1265f, 0.1279f, 0.1292f, 0.1304f, 0.1314f, 0.1323f, 0.1330f,
+0.1337f, 0.1341f, 0.1345f, 0.1347f, 0.1348f
+};
+static const float hgauss58[41] = {
+0.0640f, 0.0663f, 0.0686f, 0.0709f, 0.0732f, 0.0755f, 0.0778f, 0.0801f,
+0.0824f, 0.0847f, 0.0870f, 0.0893f, 0.0915f, 0.0938f, 0.0960f, 0.0982f,
+0.1003f, 0.1024f, 0.1044f, 0.1064f, 0.1083f, 0.1102f, 0.1120f, 0.1137f,
+0.1154f, 0.1170f, 0.1185f, 0.1199f, 0.1212f, 0.1224f, 0.1236f, 0.1246f,
+0.1255f, 0.1264f, 0.1271f, 0.1277f, 0.1282f, 0.1286f, 0.1289f, 0.1291f,
+0.1291f
+};
+static const float hgauss59[44] = {
+0.0625f, 0.0645f, 0.0665f, 0.0685f, 0.0705f, 0.0726f, 0.0746f, 0.0767f,
+0.0787f, 0.0807f, 0.0827f, 0.0847f, 0.0867f, 0.0887f, 0.0907f, 0.0926f,
+0.0945f, 0.0964f, 0.0982f, 0.1000f, 0.1017f, 0.1034f, 0.1051f, 0.1067f,
+0.1083f, 0.1097f, 0.1112f, 0.1125f, 0.1138f, 0.1151f, 0.1162f, 0.1173f,
+0.1183f, 0.1192f, 0.1201f, 0.1208f, 0.1215f, 0.1221f, 0.1226f, 0.1230f,
+0.1233f, 0.1235f, 0.1237f, 0.1237f
+};
+static const float hgauss60[48] = {
+0.0596f, 0.0614f, 0.0631f, 0.0649f, 0.0667f, 0.0685f, 0.0703f, 0.0721f,
+0.0738f, 0.0756f, 0.0774f, 0.0792f, 0.0810f, 0.0827f, 0.0845f, 0.0862f,
+0.0879f, 0.0896f, 0.0912f, 0.0929f, 0.0945f, 0.0960f, 0.0976f, 0.0991f,
+0.1005f, 0.1019f, 0.1033f, 0.1046f, 0.1059f, 0.1071f, 0.1083f, 0.1094f,
+0.1105f, 0.1115f, 0.1124f, 0.1133f, 0.1141f, 0.1149f, 0.1156f, 0.1162f,
+0.1167f, 0.1172f, 0.1176f, 0.1179f, 0.1182f, 0.1184f, 0.1185f, 0.1185f
+};
+static const float hgauss61[52] = {
+0.0575f, 0.0590f, 0.0606f, 0.0621f, 0.0637f, 0.0652f, 0.0668f, 0.0684f,
+0.0700f, 0.0715f, 0.0731f, 0.0747f, 0.0762f, 0.0778f, 0.0793f, 0.0809f,
+0.0824f, 0.0839f, 0.0854f, 0.0868f, 0.0883f, 0.0897f, 0.0911f, 0.0925f,
+0.0938f, 0.0951f, 0.0964f, 0.0976f, 0.0988f, 0.1000f, 0.1011f, 0.1022f,
+0.1033f, 0.1043f, 0.1053f, 0.1062f, 0.1070f, 0.1079f, 0.1086f, 0.1093f,
+0.1100f, 0.1106f, 0.1112f, 0.1116f, 0.1121f, 0.1125f, 0.1128f, 0.1131f,
+0.1133f, 0.1134f, 0.1135f, 0.1135f
+};
+static const float hgauss62[57] = {
+0.0545f, 0.0558f, 0.0572f, 0.0586f, 0.0599f, 0.0613f, 0.0627f, 0.0641f,
+0.0654f, 0.0668f, 0.0682f, 0.0696f, 0.0710f, 0.0723f, 0.0737f, 0.0751f,
+0.0764f, 0.0778f, 0.0791f, 0.0804f, 0.0817f, 0.0830f, 0.0843f, 0.0855f,
+0.0868f, 0.0880f, 0.0892f, 0.0903f, 0.0915f, 0.0926f, 0.0937f, 0.0948f,
+0.0958f, 0.0968f, 0.0977f, 0.0987f, 0.0996f, 0.1004f, 0.1013f, 0.1020f,
+0.1028f, 0.1035f, 0.1042f, 0.1048f, 0.1054f, 0.1059f, 0.1064f, 0.1068f,
+0.1072f, 0.1076f, 0.1079f, 0.1082f, 0.1084f, 0.1085f, 0.1087f, 0.1087f,
+0.1088f
+};
+static const float hgauss63[62] = {
+0.0522f, 0.0534f, 0.0546f, 0.0558f, 0.0570f, 0.0582f, 0.0594f, 0.0606f,
+0.0619f, 0.0631f, 0.0643f, 0.0655f, 0.0667f, 0.0679f, 0.0691f, 0.0703f,
+0.0715f, 0.0727f, 0.0739f, 0.0751f, 0.0763f, 0.0774f, 0.0786f, 0.0797f,
+0.0808f, 0.0819f, 0.0830f, 0.0841f, 0.0851f, 0.0861f, 0.0872f, 0.0882f,
+0.0891f, 0.0901f, 0.0910f, 0.0919f, 0.0928f, 0.0936f, 0.0944f, 0.0952f,
+0.0960f, 0.0967f, 0.0974f, 0.0981f, 0.0987f, 0.0994f, 0.0999f, 0.1005f,
+0.1010f, 0.1014f, 0.1019f, 0.1023f, 0.1026f, 0.1030f, 0.1033f, 0.1035f,
+0.1037f, 0.1039f, 0.1040f, 0.1041f, 0.1042f, 0.1042f
+};
+static const float hgauss64[65] = {
+0.0526f, 0.0537f, 0.0547f, 0.0558f, 0.0569f, 0.0579f, 0.0590f, 0.0601f,
+0.0611f, 0.0622f, 0.0633f, 0.0643f, 0.0654f, 0.0665f, 0.0675f, 0.0686f,
+0.0696f, 0.0707f, 0.0717f, 0.0727f, 0.0737f, 0.0748f, 0.0758f, 0.0767f,
+0.0777f, 0.0787f, 0.0796f, 0.0806f, 0.0815f, 0.0824f, 0.0833f, 0.0842f,
+0.0850f, 0.0859f, 0.0867f, 0.0875f, 0.0883f, 0.0891f, 0.0898f, 0.0905f,
+0.0912f, 0.0919f, 0.0925f, 0.0932f, 0.0938f, 0.0943f, 0.0949f, 0.0954f,
+0.0959f, 0.0964f, 0.0968f, 0.0972f, 0.0976f, 0.0979f, 0.0983f, 0.0986f,
+0.0988f, 0.0991f, 0.0993f, 0.0994f, 0.0996f, 0.0997f, 0.0998f, 0.0998f,
+0.0998f
+};
+static const float *hptr_tab_gauss[64] = {
+hgauss1, hgauss2, hgauss3, hgauss4,
+hgauss5, hgauss6, hgauss7, hgauss8,
+hgauss9, hgauss10, hgauss11, hgauss12,
+hgauss13, hgauss14, hgauss15, hgauss16,
+hgauss17, hgauss18, hgauss19, hgauss20,
+hgauss21, hgauss22, hgauss23, hgauss24,
+hgauss25, hgauss26, hgauss27, hgauss28,
+hgauss29, hgauss30, hgauss31, hgauss32,
+hgauss33, hgauss34, hgauss35, hgauss36,
+hgauss37, hgauss38, hgauss39, hgauss40,
+hgauss41, hgauss42, hgauss43, hgauss44,
+hgauss45, hgauss46, hgauss47, hgauss48,
+hgauss49, hgauss50, hgauss51, hgauss52,
+hgauss53, hgauss54, hgauss55, hgauss56,
+hgauss57, hgauss58, hgauss59, hgauss60,
+hgauss61, hgauss62, hgauss63, hgauss64
+};
diff --git a/lib/qra/qra64/fadamplorentz.c b/lib/qra/qra64/fadamplorentz.c
new file mode 100644
index 000000000..d57312d3c
--- /dev/null
+++ b/lib/qra/qra64/fadamplorentz.c
@@ -0,0 +1,329 @@
+/*
+fadamplorentz.c
+Lorentz fading tables for QRA64 modes
+
+(c) 2016 - Nico Palermo, IV3NWV
+
+This file is part of the qracodes project, a Forward Error Control
+encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes.
+-------------------------------------------------------------------------------
+
+ qracodes is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ qracodes is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with qracodes source distribution.
+ If not, see .
+
+-----------------------------------------------------------------------------
+*/
+
+static const int hlen_tab_lorentz[64] = {
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 3, 3,
+ 3, 3, 3, 3, 3, 4, 4, 4,
+ 4, 4, 5, 5, 5, 5, 6, 6,
+ 7, 7, 7, 8, 8, 9, 10, 10,
+ 11, 12, 13, 14, 15, 16, 17, 19,
+ 20, 22, 23, 25, 27, 30, 32, 35,
+ 38, 41, 45, 49, 53, 57, 62, 65
+};
+static const float hlorentz1[2] = {
+0.1464f, 0.9543f
+};
+static const float hlorentz2[2] = {
+0.1563f, 0.9503f
+};
+static const float hlorentz3[2] = {
+0.1672f, 0.9461f
+};
+static const float hlorentz4[2] = {
+0.1773f, 0.9415f
+};
+static const float hlorentz5[2] = {
+0.1868f, 0.9367f
+};
+static const float hlorentz6[2] = {
+0.1970f, 0.9314f
+};
+static const float hlorentz7[2] = {
+0.2063f, 0.9258f
+};
+static const float hlorentz8[2] = {
+0.2151f, 0.9197f
+};
+static const float hlorentz9[2] = {
+0.2236f, 0.9132f
+};
+static const float hlorentz10[2] = {
+0.2320f, 0.9061f
+};
+static const float hlorentz11[2] = {
+0.2406f, 0.8986f
+};
+static const float hlorentz12[2] = {
+0.2494f, 0.8905f
+};
+static const float hlorentz13[2] = {
+0.2584f, 0.8819f
+};
+static const float hlorentz14[2] = {
+0.2674f, 0.8727f
+};
+static const float hlorentz15[3] = {
+0.1400f, 0.2765f, 0.8629f
+};
+static const float hlorentz16[3] = {
+0.1451f, 0.2857f, 0.8525f
+};
+static const float hlorentz17[3] = {
+0.1504f, 0.2949f, 0.8414f
+};
+static const float hlorentz18[3] = {
+0.1557f, 0.3041f, 0.8298f
+};
+static const float hlorentz19[3] = {
+0.1611f, 0.3133f, 0.8174f
+};
+static const float hlorentz20[3] = {
+0.1666f, 0.3223f, 0.8045f
+};
+static const float hlorentz21[3] = {
+0.1721f, 0.3312f, 0.7909f
+};
+static const float hlorentz22[4] = {
+0.1195f, 0.1778f, 0.3399f, 0.7766f
+};
+static const float hlorentz23[4] = {
+0.1235f, 0.1835f, 0.3483f, 0.7618f
+};
+static const float hlorentz24[4] = {
+0.1277f, 0.1893f, 0.3564f, 0.7463f
+};
+static const float hlorentz25[4] = {
+0.1319f, 0.1952f, 0.3641f, 0.7303f
+};
+static const float hlorentz26[4] = {
+0.1362f, 0.2011f, 0.3712f, 0.7138f
+};
+static const float hlorentz27[5] = {
+0.1062f, 0.1407f, 0.2071f, 0.3779f, 0.6967f
+};
+static const float hlorentz28[5] = {
+0.1097f, 0.1452f, 0.2132f, 0.3839f, 0.6793f
+};
+static const float hlorentz29[5] = {
+0.1134f, 0.1499f, 0.2193f, 0.3891f, 0.6615f
+};
+static const float hlorentz30[5] = {
+0.1172f, 0.1546f, 0.2254f, 0.3936f, 0.6434f
+};
+static const float hlorentz31[6] = {
+0.0974f, 0.1211f, 0.1595f, 0.2314f, 0.3973f, 0.6251f
+};
+static const float hlorentz32[6] = {
+0.1007f, 0.1251f, 0.1645f, 0.2374f, 0.4000f, 0.6066f
+};
+static const float hlorentz33[7] = {
+0.0872f, 0.1042f, 0.1292f, 0.1695f, 0.2434f, 0.4017f, 0.5880f
+};
+static const float hlorentz34[7] = {
+0.0902f, 0.1077f, 0.1335f, 0.1747f, 0.2491f, 0.4025f, 0.5694f
+};
+static const float hlorentz35[7] = {
+0.0934f, 0.1114f, 0.1379f, 0.1799f, 0.2547f, 0.4022f, 0.5509f
+};
+static const float hlorentz36[8] = {
+0.0832f, 0.0967f, 0.1152f, 0.1423f, 0.1851f, 0.2600f, 0.4008f, 0.5325f
+};
+static const float hlorentz37[8] = {
+0.0862f, 0.1001f, 0.1192f, 0.1469f, 0.1903f, 0.2649f, 0.3985f, 0.5143f
+};
+static const float hlorentz38[9] = {
+0.0784f, 0.0893f, 0.1036f, 0.1232f, 0.1515f, 0.1955f, 0.2694f, 0.3950f,
+0.4964f
+};
+static const float hlorentz39[10] = {
+0.0724f, 0.0813f, 0.0925f, 0.1072f, 0.1273f, 0.1562f, 0.2005f, 0.2733f,
+0.3906f, 0.4787f
+};
+static const float hlorentz40[10] = {
+0.0751f, 0.0842f, 0.0958f, 0.1109f, 0.1315f, 0.1609f, 0.2054f, 0.2767f,
+0.3853f, 0.4614f
+};
+static const float hlorentz41[11] = {
+0.0703f, 0.0779f, 0.0873f, 0.0992f, 0.1148f, 0.1358f, 0.1656f, 0.2101f,
+0.2794f, 0.3790f, 0.4444f
+};
+static const float hlorentz42[12] = {
+0.0665f, 0.0730f, 0.0808f, 0.0905f, 0.1027f, 0.1187f, 0.1401f, 0.1702f,
+0.2145f, 0.2813f, 0.3720f, 0.4278f
+};
+static const float hlorentz43[13] = {
+0.0634f, 0.0690f, 0.0757f, 0.0838f, 0.0938f, 0.1063f, 0.1226f, 0.1444f,
+0.1747f, 0.2185f, 0.2824f, 0.3643f, 0.4117f
+};
+static const float hlorentz44[14] = {
+0.0609f, 0.0658f, 0.0716f, 0.0785f, 0.0869f, 0.0971f, 0.1100f, 0.1266f,
+0.1487f, 0.1791f, 0.2220f, 0.2826f, 0.3559f, 0.3960f
+};
+static const float hlorentz45[15] = {
+0.0588f, 0.0632f, 0.0683f, 0.0743f, 0.0814f, 0.0900f, 0.1005f, 0.1137f,
+0.1306f, 0.1529f, 0.1831f, 0.2250f, 0.2820f, 0.3470f, 0.3808f
+};
+static const float hlorentz46[16] = {
+0.0571f, 0.0611f, 0.0657f, 0.0709f, 0.0771f, 0.0844f, 0.0932f, 0.1040f,
+0.1175f, 0.1346f, 0.1570f, 0.1869f, 0.2274f, 0.2804f, 0.3377f, 0.3660f
+};
+static const float hlorentz47[17] = {
+0.0557f, 0.0594f, 0.0635f, 0.0682f, 0.0736f, 0.0800f, 0.0875f, 0.0965f,
+0.1076f, 0.1212f, 0.1385f, 0.1609f, 0.1903f, 0.2292f, 0.2781f, 0.3281f,
+0.3517f
+};
+static const float hlorentz48[19] = {
+0.0516f, 0.0546f, 0.0579f, 0.0617f, 0.0659f, 0.0708f, 0.0764f, 0.0829f,
+0.0906f, 0.0999f, 0.1111f, 0.1249f, 0.1423f, 0.1645f, 0.1933f, 0.2302f,
+0.2748f, 0.3182f, 0.3379f
+};
+static const float hlorentz49[20] = {
+0.0509f, 0.0537f, 0.0567f, 0.0602f, 0.0640f, 0.0684f, 0.0734f, 0.0792f,
+0.0859f, 0.0938f, 0.1032f, 0.1146f, 0.1286f, 0.1460f, 0.1679f, 0.1957f,
+0.2305f, 0.2709f, 0.3082f, 0.3245f
+};
+static const float hlorentz50[22] = {
+0.0480f, 0.0504f, 0.0529f, 0.0558f, 0.0589f, 0.0625f, 0.0665f, 0.0710f,
+0.0761f, 0.0821f, 0.0889f, 0.0970f, 0.1066f, 0.1181f, 0.1321f, 0.1494f,
+0.1709f, 0.1976f, 0.2300f, 0.2662f, 0.2981f, 0.3115f
+};
+static const float hlorentz51[23] = {
+0.0477f, 0.0499f, 0.0523f, 0.0550f, 0.0580f, 0.0612f, 0.0649f, 0.0690f,
+0.0736f, 0.0789f, 0.0850f, 0.0920f, 0.1002f, 0.1099f, 0.1215f, 0.1355f,
+0.1526f, 0.1735f, 0.1989f, 0.2288f, 0.2609f, 0.2880f, 0.2991f
+};
+static const float hlorentz52[25] = {
+0.0456f, 0.0475f, 0.0496f, 0.0519f, 0.0544f, 0.0572f, 0.0602f, 0.0636f,
+0.0673f, 0.0715f, 0.0763f, 0.0817f, 0.0879f, 0.0951f, 0.1034f, 0.1132f,
+0.1248f, 0.1387f, 0.1554f, 0.1755f, 0.1995f, 0.2268f, 0.2550f, 0.2779f,
+0.2870f
+};
+static const float hlorentz53[27] = {
+0.0438f, 0.0456f, 0.0474f, 0.0494f, 0.0516f, 0.0539f, 0.0565f, 0.0594f,
+0.0625f, 0.0660f, 0.0698f, 0.0742f, 0.0790f, 0.0846f, 0.0909f, 0.0981f,
+0.1065f, 0.1164f, 0.1279f, 0.1416f, 0.1579f, 0.1771f, 0.1994f, 0.2242f,
+0.2487f, 0.2680f, 0.2754f
+};
+static const float hlorentz54[30] = {
+0.0410f, 0.0424f, 0.0440f, 0.0456f, 0.0474f, 0.0493f, 0.0513f, 0.0536f,
+0.0560f, 0.0587f, 0.0616f, 0.0648f, 0.0684f, 0.0724f, 0.0768f, 0.0818f,
+0.0874f, 0.0938f, 0.1011f, 0.1096f, 0.1194f, 0.1308f, 0.1442f, 0.1599f,
+0.1781f, 0.1987f, 0.2208f, 0.2421f, 0.2582f, 0.2643f
+};
+static const float hlorentz55[32] = {
+0.0400f, 0.0413f, 0.0427f, 0.0441f, 0.0457f, 0.0474f, 0.0492f, 0.0512f,
+0.0533f, 0.0557f, 0.0582f, 0.0609f, 0.0639f, 0.0672f, 0.0709f, 0.0749f,
+0.0795f, 0.0845f, 0.0902f, 0.0967f, 0.1041f, 0.1125f, 0.1222f, 0.1334f,
+0.1464f, 0.1614f, 0.1785f, 0.1973f, 0.2169f, 0.2351f, 0.2485f, 0.2535f
+};
+static const float hlorentz56[35] = {
+0.0380f, 0.0391f, 0.0403f, 0.0416f, 0.0429f, 0.0444f, 0.0459f, 0.0475f,
+0.0493f, 0.0512f, 0.0532f, 0.0554f, 0.0578f, 0.0604f, 0.0632f, 0.0663f,
+0.0697f, 0.0734f, 0.0775f, 0.0821f, 0.0873f, 0.0930f, 0.0995f, 0.1069f,
+0.1153f, 0.1248f, 0.1358f, 0.1483f, 0.1624f, 0.1782f, 0.1952f, 0.2125f,
+0.2280f, 0.2391f, 0.2432f
+};
+static const float hlorentz57[38] = {
+0.0364f, 0.0374f, 0.0384f, 0.0395f, 0.0407f, 0.0419f, 0.0432f, 0.0446f,
+0.0461f, 0.0477f, 0.0494f, 0.0512f, 0.0531f, 0.0552f, 0.0575f, 0.0599f,
+0.0626f, 0.0655f, 0.0686f, 0.0721f, 0.0759f, 0.0801f, 0.0848f, 0.0899f,
+0.0957f, 0.1022f, 0.1095f, 0.1178f, 0.1271f, 0.1377f, 0.1496f, 0.1629f,
+0.1774f, 0.1926f, 0.2076f, 0.2207f, 0.2299f, 0.2332f
+};
+static const float hlorentz58[41] = {
+0.0351f, 0.0360f, 0.0369f, 0.0379f, 0.0389f, 0.0400f, 0.0411f, 0.0423f,
+0.0436f, 0.0450f, 0.0464f, 0.0479f, 0.0496f, 0.0513f, 0.0532f, 0.0552f,
+0.0573f, 0.0596f, 0.0621f, 0.0648f, 0.0678f, 0.0710f, 0.0745f, 0.0784f,
+0.0826f, 0.0873f, 0.0925f, 0.0983f, 0.1048f, 0.1120f, 0.1201f, 0.1291f,
+0.1392f, 0.1505f, 0.1628f, 0.1759f, 0.1894f, 0.2023f, 0.2134f, 0.2209f,
+0.2237f
+};
+static const float hlorentz59[45] = {
+0.0333f, 0.0341f, 0.0349f, 0.0357f, 0.0366f, 0.0375f, 0.0384f, 0.0394f,
+0.0405f, 0.0416f, 0.0428f, 0.0440f, 0.0453f, 0.0467f, 0.0482f, 0.0498f,
+0.0515f, 0.0532f, 0.0552f, 0.0572f, 0.0594f, 0.0618f, 0.0643f, 0.0671f,
+0.0701f, 0.0734f, 0.0770f, 0.0809f, 0.0851f, 0.0898f, 0.0950f, 0.1008f,
+0.1071f, 0.1142f, 0.1221f, 0.1307f, 0.1403f, 0.1508f, 0.1621f, 0.1739f,
+0.1857f, 0.1968f, 0.2060f, 0.2123f, 0.2145f
+};
+static const float hlorentz60[49] = {
+0.0319f, 0.0325f, 0.0332f, 0.0339f, 0.0347f, 0.0355f, 0.0363f, 0.0371f,
+0.0380f, 0.0390f, 0.0400f, 0.0410f, 0.0421f, 0.0432f, 0.0444f, 0.0457f,
+0.0471f, 0.0485f, 0.0500f, 0.0517f, 0.0534f, 0.0552f, 0.0572f, 0.0593f,
+0.0615f, 0.0640f, 0.0666f, 0.0694f, 0.0724f, 0.0757f, 0.0793f, 0.0833f,
+0.0875f, 0.0922f, 0.0974f, 0.1030f, 0.1093f, 0.1161f, 0.1237f, 0.1319f,
+0.1409f, 0.1506f, 0.1608f, 0.1713f, 0.1816f, 0.1910f, 0.1987f, 0.2038f,
+0.2056f
+};
+static const float hlorentz61[53] = {
+0.0307f, 0.0313f, 0.0319f, 0.0325f, 0.0332f, 0.0338f, 0.0346f, 0.0353f,
+0.0361f, 0.0369f, 0.0377f, 0.0386f, 0.0395f, 0.0405f, 0.0415f, 0.0426f,
+0.0437f, 0.0449f, 0.0462f, 0.0475f, 0.0489f, 0.0504f, 0.0519f, 0.0536f,
+0.0553f, 0.0572f, 0.0592f, 0.0614f, 0.0637f, 0.0661f, 0.0688f, 0.0716f,
+0.0747f, 0.0780f, 0.0816f, 0.0856f, 0.0898f, 0.0945f, 0.0996f, 0.1051f,
+0.1111f, 0.1177f, 0.1249f, 0.1327f, 0.1410f, 0.1499f, 0.1590f, 0.1683f,
+0.1771f, 0.1851f, 0.1915f, 0.1957f, 0.1971f
+};
+static const float hlorentz62[57] = {
+0.0297f, 0.0302f, 0.0308f, 0.0313f, 0.0319f, 0.0325f, 0.0332f, 0.0338f,
+0.0345f, 0.0352f, 0.0359f, 0.0367f, 0.0375f, 0.0384f, 0.0392f, 0.0401f,
+0.0411f, 0.0421f, 0.0432f, 0.0443f, 0.0454f, 0.0466f, 0.0479f, 0.0493f,
+0.0507f, 0.0522f, 0.0538f, 0.0555f, 0.0573f, 0.0592f, 0.0613f, 0.0635f,
+0.0658f, 0.0683f, 0.0710f, 0.0738f, 0.0769f, 0.0803f, 0.0839f, 0.0878f,
+0.0920f, 0.0965f, 0.1015f, 0.1069f, 0.1127f, 0.1190f, 0.1258f, 0.1330f,
+0.1406f, 0.1486f, 0.1568f, 0.1648f, 0.1724f, 0.1791f, 0.1844f, 0.1878f,
+0.1890f
+};
+static const float hlorentz63[62] = {
+0.0284f, 0.0289f, 0.0294f, 0.0299f, 0.0304f, 0.0309f, 0.0315f, 0.0320f,
+0.0326f, 0.0332f, 0.0338f, 0.0345f, 0.0352f, 0.0359f, 0.0366f, 0.0374f,
+0.0382f, 0.0390f, 0.0399f, 0.0408f, 0.0417f, 0.0427f, 0.0437f, 0.0448f,
+0.0459f, 0.0471f, 0.0484f, 0.0497f, 0.0511f, 0.0525f, 0.0541f, 0.0557f,
+0.0575f, 0.0593f, 0.0612f, 0.0633f, 0.0655f, 0.0679f, 0.0704f, 0.0731f,
+0.0760f, 0.0791f, 0.0824f, 0.0859f, 0.0898f, 0.0939f, 0.0984f, 0.1032f,
+0.1083f, 0.1139f, 0.1198f, 0.1261f, 0.1328f, 0.1398f, 0.1469f, 0.1541f,
+0.1610f, 0.1675f, 0.1731f, 0.1774f, 0.1802f, 0.1811f
+};
+static const float hlorentz64[65] = {
+0.0283f, 0.0287f, 0.0291f, 0.0296f, 0.0301f, 0.0306f, 0.0311f, 0.0316f,
+0.0322f, 0.0327f, 0.0333f, 0.0339f, 0.0345f, 0.0352f, 0.0359f, 0.0366f,
+0.0373f, 0.0381f, 0.0388f, 0.0397f, 0.0405f, 0.0414f, 0.0423f, 0.0433f,
+0.0443f, 0.0454f, 0.0465f, 0.0476f, 0.0489f, 0.0501f, 0.0515f, 0.0529f,
+0.0544f, 0.0560f, 0.0576f, 0.0594f, 0.0613f, 0.0632f, 0.0653f, 0.0676f,
+0.0699f, 0.0724f, 0.0751f, 0.0780f, 0.0811f, 0.0843f, 0.0879f, 0.0916f,
+0.0956f, 0.0999f, 0.1046f, 0.1095f, 0.1147f, 0.1202f, 0.1261f, 0.1321f,
+0.1384f, 0.1447f, 0.1510f, 0.1569f, 0.1624f, 0.1670f, 0.1706f, 0.1728f,
+0.1736f
+};
+static const float *hptr_tab_lorentz[64] = {
+hlorentz1, hlorentz2, hlorentz3, hlorentz4,
+hlorentz5, hlorentz6, hlorentz7, hlorentz8,
+hlorentz9, hlorentz10, hlorentz11, hlorentz12,
+hlorentz13, hlorentz14, hlorentz15, hlorentz16,
+hlorentz17, hlorentz18, hlorentz19, hlorentz20,
+hlorentz21, hlorentz22, hlorentz23, hlorentz24,
+hlorentz25, hlorentz26, hlorentz27, hlorentz28,
+hlorentz29, hlorentz30, hlorentz31, hlorentz32,
+hlorentz33, hlorentz34, hlorentz35, hlorentz36,
+hlorentz37, hlorentz38, hlorentz39, hlorentz40,
+hlorentz41, hlorentz42, hlorentz43, hlorentz44,
+hlorentz45, hlorentz46, hlorentz47, hlorentz48,
+hlorentz49, hlorentz50, hlorentz51, hlorentz52,
+hlorentz53, hlorentz54, hlorentz55, hlorentz56,
+hlorentz57, hlorentz58, hlorentz59, hlorentz60,
+hlorentz61, hlorentz62, hlorentz63, hlorentz64
+};
diff --git a/lib/qra/qra64/main.c b/lib/qra/qra64/main.c
index e5608db25..96ff24778 100644
--- a/lib/qra/qra64/main.c
+++ b/lib/qra/qra64/main.c
@@ -1,6 +1,6 @@
/*
main.c
-QRA64 mode encode/decode test
+QRA64 mode encode/decode tests
(c) 2016 - Nico Palermo, IV3NWV
@@ -145,21 +145,22 @@ symbol.
normrnd_s(rp,NSAMPLES,0,sigma);
normrnd_s(rq,NSAMPLES,0,sigma);
- if (channel_type == CHANNEL_AWGN)
- for (k=0;k-15)
+ if (channel_type == CHANNEL_AWGN)
+ for (k=0;k=0) {
+ ebnodbavg +=ebnodbest;
+ if (memcmp(xdec,x,12*sizeof(int))==0)
+ ndecok[rc]++;
+ else
+ nundet++;
+ }
+ }
+ printf("\n\n");
+
+ printf("Transimtted msgs:%d\nDecoded msgs:\n\n",ntx);
+ for (k=0;k<12;k++) {
+ printf("%3d with %s\n",ndecok[k],decode_type[k]);
+ ndec += ndecok[k];
+ }
+ printf("\nTotal: %d/%d (%d undetected errors)\n\n",ndec,ntx,nundet);
+ printf("");
+
+ ebnodbavg/=(ndec+nundet);
+ printf("Estimated SNR (average in dB) = %.2f dB\n\n",ebnodbavg-QRA64_SNR_EBNO_OFFSET);
+
+ return 0;
+}
+
+
+
void syntax(void)
{
printf("\nQRA64 Mode Tests\n");
@@ -421,6 +508,11 @@ void syntax(void)
printf(" -a : set decode type 0=NOAP 1=AUTOAP (default) 2=USERAP\n");
printf(" -t: 0=simulate seq of msgs between IV3NWV and K1JT (default)\n");
printf(" 1=simulate K1JT receiving K1JT IV3NWV JN66\n");
+ printf(" 2=simulate fast-fading routines (option -c ignored)\n");
+ printf("Options used only for fast-fading simulations:\n");
+ printf(" -b : 90%% fading bandwidth in Hz [1..230 Hz] (default = 2.5 Hz)\n");
+ printf(" -m : fading model. 0=Gauss, 1=Lorentz (default = Lorentz)\n");
+ printf(" -q : qra64 submode. 0=QRA64A,... 4=QRA64E (default = QRA64A)\n");
printf(" -h: this help\n");
}
@@ -433,55 +525,88 @@ int main(int argc, char* argv[])
unsigned int testtype=0;
int nqso = 100;
float EbNodB;
+ float B90 = 2.5;
+ int fadingModel = 1;
+ int submode = 0;
// Parse the command line
while(--argc) {
argv++;
+
if (strncmp(*argv,"-h",2)==0) {
syntax();
return 0;
- } else {
- if (strncmp(*argv,"-a",2)==0) {
- mode = ( int)atoi((*argv)+2);
- if (mode>2) {
- printf("Invalid decoding mode\n");
- syntax();
- return -1;
- }
- } else {
+ }
+ else
+ if (strncmp(*argv,"-a",2)==0) {
+ mode = ( int)atoi((*argv)+2);
+ if (mode>2) {
+ printf("Invalid decoding mode\n");
+ syntax();
+ return -1;
+ }
+ }
+ else
if (strncmp(*argv,"-s",2)==0) {
- SNRdB = (float)atof((*argv)+2);
- if (SNRdB>20 || SNRdB<-40) {
- printf("SNR should be in the range [-40..20]\n");
+ SNRdB = (float)atof((*argv)+2);
+ if (SNRdB>20 || SNRdB<-50) {
+ printf("SNR should be in the range [-50..20]\n");
+ syntax();
+ return -1;
+ }
+ }
+ else
+ if (strncmp(*argv,"-t",2)==0) {
+ testtype = ( int)atoi((*argv)+2);
+ if (testtype>2) {
+ printf("Invalid test type\n");
+ syntax();
+ return -1;
+ }
+ }
+ else
+ if (strncmp(*argv,"-c",2)==0) {
+ channel = ( int)atoi((*argv)+2);
+ if (channel>CHANNEL_RAYLEIGH) {
+ printf("Invalid channel type\n");
+ syntax();
+ return -1;
+ }
+ }
+ else
+ if (strncmp(*argv,"-b",2)==0) {
+ B90 = (float)atof((*argv)+2);
+ if (B90<1 || B90>230) {
+ printf("Invalid B90\n");
+ syntax();
+ return -1;
+ }
+ }
+ else
+ if (strncmp(*argv,"-m",2)==0) {
+ fadingModel = (int)atoi((*argv)+2);
+ if (fadingModel<0 || fadingModel>1) {
+ printf("Invalid fading model\n");
+ syntax();
+ return -1;
+ }
+ }
+ else
+ if (strncmp(*argv,"-q",2)==0) {
+ submode = (int)atoi((*argv)+2);
+ if (submode<0 || submode>4) {
+ printf("Invalid submode\n");
+ syntax();
+ return -1;
+ }
+ }
+ else {
+ printf("Invalid option\n");
syntax();
return -1;
- }
- } else {
- if (strncmp(*argv,"-t",2)==0) {
- testtype = ( int)atoi((*argv)+2);
- if (testtype>1) {
- printf("Invalid test type\n");
- syntax();
- return -1;
}
- } else {
- if (strncmp(*argv,"-c",2)==0) {
- channel = ( int)atoi((*argv)+2);
- if (channel>CHANNEL_RAYLEIGH) {
- printf("Invalid channel type\n");
- syntax();
- return -1;
- }
- } else {
- printf("Invalid option\n");
- syntax();
- return -1;
- }
- }
}
- }
- }
- }
+
EbNodB = SNRdB+QRA64_SNR_EBNO_OFFSET;
@@ -490,21 +615,33 @@ int main(int argc, char* argv[])
#endif
if (testtype==0) {
- for (k=0;k
-#include
#include
#include "qra64.h"
#include "../qracodes/qracodes.h"
#include "../qracodes/qra13_64_64_irr_e.h"
#include "../qracodes/pdmath.h"
+#include "../qracodes/normrnd.h"
// Code parameters of the QRA64 mode
#define QRA64_CODE qra_13_64_64_irr_e
@@ -54,8 +54,28 @@ resulting code is a (12,63) code
static int calc_crc6(const int *x, int sz);
static void ix_mask(float *dst, const float *src, const int *mask,
const int *x);
-static int qra64_do_decode(int *x, const float *pix, const int *ap_mask,
+static int qra64_decode_attempts(qra64codec *pcodec, int *xdec, const float *ix);
+static int qra64_do_decode(int *x, const float *pix, const int *ap_mask,
const int *ap_x);
+static float qra64_fastfading_estim_noise_std(
+ float *rxen,
+ const float esnometric,
+ const int submode);
+static void qra64_fastfading_intrinsics(
+ float *pix,
+ const float *rxamp,
+ const float *hptr,
+ const int hlen,
+ const float cmetric,
+ const int submode);
+static float qra64_fastfading_msg_esno(
+ const int *ydec,
+ const float *rxamp,
+ const float sigma,
+ const float EsNoMetric,
+ const int hlen,
+ const int submode);
+
// a-priori information masks for fields in JT65-like msgs --------------------
#define MASK_CQQRZ 0xFFFFFFC // CQ/QRZ calls common bits
@@ -259,101 +279,13 @@ int qra64_decode(qra64codec *pcodec, float *ebno, int *x, const float *rxen)
// Initialize crc prob to a uniform distribution
pd_init(dsttmp,pd_uniform(QRA64_m),QRA64_M);
- // Attempt to decode without a-priori info --------------------------------
- rc = qra64_do_decode(xdec, ix, NULL, NULL);
- if (rc>=0) {
- rc = 0; // successfull decode with AP0
- goto decode_end;
- }
- else
- if (pcodec->apflags==QRA_NOAP)
- // nothing more to do
- return rc; // rc<0 = unsuccessful decode
+ // Try to decode using all AP cases if required
+ rc = qra64_decode_attempts(pcodec, xdec, ix);
- // Here we handle decoding with AP knowledge
+ if (rc<0)
+ return rc; // no success
- // Attempt to decode CQ calls
- rc = qra64_do_decode(xdec,ix,pcodec->apmask_cqqrz, pcodec->apmsg_cqqrz);
- if (rc>=0) { rc = 1; goto decode_end; }; // decoded [cq/qrz ? ?]
- rc = qra64_do_decode(xdec, ix, pcodec->apmask_cqqrz_ooo,
- pcodec->apmsg_cqqrz);
- if (rc>=0) { rc = 2; goto decode_end; }; // decoded [cq ? ooo]
-
- // attempt to decode calls directed to us
- if (pcodec->apmsg_set[APTYPE_MYCALL]) {
- rc = qra64_do_decode(xdec, ix, pcodec->apmask_call1,
- pcodec->apmsg_call1);
- if (rc>=0) { rc = 3; goto decode_end; }; // decoded [mycall ? ?]
- rc = qra64_do_decode(xdec, ix, pcodec->apmask_call1_ooo,
- pcodec->apmsg_call1);
- if (rc>=0) { rc = 4; goto decode_end; }; // decoded [mycall ? ooo]
- }
-
- // attempt to decode [mycall srccall ?] msgs
- if (pcodec->apmsg_set[APTYPE_BOTHCALLS]) {
- rc = qra64_do_decode(xdec, ix, pcodec->apmask_call1_call2,
- pcodec->apmsg_call1_call2);
- if (rc>=0) { rc = 5; goto decode_end; }; // decoded [mycall srccall ?]
- }
-
- // attempt to decode [? hiscall ?/b] msgs
- if (pcodec->apmsg_set[APTYPE_HISCALL]) {
- rc = qra64_do_decode(xdec, ix, pcodec->apmask_call2,
- pcodec->apmsg_call2);
- if (rc>=0) { rc = 6; goto decode_end; }; // decoded [? hiscall ?]
- rc = qra64_do_decode(xdec, ix, pcodec->apmask_call2_ooo,
- pcodec->apmsg_call2);
- if (rc>=0) { rc = 7; goto decode_end; }; // decoded [? hiscall ooo]
- }
-
- // attempt to decode [cq/qrz hiscall ?/b/grid] msgs
- if (pcodec->apmsg_set[APTYPE_CQHISCALL]) {
-
- rc = qra64_do_decode(xdec, ix, pcodec->apmask_cq_call2,
- pcodec->apmsg_cq_call2);
- if (rc>=0) { rc = 9; goto decode_end; }; // decoded [cq/qrz hiscall ?]
-
- rc = qra64_do_decode(xdec, ix, pcodec->apmask_cq_call2_ooo,
- pcodec->apmsg_cq_call2_grid);
- if (rc>=0) {
- // Full AP mask need special handling
- // To minimize false decodes we check the decoded message
- // with what passed in the ap_set call
- if (memcmp(pcodec->apmsg_cq_call2_grid,xdec, QRA64_K*sizeof(int))!=0)
- return -1;
- rc = 11; goto decode_end;
- }; // decoded [cq/qrz hiscall grid]
-
- rc = qra64_do_decode(xdec, ix, pcodec->apmask_cq_call2_ooo,
- pcodec->apmsg_cq_call2);
- if (rc>=0) {
- // Full AP mask need special handling
- // To minimize false decodes we check the decoded message
- // with what passed in the ap_set call
- if (memcmp(pcodec->apmsg_cq_call2,xdec, QRA64_K*sizeof(int))!=0)
- return -1;
- rc = 10; goto decode_end; }; // decoded [cq/qrz hiscall ]
- }
-
- // attempt to decode [mycall hiscall grid]
- if (pcodec->apmsg_set[APTYPE_FULL]) {
- rc = qra64_do_decode(xdec, ix, pcodec->apmask_call1_call2_grid,
- pcodec->apmsg_call1_call2_grid);
- if (rc>=0) {
- // Full AP mask need special handling
- // All the three msg fields were given.
- // To minimize false decodes we check the decoded message
- // with what passed in the ap_set call
- if (memcmp(pcodec->apmsg_call1_call2_grid,xdec, QRA64_K*sizeof(int))!=0)
- return -1;
- rc = 8; goto decode_end;
- }; // decoded [mycall hiscall grid]
- }
-
- // all decoding attempts failed
- return rc;
-
-decode_end: // successfull decode
+ // successfull decode --------------------------------
// copy decoded message (without crc) to output buffer
memcpy(x,xdec,QRA64_K*sizeof(int));
@@ -404,8 +336,585 @@ decode_end: // successfull decode
return rc;
}
+// Tables of fading amplitudes coefficients for QRA64 (Ts=6912/12000)
+// As the fading is assumed to be symmetric around the nominal frequency
+// only the leftmost and the central coefficient are stored in the tables.
+// (files have been generated with the Matlab code efgengauss.m and efgenlorentz.m)
+#include "fadampgauss.c"
+#include "fadamplorentz.c"
+
+int qra64_decode_fastfading(
+ qra64codec *pcodec, // ptr to the codec structure
+ float *ebno, // ptr to where the estimated Eb/No value will be saved
+ int *x, // ptr to decoded message
+ float *rxen, // ptr to received symbol energies array
+ const int submode, // submode idx (0=QRA64A ... 4=QRA64E)
+ const float B90, // spread bandwidth (90% fractional energy)
+ const int fadingModel) // 0=Gaussian 1=Lorentzian fade model
+
+// Decode a QRA64 msg using a fast-fading metric
+//
+// rxen: The array of the received bin energies
+// Bins must be spaced by integer multiples of the symbol rate (1/Ts Hz)
+// The array must be an array of total length U = L x N where:
+// L: is the number of frequency bins per message symbol (see after)
+// N: is the number of symbols in a QRA64 msg (63)
+
+// The number of bins/symbol L depends on the selected submode accordingly to
+// the following rule:
+// L = (64+64*2^submode+64) = 64*(2+2^submode)
+// Tone 0 is always supposed to be at offset 64 in the array.
+// The m-th tone nominal frequency is located at offset 64 + m*2^submode (m=0..63)
+//
+// Submode A: (2^submode = 1)
+// L = 64*3 = 196 bins/symbol
+// Total length of the energies array: U = 192*63 = 12096 floats
+//
+// Submode B: (2^submode = 2)
+// L = 64*4 = 256 bins/symbol
+// Total length of the energies array: U = 256*63 = 16128 floats
+//
+// Submode C: (2^submode = 4)
+// L = 64*6 = 384 bins/symbol
+// Total length of the energies array: U = 384*63 = 24192 floats
+//
+// Submode D: (2^submode = 8)
+// L = 64*10 = 640 bins/symbol
+// Total length of the energies array: U = 640*63 = 40320 floats
+//
+// Submode E: (2^submode = 16)
+// L = 64*18 = 1152 bins/symbol
+// Total length of the energies array: U = 1152*63 = 72576 floats
+//
+// Note: The rxen array is modified and reused for internal calculations.
+//
+//
+// B90: spread fading bandwidth in Hz (90% fractional average energy)
+//
+// B90 should be in the range 1 Hz ... 238 Hz
+// The value passed to the call is rounded to the closest value among the
+// 64 available values:
+// B = 1.09^k Hz, with k=0,1,...,63
+//
+// I.e. B90=27 Hz will be approximated in this way:
+// k = rnd(log(27)/log(1.09)) = 38
+// B90 = 1.09^k = 1.09^38 = 26.4 Hz
+//
+// For any input value the maximum rounding error is not larger than +/- 5%
+//
+
+{
+
+ int k;
+ float *srctmp, *dsttmp;
+ float ix[QRA64_NC*QRA64_M]; // (depunctured) intrisic information
+ int xdec[QRA64_KC]; // decoded message (with crc)
+ int ydec[QRA64_NC]; // re-encoded message (for snr calculations)
+ float noisestd; // estimated noise std
+ float esno,ebnoval; // estimated Eb/No
+ float tempf;
+ float EsNoMetric, cmetric;
+ int rc;
+ int hidx, hlen;
+ const float *hptr;
+
+ if (QRA64_NMSG!=QRA64_CODE.NMSG)
+ return -16; // QRA64_NMSG define is wrong
+
+ if (submode<0 || submode>4)
+ return -17; // invalid submode
+
+ if (B90<1.0f || B90>238.0f)
+ return -18; // B90 out of range
+
+ // compute index to most appropriate amplitude weighting function coefficients
+ hidx = (int)(log((float)B90)/log(1.09f) - 0.499f);
+
+ if (hidx<0 || hidx > 64)
+ return -19; // index of weighting function out of range
+
+ if (fadingModel==0) { // gaussian fading model
+ // point to gaussian weighting taps
+ hlen = hlen_tab_gauss[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun)
+ hptr = hptr_tab_gauss[hidx]; // pointer to the first (L+1)/2 coefficients of w fun
+ }
+ else if (fadingModel==1) {
+ // point to lorentzian weighting taps
+ hlen = hlen_tab_lorentz[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun)
+ hptr = hptr_tab_lorentz[hidx]; // pointer to the first (L+1)/2 coefficients of w fun
+ }
+ else
+ return -20; // invalid fading model index
+
+
+ // compute (euristically) the optimal decoder metric accordingly the given spread amount
+ // We assume that the decoder threshold is:
+ // Es/No(dB) = Es/No(AWGN)(dB) + 8*log(B90)/log(240)(dB)
+ // that's to say, at the maximum Doppler spread bandwidth (240 Hz) there's a ~8 dB Es/No degradation
+ // over the AWGN case
+ tempf = 8.0f*(float)log((float)B90)/(float)log(240.0f);
+ EsNoMetric = pcodec->decEsNoMetric*(float)pow(10.0f,tempf/10.0f);
+
+ // Step 1 -----------------------------------------------------------------------------------
+ // Evaluate the noise stdev from the received energies at nominal tone frequencies
+ // and transform energies to amplitudes
+ tempf = hptr[hlen-1]; // amplitude weigth at nominal freq;
+ tempf = tempf*tempf; // fractional energy at nominal freq. bin
+
+ noisestd = qra64_fastfading_estim_noise_std(rxen, EsNoMetric, submode);
+ cmetric = (float)sqrt(M_PI_2*EsNoMetric)/noisestd;
+
+ // Step 2 -----------------------------------------------------------------------------------
+ // Compute message symbols probability distributions
+ qra64_fastfading_intrinsics(ix, rxen, hptr, hlen, cmetric, submode);
+
+ // Step 3 ---------------------------------------------------------------------------
+ // De-puncture observations adding a uniform distribution for the crc symbol
+ // Move check symbols distributions one symbol towards the end
+ dsttmp = PD_ROWADDR(ix,QRA64_M, QRA64_NC-1); //Point to last symbol prob dist
+ srctmp = dsttmp-QRA64_M; // source is the previous pd
+ for (k=0;k238.0f)
+ return -18; // B90 out of range
+
+ // compute index to most appropriate amplitude weighting function coefficients
+ hidx = (int)(log((float)B90)/log(1.09f) - 0.499f);
+
+ if (hidx<0 || hidx > 64)
+ return -19; // index of weighting function out of range
+
+ if (fadingModel==0) { // gaussian fading model
+ // point to gaussian weighting taps
+ hlen = hlen_tab_gauss[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun)
+ hptr = hptr_tab_gauss[hidx]; // pointer to the first (L+1)/2 coefficients of w fun
+ }
+ else if (fadingModel==1) {
+ // point to lorentzian weighting taps
+ hlen = hlen_tab_lorentz[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun)
+ hptr = hptr_tab_lorentz[hidx]; // pointer to the first (L+1)/2 coefficients of w fun
+ }
+ else
+ return -20; // invalid fading model index
+
+
+ // Compute the unfaded tone amplitudes from the Eb/No value passed to the call
+ N0 = 1.0f; // assume unitary noise PSD
+ sigmanoise = (float)sqrt(N0/2);
+ EsN0 = (float)pow(10.0f,EbN0dB/10.0f)*QRA64_m*QRA64_K/QRA64_N; // Es/No = m*R*Eb/No
+ Es = EsN0*N0;
+ A = (float)sqrt(Es/2.0f); // unfaded tone amplitude (i^2+q^2 = Es/2+Es/2 = Es)
+
+
+ // Generate gaussian noise iq components
+ normrnd_s(channel_out, bpm*2, 0 , sigmanoise);
+
+ // Add message symbols energies
+ for (n=0;n=0;j--) {
+ sigmasig = A*hptr[j];
+ normrnd_s(iq, 2, 0 , sigmasig);
+// iq[0]=sigmasig*sqrt(2); iq[1]=0; debug: used to verify Eb/No
+ *curi++ += iq[0];
+ *curq++ += iq[1];
+// tote +=iq[0]*iq[0]+iq[1]*iq[1]; // debug
+ }
+
+ }
+
+// tote = tote/QRA64_N; // debug
+
+ // compute total bin energies (S+N) and store in first half of buffer
+ curi = channel_out;
+ curq = channel_out+bpm;
+ for (n=0;n=0;j--) {
+ u = *curbin++ * hptr[j]*cmetric;
+ u = u*u/(u+(float)M_E); // log(I0(u)) approx.
+ loglh = loglh + u;
+ }
+ if (loglh>maxloglh) // keep track of the max loglikelihood
+ maxloglh = loglh;
+ curix[k]=loglh;
+ }
+ // scale to likelihoods
+ sumix = 0.f;
+ for (k=0;k=0;j--) {
+ u = *curbin++;
+ msgsn += u*u;
+ }
+
+ }
+
+ msgsn = msgsn/(QRA64_N*tothlen); // avg msg energy per bin (noise included)
+
+ // as sigma is overestimated (sigmatrue = sigma*sqrt((1+EsNoMetric/bps)/(1+EsNo/bps))
+ // we have: msgsn = (1+x/hlen)/(1+x/bps)*2*sigma^2*(1+EsnoMetric/bps), where x = Es/N0(true)
+ //
+ // we can then write:
+ // u = msgsn/2.0f/(sigma*sigma)/(1.0f+EsNoMetric/bps);
+ // (1+x/hlen)/(1+x/bps) = u
+
+ u = msgsn/(2.0f*sigma*sigma)/(1.0f+EsNoMetric/bps);
+
+ // check u>1
+ if (u<1)
+ return 0.f;
+
+ // check u(bps/tothlen))
+ return 10000.f;
+
+ // solve for Es/No
+ esno = (u-1.0f)/(1.0f/tothlen-u/bps);
+
+ return esno;
+
+
+}
+
+
+// Attempt to decode given intrisic information
+static int qra64_decode_attempts(qra64codec *pcodec, int *xdec, const float *ix)
+{
+ int rc;
+
+ // Attempt to decode without a-priori info --------------------------------
+ rc = qra64_do_decode(xdec, ix, NULL, NULL);
+ if (rc>=0)
+ return 0; // successfull decode with AP0
+ else
+ if (pcodec->apflags==QRA_NOAP)
+ // nothing more to do
+ return rc; // rc<0 = unsuccessful decode
+
+ // Here we handle decoding with AP knowledge
+
+ // Attempt to decode CQ calls
+ rc = qra64_do_decode(xdec,ix,pcodec->apmask_cqqrz, pcodec->apmsg_cqqrz);
+ if (rc>=0) return 1; // decoded [cq/qrz ? ?]
+
+ rc = qra64_do_decode(xdec, ix, pcodec->apmask_cqqrz_ooo,
+ pcodec->apmsg_cqqrz);
+ if (rc>=0) return 2; // decoded [cq ? ooo]
+
+ // attempt to decode calls directed to us
+ if (pcodec->apmsg_set[APTYPE_MYCALL]) {
+ rc = qra64_do_decode(xdec, ix, pcodec->apmask_call1,
+ pcodec->apmsg_call1);
+ if (rc>=0) return 3; // decoded [mycall ? ?]
+ rc = qra64_do_decode(xdec, ix, pcodec->apmask_call1_ooo,
+ pcodec->apmsg_call1);
+ if (rc>=0) return 4; // decoded [mycall ? ooo]
+ }
+
+ // attempt to decode [mycall srccall ?] msgs
+ if (pcodec->apmsg_set[APTYPE_BOTHCALLS]) {
+ rc = qra64_do_decode(xdec, ix, pcodec->apmask_call1_call2,
+ pcodec->apmsg_call1_call2);
+ if (rc>=0) return 5; // decoded [mycall srccall ?]
+ }
+
+ // attempt to decode [? hiscall ?/b] msgs
+ if (pcodec->apmsg_set[APTYPE_HISCALL]) {
+ rc = qra64_do_decode(xdec, ix, pcodec->apmask_call2,
+ pcodec->apmsg_call2);
+ if (rc>=0) return 6; // decoded [? hiscall ?]
+ rc = qra64_do_decode(xdec, ix, pcodec->apmask_call2_ooo,
+ pcodec->apmsg_call2);
+ if (rc>=0) return 7; // decoded [? hiscall ooo]
+ }
+
+ // attempt to decode [cq/qrz hiscall ?/b/grid] msgs
+ if (pcodec->apmsg_set[APTYPE_CQHISCALL]) {
+
+ rc = qra64_do_decode(xdec, ix, pcodec->apmask_cq_call2,
+ pcodec->apmsg_cq_call2);
+ if (rc>=0) return 9; // decoded [cq/qrz hiscall ?]
+
+ rc = qra64_do_decode(xdec, ix, pcodec->apmask_cq_call2_ooo,
+ pcodec->apmsg_cq_call2_grid);
+ if (rc>=0) {
+ // Full AP mask need special handling
+ // To minimize false decodes we check the decoded message
+ // with what passed in the ap_set call
+ if (memcmp(pcodec->apmsg_cq_call2_grid,xdec, QRA64_K*sizeof(int))!=0)
+ return -1;
+ else
+ return 11; // decoded [cq/qrz hiscall grid]
+ };
+
+ rc = qra64_do_decode(xdec, ix, pcodec->apmask_cq_call2_ooo,
+ pcodec->apmsg_cq_call2);
+ if (rc>=0) {
+ // Full AP mask need special handling
+ // To minimize false decodes we check the decoded message
+ // with what passed in the ap_set call
+ if (memcmp(pcodec->apmsg_cq_call2,xdec, QRA64_K*sizeof(int))!=0)
+ return -1;
+ else
+ return 10; // decoded [cq/qrz hiscall ]
+ }
+ }
+
+ // attempt to decode [mycall hiscall grid]
+ if (pcodec->apmsg_set[APTYPE_FULL]) {
+ rc = qra64_do_decode(xdec, ix, pcodec->apmask_call1_call2_grid,
+ pcodec->apmsg_call1_call2_grid);
+ if (rc>=0) {
+ // Full AP mask need special handling
+ // All the three msg fields were given.
+ // To minimize false decodes we check the decoded message
+ // with what passed in the ap_set call
+ if (memcmp(pcodec->apmsg_call1_call2_grid,xdec, QRA64_K*sizeof(int))!=0)
+ return -1;
+ else
+ return 8; // decoded [mycall hiscall grid]
+ }
+ }
+
+ // all decoding attempts failed
+ return rc;
+}
+
+
+
// Decode with given a-priori information
static int qra64_do_decode(int *xdec, const float *pix, const int *ap_mask,
const int *ap_x)
@@ -441,6 +950,8 @@ static int qra64_do_decode(int *xdec, const float *pix, const int *ap_mask,
return 0;
}
+
+
// crc functions --------------------------------------------------------------
// crc-6 generator polynomial
// g(x) = x^6 + a5*x^5 + ... + a1*x + a0
diff --git a/lib/qra/qra64/qra64.h b/lib/qra/qra64/qra64.h
index e4b431872..e3c60e43a 100644
--- a/lib/qra/qra64/qra64.h
+++ b/lib/qra/qra64/qra64.h
@@ -140,6 +140,83 @@ int qra64_decode(qra64codec *pcodec, float *ebno, int *x, const float *r);
// return codes in the range 1-10 indicate the amount and the type of a-priori
// information was required to decode the received message.
+
+// Decode a QRA64 msg using a fast-fading metric
+int qra64_decode_fastfading(
+ qra64codec *pcodec, // ptr to the codec structure
+ float *ebno, // ptr to where the estimated Eb/No value will be saved
+ int *x, // ptr to decoded message
+ float *rxen, // ptr to received symbol energies array
+ const int submode, // submode idx (0=QRA64A ... 4=QRA64E)
+ const float B90, // spread bandwidth (90% fractional energy)
+ const int fadingModel); // 0=Gaussian 1=Lorentzian fade model
+//
+// rxen: The array of the received bin energies
+// Bins must be spaced by integer multiples of the symbol rate (1/Ts Hz)
+// The array must be an array of total length U = L x N where:
+// L: is the number of frequency bins per message symbol (see after)
+// N: is the number of symbols in a QRA64 msg (63)
+//
+// The number of bins/symbol L depends on the selected submode accordingly to
+// the following rule:
+// L = (64+64*2^submode+64) = 64*(2+2^submode)
+// Tone 0 is always supposed to be at offset 64 in the array.
+// The m-th tone nominal frequency is located at offset 64 + m*2^submode (m=0..63)
+//
+// Submode A: (2^submode = 1)
+// L = 64*3 = 196 bins/symbol
+// Total length of the energies array: U = 192*63 = 12096 floats
+//
+// Submode B: (2^submode = 2)
+// L = 64*4 = 256 bins/symbol
+// Total length of the energies array: U = 256*63 = 16128 floats
+//
+// Submode C: (2^submode = 4)
+// L = 64*6 = 384 bins/symbol
+// Total length of the energies array: U = 384*63 = 24192 floats
+//
+// Submode D: (2^submode = 8)
+// L = 64*10 = 640 bins/symbol
+// Total length of the energies array: U = 640*63 = 40320 floats
+//
+// Submode E: (2^submode = 16)
+// L = 64*18 = 1152 bins/symbol
+// Total length of the energies array: U = 1152*63 = 72576 floats
+//
+// Note: The rxen array is modified and reused for internal calculations.
+//
+//
+// B90: spread fading bandwidth in Hz (90% fractional average energy)
+//
+// B90 should be in the range 1 Hz ... 238 Hz
+// The value passed to the call is rounded to the closest value among the
+// 64 available values:
+// B = 1.09^k Hz, with k=0,1,...,63
+//
+// I.e. B90=27 Hz will be approximated in this way:
+// k = rnd(log(27)/log(1.09)) = 38
+// B90 = 1.09^k = 1.09^38 = 26.4 Hz
+//
+// For any input value the maximum rounding error is not larger than +/- 5%
+//
+// return codes: same return codes of qra64_decode (+some additional error codes)
+
+
+// Simulate the fast-fading channel (to be used with qra64_decode_fastfading)
+int qra64_fastfading_channel(
+ float **rxen,
+ const int *xmsg,
+ const int submode,
+ const float EbN0dB,
+ const float B90,
+ const int fadingModel);
+// Simulate transmission over a fading channel with given B90, fading model and submode
+// and non coherent detection.
+// Sets rxen to point to an array of bin energies formatted as required
+// by the (fast-fading) decoding routine.
+// returns 0 on success or negative values on error conditions
+
+
int qra64_apset(qra64codec *pcodec, const int mycall, const int hiscall, const int grid, const int aptype);
// Set decoder a-priori knowledge accordingly to the type of the message to
// look up for
diff --git a/lib/qra/qra64/qra64example.txt b/lib/qra/qra64/qra64example.txt
new file mode 100644
index 000000000..b815a134a
--- /dev/null
+++ b/lib/qra/qra64/qra64example.txt
@@ -0,0 +1,50 @@
+Example:
+
+qra64 -a2 -t2 -b10.0 -m1 -q2 -s-28.0
+
+Simulate fast-fading decoding (-t2) with
+USER_AP (-a2)
+B90 = 10.0 Hz (-b10.0)
+Lorentz model (-m1)
+submode QRA64C (-q2)
+Input SNR = -28.0 dB (-s-28.0)
+
+(type qra64 -h for command syntax)
+
+Command Output:
+
+Simulating the QRA64 decoder with fast-fading metric
+B90=10.00 Hz - Fading Model=Lorentz - Submode=QRA64C
+
+K1JT decoder enabled for [K1JT ? ?/blank]
+K1JT decoder enabled for [K1JT IV3NWV ?]
+K1JT decoder enabled for [? IV3NWV ?/blank]
+K1JT decoder enabled for [K1JT IV3NWV JN66]
+K1JT decoder enabled for [CQ IV3NWV ?/b/JN66]
+
+Encoding msg: [K1JT IV3NWV JN66]
+Decoding with K1JT's decoder
+....................................................................................................
+
+Transimtted msgs:100
+Decoded msgs:
+
+ 0 with [? ? ?] AP0
+ 0 with [CQ ? ?] AP27
+ 0 with [CQ ? ] AP42
+ 4 with [CALL ? ?] AP29
+ 0 with [CALL ? ] AP44
+ 0 with [CALL CALL ?] AP57
+ 2 with [? CALL ?] AP29
+ 0 with [? CALL ] AP44
+ 91 with [CALL CALL G] AP72
+ 0 with [CQ CALL ?] AP55
+ 0 with [CQ CALL ] AP70
+ 0 with [CQ CALL G] AP70
+
+Total: 97/100 (0 undetected errors)
+
+Estimated SNR (average in dB) = -28.39 dB
+
+Input SNR = -28.0dB ap-mode=USER AP
+