2023-01-08 13:03:29 -05:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
2023-11-18 23:24:06 -05:00
|
|
|
// Copyright (C) 2023 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
2023-01-08 13:03:29 -05:00
|
|
|
// //
|
|
|
|
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
|
|
|
// reformatted and adapted to Qt and SDRangel context //
|
|
|
|
// //
|
|
|
|
// This program 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 as version 3 of the License, or //
|
|
|
|
// (at your option) any later version. //
|
|
|
|
// //
|
|
|
|
// This program 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 V3 for more details. //
|
|
|
|
// //
|
|
|
|
// You should have received a copy of the GNU General Public License //
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <string>
|
2023-01-10 22:49:16 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
#include "unpack.h"
|
2023-01-12 14:54:42 -05:00
|
|
|
#include "unpack0.h"
|
2023-01-08 13:03:29 -05:00
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
namespace FT8 {
|
|
|
|
|
2023-01-19 17:51:43 -05:00
|
|
|
int Packing::ihashcall(std::string rawcall, int m)
|
2023-01-08 13:03:29 -05:00
|
|
|
{
|
2023-01-19 17:51:43 -05:00
|
|
|
std::string call = trim(rawcall);
|
2023-01-08 13:03:29 -05:00
|
|
|
const char *chars = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/";
|
|
|
|
|
2023-01-19 17:51:43 -05:00
|
|
|
while (call.size() < 11) {
|
2023-01-08 13:03:29 -05:00
|
|
|
call += " ";
|
2023-01-19 17:51:43 -05:00
|
|
|
}
|
2023-01-08 13:03:29 -05:00
|
|
|
|
|
|
|
unsigned long long x = 0;
|
2023-01-19 17:51:43 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
for (int i = 0; i < 11; i++)
|
|
|
|
{
|
|
|
|
int c = call[i];
|
|
|
|
const char *p = strchr(chars, c);
|
2023-01-23 22:05:35 -05:00
|
|
|
|
|
|
|
if (p)
|
|
|
|
{
|
|
|
|
int j = p - chars;
|
|
|
|
x = 38 * x + j;
|
|
|
|
}
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
2023-01-19 17:51:43 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
x = x * 47055833459LL;
|
|
|
|
x = x >> (64 - m);
|
|
|
|
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// turn 28 bits of packed call into the call
|
|
|
|
//
|
2023-01-10 22:49:16 -05:00
|
|
|
std::string Packing::unpackcall(int x)
|
2023-01-08 13:03:29 -05:00
|
|
|
{
|
|
|
|
char tmp[64];
|
|
|
|
|
|
|
|
const char *c1 = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
const char *c2 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
const char *c3 = "0123456789";
|
|
|
|
const char *c4 = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
|
2023-01-19 17:51:43 -05:00
|
|
|
if (x == 0) {
|
2023-01-08 13:03:29 -05:00
|
|
|
return "DE";
|
2023-01-19 17:51:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (x == 1) {
|
2023-01-08 13:03:29 -05:00
|
|
|
return "QRZ";
|
2023-01-19 17:51:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (x == 2) {
|
2023-01-08 13:03:29 -05:00
|
|
|
return "CQ";
|
2023-01-19 17:51:43 -05:00
|
|
|
}
|
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
if (x <= 1002)
|
|
|
|
{
|
|
|
|
sprintf(tmp, "CQ %d", x - 3);
|
2023-01-19 17:51:43 -05:00
|
|
|
return std::string(tmp);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
2023-01-19 17:51:43 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
if (x <= 532443)
|
|
|
|
{
|
|
|
|
x -= 1003;
|
|
|
|
int ci1 = x / (27 * 27 * 27);
|
|
|
|
x %= 27 * 27 * 27;
|
|
|
|
int ci2 = x / (27 * 27);
|
|
|
|
x %= 27 * 27;
|
|
|
|
int ci3 = x / 27;
|
|
|
|
x %= 27;
|
|
|
|
int ci4 = x;
|
|
|
|
sprintf(tmp, "CQ %c%c%c%c", c4[ci1], c4[ci2], c4[ci3], c4[ci4]);
|
2023-01-19 17:51:43 -05:00
|
|
|
return std::string(tmp);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
2023-01-19 17:51:43 -05:00
|
|
|
if (x < NTOKENS) {
|
2023-01-08 13:03:29 -05:00
|
|
|
return "<TOKEN>";
|
|
|
|
}
|
|
|
|
|
|
|
|
x -= NTOKENS;
|
|
|
|
|
|
|
|
if (x < MAX22)
|
|
|
|
{
|
|
|
|
// 22-bit hash...
|
|
|
|
std::string s;
|
|
|
|
hashes_mu.lock();
|
2023-01-19 17:51:43 -05:00
|
|
|
|
|
|
|
if (hashes22.count(x) > 0) {
|
2023-01-08 13:03:29 -05:00
|
|
|
s = hashes22[x];
|
2023-01-19 17:51:43 -05:00
|
|
|
} else {
|
2023-01-08 13:03:29 -05:00
|
|
|
s = "<...22>";
|
|
|
|
}
|
2023-01-19 17:51:43 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
hashes_mu.unlock();
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
x -= MAX22;
|
|
|
|
|
|
|
|
char a[7];
|
|
|
|
|
|
|
|
a[5] = c4[x % 27];
|
|
|
|
x = x / 27;
|
|
|
|
a[4] = c4[x % 27];
|
|
|
|
x = x / 27;
|
|
|
|
a[3] = c4[x % 27];
|
|
|
|
x = x / 27;
|
|
|
|
a[2] = c3[x % 10];
|
|
|
|
x = x / 10;
|
|
|
|
a[1] = c2[x % 36];
|
|
|
|
x = x / 36;
|
|
|
|
a[0] = c1[x];
|
|
|
|
|
|
|
|
a[6] = '\0';
|
|
|
|
|
2023-01-19 17:51:43 -05:00
|
|
|
return std::string(a);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// unpack a 15-bit grid square &c.
|
|
|
|
// 77-bit version, from inspection of packjt77.f90.
|
|
|
|
// ir is the bit after the two 28+1-bit callee/caller.
|
2023-01-28 16:45:07 -05:00
|
|
|
std::string Packing::unpackgrid15(int ng, int ir)
|
2023-01-08 13:03:29 -05:00
|
|
|
{
|
|
|
|
if (ng < NGBASE)
|
|
|
|
{
|
|
|
|
// maidenhead grid system:
|
|
|
|
// latitude from south pole to north pole.
|
|
|
|
// longitude eastward from anti-meridian.
|
|
|
|
// first: 20 degrees longitude.
|
|
|
|
// second: 10 degrees latitude.
|
|
|
|
// third: 2 degrees longitude.
|
|
|
|
// fourth: 1 degree latitude.
|
|
|
|
// so there are 18*18*10*10 possibilities.
|
|
|
|
int x1 = ng / (18 * 10 * 10);
|
|
|
|
ng %= 18 * 10 * 10;
|
|
|
|
int x2 = ng / (10 * 10);
|
|
|
|
ng %= 10 * 10;
|
|
|
|
int x3 = ng / 10;
|
|
|
|
ng %= 10;
|
|
|
|
int x4 = ng;
|
|
|
|
char tmp[5];
|
|
|
|
tmp[0] = 'A' + x1;
|
|
|
|
tmp[1] = 'A' + x2;
|
|
|
|
tmp[2] = '0' + x3;
|
|
|
|
tmp[3] = '0' + x4;
|
|
|
|
tmp[4] = '\0';
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
ng -= NGBASE;
|
|
|
|
|
2023-01-19 17:51:43 -05:00
|
|
|
if (ng == 1) {
|
2023-01-08 13:03:29 -05:00
|
|
|
return " "; // ???
|
|
|
|
}
|
2023-01-19 17:51:43 -05:00
|
|
|
if (ng == 2) {
|
2023-01-08 13:03:29 -05:00
|
|
|
return "RRR ";
|
|
|
|
}
|
2023-01-19 17:51:43 -05:00
|
|
|
if (ng == 3) {
|
2023-01-08 13:03:29 -05:00
|
|
|
return "RR73";
|
|
|
|
}
|
2023-01-19 17:51:43 -05:00
|
|
|
if (ng == 4) {
|
2023-01-08 13:03:29 -05:00
|
|
|
return "73 ";
|
|
|
|
}
|
|
|
|
|
|
|
|
int db = ng - 35;
|
|
|
|
char tmp[16];
|
2023-01-19 17:51:43 -05:00
|
|
|
|
|
|
|
if (db >= 0) {
|
2023-01-08 13:03:29 -05:00
|
|
|
sprintf(tmp, "%s+%02d", ir ? "R" : "", db);
|
2023-01-19 17:51:43 -05:00
|
|
|
} else {
|
2023-01-08 13:03:29 -05:00
|
|
|
sprintf(tmp, "%s-%02d", ir ? "R" : "", 0 - db);
|
|
|
|
}
|
2023-01-19 17:51:43 -05:00
|
|
|
|
|
|
|
return std::string(tmp);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
2023-01-28 16:45:07 -05:00
|
|
|
std::string Packing::unpackgrid25(int ng)
|
|
|
|
{
|
|
|
|
int x1 = ng / (18 * 10 * 10 * 25 * 25);
|
|
|
|
ng %= (18 * 10 * 10 * 25 * 25);
|
|
|
|
int x2 = ng / (10 * 10 * 25 * 25);
|
|
|
|
ng %= (10 * 10 * 25 * 25);
|
|
|
|
int x3 = ng / (10 * 25 * 25);
|
|
|
|
ng %= (10 * 25 * 25);
|
|
|
|
int x4 = ng / (25 * 25);
|
|
|
|
ng %= (25 * 25);
|
|
|
|
int x5 = ng / (25);
|
|
|
|
ng %= (25);
|
|
|
|
int x6 = ng;
|
|
|
|
char tmp[7];
|
|
|
|
tmp[0] = 'A' + x1;
|
|
|
|
tmp[1] = 'A' + x2;
|
|
|
|
tmp[2] = '0' + x3;
|
|
|
|
tmp[3] = '0' + x4;
|
|
|
|
tmp[4] = 'A' + x5;
|
|
|
|
tmp[5] = 'A' + x6;
|
|
|
|
tmp[6] = '\0';
|
|
|
|
|
|
|
|
return std::string(tmp);
|
|
|
|
}
|
|
|
|
|
2023-01-10 22:49:16 -05:00
|
|
|
void Packing::remember_call(std::string call)
|
2023-01-08 13:03:29 -05:00
|
|
|
{
|
|
|
|
hashes_mu.lock();
|
2023-01-19 17:51:43 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
if (call.size() >= 3 && call[0] != '<')
|
|
|
|
{
|
|
|
|
hashes22[ihashcall(call, 22)] = call;
|
|
|
|
hashes12[ihashcall(call, 12)] = call;
|
2023-01-28 05:39:44 -05:00
|
|
|
hashes10[ihashcall(call, 10)] = call;
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
2023-01-19 17:51:43 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
hashes_mu.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// i3 == 4
|
|
|
|
// a call that doesn't fit in 28 bits.
|
|
|
|
// 12 bits: hash of a previous call
|
|
|
|
// 58 bits: 11 characters
|
|
|
|
// 1 bit: swap
|
|
|
|
// 2 bits: 1 RRR, 2 RR73, 3 73
|
|
|
|
// 1 bit: 1 means CQ
|
2023-01-10 22:49:16 -05:00
|
|
|
std::string Packing::unpack_4(int a77[], std::string& call1str, std::string& call2str, std::string& locstr)
|
2023-01-08 13:03:29 -05:00
|
|
|
{
|
2023-01-08 17:15:31 -05:00
|
|
|
(void) locstr;
|
2023-01-08 13:03:29 -05:00
|
|
|
// 38 possible characters:
|
|
|
|
const char *chars = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/";
|
2023-01-10 22:49:16 -05:00
|
|
|
long long n58 = un64(a77, 12, 58);
|
2023-01-08 13:03:29 -05:00
|
|
|
char call[16];
|
2023-01-08 17:15:31 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
for (int i = 0; i < 11; i++)
|
|
|
|
{
|
|
|
|
call[10 - i] = chars[n58 % 38];
|
|
|
|
n58 = n58 / 38;
|
|
|
|
}
|
2023-01-08 17:15:31 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
call[11] = '\0';
|
2023-01-19 17:51:43 -05:00
|
|
|
std::string callstr(call);
|
2023-01-08 13:03:29 -05:00
|
|
|
|
2023-01-19 17:51:43 -05:00
|
|
|
remember_call(callstr);
|
2023-01-08 13:03:29 -05:00
|
|
|
|
2023-01-19 17:51:43 -05:00
|
|
|
if (un64(a77, 73, 1) == 1)
|
|
|
|
{
|
|
|
|
call1str = std::string("CQ ") + callstr;
|
|
|
|
return call1str;
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
2023-01-10 22:49:16 -05:00
|
|
|
int x12 = un64(a77, 0, 12);
|
2023-01-08 13:03:29 -05:00
|
|
|
// 12-bit hash
|
|
|
|
hashes_mu.lock();
|
|
|
|
std::string ocall;
|
2023-01-08 17:15:31 -05:00
|
|
|
|
|
|
|
if (hashes12.count(x12) > 0) {
|
2023-01-08 13:03:29 -05:00
|
|
|
ocall = hashes12[x12];
|
2023-01-08 17:15:31 -05:00
|
|
|
} else {
|
2023-01-08 13:03:29 -05:00
|
|
|
ocall = "<...12>";
|
|
|
|
}
|
2023-01-08 17:15:31 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
hashes_mu.unlock();
|
2023-01-10 22:49:16 -05:00
|
|
|
int swap = un64(a77, 70, 1);
|
2023-01-08 13:03:29 -05:00
|
|
|
std::string msg;
|
2023-01-08 17:15:31 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
if (swap)
|
|
|
|
{
|
2023-01-29 17:20:58 -05:00
|
|
|
msg = call1str + " " + ocall;
|
|
|
|
call1str = trim(call);
|
|
|
|
call2str = trim(ocall);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
msg = std::string(ocall) + " " + call;
|
2023-01-29 17:20:58 -05:00
|
|
|
call1str = trim(ocall);
|
|
|
|
call2str = trim(call);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
2023-01-10 22:49:16 -05:00
|
|
|
int suffix = un64(a77, 71, 2);
|
2023-01-08 17:15:31 -05:00
|
|
|
|
2023-01-19 17:51:43 -05:00
|
|
|
if (suffix == 1)
|
|
|
|
{
|
|
|
|
locstr = " RRR";
|
2023-01-08 17:15:31 -05:00
|
|
|
} else if (suffix == 2) {
|
2023-01-19 17:51:43 -05:00
|
|
|
locstr = " RR73";
|
2023-01-08 17:15:31 -05:00
|
|
|
} else if (suffix == 3) {
|
2023-01-19 17:51:43 -05:00
|
|
|
locstr = " 73";
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
2023-01-19 17:51:43 -05:00
|
|
|
msg += locstr;
|
2023-01-08 13:03:29 -05:00
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// i3=1
|
|
|
|
//
|
2023-01-10 22:49:16 -05:00
|
|
|
std::string Packing::unpack_1(int a77[], std::string& call1str, std::string& call2str, std::string& locstr)
|
2023-01-08 13:03:29 -05:00
|
|
|
{
|
|
|
|
// type 1:
|
|
|
|
// 28 call1
|
|
|
|
// 1 P/R
|
|
|
|
// 28 call2
|
|
|
|
// 1 P/R
|
|
|
|
// 1 ???
|
|
|
|
// 15 grid
|
|
|
|
// 3 type
|
|
|
|
|
|
|
|
int i = 0;
|
2023-01-10 22:49:16 -05:00
|
|
|
int call1 = un64(a77, i, 28);
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 28;
|
|
|
|
int rover1 = a77[i];
|
|
|
|
i += 1;
|
2023-01-10 22:49:16 -05:00
|
|
|
int call2 = un64(a77, i, 28);
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 28;
|
|
|
|
int rover2 = a77[i];
|
|
|
|
i += 1;
|
|
|
|
int ir = a77[i];
|
|
|
|
i += 1;
|
2023-01-10 22:49:16 -05:00
|
|
|
int grid = un64(a77, i, 15);
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 15;
|
2023-01-10 22:49:16 -05:00
|
|
|
int i3 = un64(a77, i, 3);
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 3;
|
2023-01-23 22:05:35 -05:00
|
|
|
|
|
|
|
if (!((i3 == 1 || i3 == 2) && i == 77)) {
|
|
|
|
return std::string("");
|
|
|
|
}
|
2023-01-08 13:03:29 -05:00
|
|
|
|
2023-01-08 17:15:31 -05:00
|
|
|
call1str = trim(unpackcall(call1));
|
|
|
|
call2str = trim(unpackcall(call2));
|
2023-01-28 16:45:07 -05:00
|
|
|
locstr = unpackgrid15(grid, ir);
|
2023-01-08 13:03:29 -05:00
|
|
|
|
2023-01-08 17:15:31 -05:00
|
|
|
remember_call(call1str);
|
|
|
|
remember_call(call2str);
|
2023-01-08 13:03:29 -05:00
|
|
|
|
2023-01-19 17:51:43 -05:00
|
|
|
const std::string pr = (i3 == 1 ? "/R" : "/P");
|
2023-01-08 13:03:29 -05:00
|
|
|
|
2023-01-08 17:15:31 -05:00
|
|
|
return call1str + (rover1 ? pr : "") + " " + call2str + (rover2 ? pr : "") + " " + locstr;
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
2023-01-28 16:45:07 -05:00
|
|
|
std::string Packing::unpack_5(int a77[], std::string& call1str, std::string& call2str, std::string& locstr)
|
|
|
|
{
|
|
|
|
int x12 = un64(a77, 0, 12);
|
|
|
|
// 12-bit hash
|
|
|
|
hashes_mu.lock();
|
|
|
|
std::string ocall;
|
|
|
|
|
|
|
|
if (hashes12.count(x12) > 0) {
|
|
|
|
ocall = hashes12[x12];
|
|
|
|
} else {
|
|
|
|
ocall = "<...12>";
|
|
|
|
}
|
|
|
|
|
|
|
|
call1str = std::string(ocall);
|
|
|
|
|
|
|
|
int x22 = un64(a77, 12, 22);
|
|
|
|
|
|
|
|
if (hashes22.count(x22) > 0) {
|
|
|
|
ocall = hashes12[x22];
|
|
|
|
} else {
|
|
|
|
ocall = "<...22>";
|
|
|
|
}
|
|
|
|
|
2023-01-29 13:27:45 -05:00
|
|
|
hashes_mu.unlock();
|
2023-01-28 16:45:07 -05:00
|
|
|
call2str = std::string(ocall);
|
|
|
|
|
|
|
|
// mext bit is alway for R
|
|
|
|
int i = 12+ 22 +1;
|
|
|
|
// r3
|
|
|
|
int rst = un64(a77, i, 3);
|
|
|
|
rst = 52 + 10 * rst;
|
|
|
|
i += 3;
|
|
|
|
int qsonb = un64(a77, i, 11);
|
|
|
|
char report[16];
|
|
|
|
sprintf(report, "%d%04d", rst, qsonb);
|
|
|
|
i += 11;
|
|
|
|
// g25
|
|
|
|
int ng = un64(a77, i, 25);
|
|
|
|
locstr = unpackgrid25(ng);
|
|
|
|
|
|
|
|
std::string msg;
|
|
|
|
msg = call1str + " " + call2str + " " + std::string(report) + " " + locstr;
|
|
|
|
call1str += " " + std::string(report);
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
// free text
|
|
|
|
// 71 bits, 13 characters, each one of 42 choices.
|
|
|
|
// reversed.
|
|
|
|
// details from wsjt-x's packjt77.f90
|
2023-01-10 22:49:16 -05:00
|
|
|
std::string Packing::unpack_0_0(int a77[], std::string& call1str, std::string& call2str, std::string& locstr)
|
2023-01-08 13:03:29 -05:00
|
|
|
{
|
2023-01-28 05:39:44 -05:00
|
|
|
// bit fields: f71
|
2023-01-08 17:15:31 -05:00
|
|
|
(void) call2str;
|
|
|
|
(void) locstr;
|
2023-01-08 13:03:29 -05:00
|
|
|
// the 42 possible characters.
|
|
|
|
const char *cc = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?";
|
2023-01-10 22:49:16 -05:00
|
|
|
boost::multiprecision::int128_t x = un128(a77, 0, 71);
|
2023-01-08 13:03:29 -05:00
|
|
|
std::string msg = "0123456789123";
|
2023-01-19 17:51:43 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
for (int i = 0; i < 13; i++)
|
|
|
|
{
|
2023-01-10 22:49:16 -05:00
|
|
|
msg[13 - 1 - i] = cc[(int) (x % 42)];
|
2023-01-08 13:03:29 -05:00
|
|
|
x = x / 42;
|
|
|
|
}
|
2023-01-19 17:51:43 -05:00
|
|
|
|
2023-01-08 17:15:31 -05:00
|
|
|
call1str = msg;
|
2023-01-08 13:03:29 -05:00
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2023-01-28 05:39:44 -05:00
|
|
|
std::string Packing::unpack_0_1(int a77[], std::string& call1str, std::string& call2str, std::string& locstr)
|
|
|
|
{
|
|
|
|
// bit fields: c28 c28 h10 r5
|
|
|
|
int i = 0;
|
|
|
|
int call1 = un64(a77, i, 28); // c28
|
|
|
|
i += 28;
|
|
|
|
int call2 = un64(a77, i, 28); // c28
|
|
|
|
|
|
|
|
call1str = trim(unpackcall(call1)) + ";" + trim(unpackcall(call2));
|
|
|
|
|
|
|
|
i += 28;
|
|
|
|
int x10 = un64(a77, i, 10);
|
|
|
|
// 10-bit hash
|
|
|
|
hashes_mu.lock();
|
|
|
|
std::string ocall;
|
|
|
|
|
|
|
|
if (hashes10.count(x10) > 0)
|
|
|
|
{
|
|
|
|
call2str = hashes10[x10];
|
|
|
|
ocall = "<" + call2str + ">";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
call2str = "<...10>";
|
|
|
|
ocall = call2str;
|
|
|
|
}
|
|
|
|
|
|
|
|
hashes_mu.unlock();
|
|
|
|
i += 10;
|
|
|
|
int i5 = un64(a77, i, 5); // decode r5
|
|
|
|
int r = 2*i5 - 30;
|
|
|
|
char tmp[32];
|
|
|
|
|
|
|
|
if (r >= 0) {
|
|
|
|
sprintf(tmp, "+%02d", r);
|
|
|
|
} else {
|
|
|
|
sprintf(tmp, "-%02d", -r);
|
|
|
|
}
|
|
|
|
|
|
|
|
locstr = std::string(tmp);
|
|
|
|
std::string msg;
|
|
|
|
msg = trim(unpackcall(call1)) + " RR73;" + trim(unpackcall(call2)) + " " + ocall;
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2023-01-28 16:45:07 -05:00
|
|
|
std::string Packing::unpack_0_5(int a77[], std::string& call1str, std::string& call2str, std::string& locstr)
|
|
|
|
{
|
|
|
|
(void) call2str;
|
|
|
|
(void) locstr;
|
|
|
|
|
|
|
|
const char *cc = "0123456789ABCDEF";
|
|
|
|
std::string msg = "123456789ABCDEF012";
|
|
|
|
|
|
|
|
// first digit is on 3 bits
|
|
|
|
int d0 = un64(a77, 0, 3);
|
|
|
|
msg[17] = cc[d0];
|
|
|
|
// 17 hexadecimal digits = 17*4 = 68 bits
|
|
|
|
boost::multiprecision::int128_t x = un128(a77, 3, 68);
|
|
|
|
|
|
|
|
for (int i = 0; i < 17; i++)
|
|
|
|
{
|
|
|
|
msg[17 - 1 - i] = cc[(int) (x % 4)];
|
|
|
|
x = x / 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
call1str = msg;
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
// ARRL RTTY Round-Up states/provinces
|
2023-01-10 22:49:16 -05:00
|
|
|
const char *Packing::ru_states[] = {
|
2023-01-08 13:03:29 -05:00
|
|
|
"AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", "GA",
|
|
|
|
"HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD",
|
|
|
|
"MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ",
|
|
|
|
"NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC",
|
|
|
|
"SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY",
|
|
|
|
"NB", "NS", "QC", "ON", "MB", "SK", "AB", "BC", "NWT", "NF",
|
|
|
|
"LB", "NU", "YT", "PEI", "DC"};
|
|
|
|
|
|
|
|
// i3=3
|
|
|
|
// 3 TU; W9XYZ K1ABC R 579 MA 1 28 28 1 3 13 74 ARRL RTTY Roundup
|
|
|
|
// 1 TU
|
|
|
|
// 28 call1
|
|
|
|
// 28 call2
|
|
|
|
// 1 R
|
|
|
|
// 3 RST 529 to 599
|
|
|
|
// 13 state/province/serialnumber
|
2023-01-10 22:49:16 -05:00
|
|
|
std::string Packing::unpack_3(int a77[], std::string& call1str, std::string& call2str, std::string& locstr)
|
2023-01-08 13:03:29 -05:00
|
|
|
{
|
2023-01-08 17:15:31 -05:00
|
|
|
(void) locstr;
|
2023-01-08 13:03:29 -05:00
|
|
|
int i = 0;
|
|
|
|
int tu = a77[i];
|
|
|
|
i += 1;
|
2023-01-10 22:49:16 -05:00
|
|
|
int call1 = un64(a77, i, 28);
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 28;
|
2023-01-10 22:49:16 -05:00
|
|
|
int call2 = un64(a77, i, 28);
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 28;
|
|
|
|
int r = a77[i];
|
|
|
|
i += 1;
|
2023-01-10 22:49:16 -05:00
|
|
|
int rst = un64(a77, i, 3);
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 3;
|
2023-01-10 22:49:16 -05:00
|
|
|
int serial = un64(a77, i, 13);
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 13;
|
|
|
|
|
2023-01-08 17:15:31 -05:00
|
|
|
call1str = trim(unpackcall(call1));
|
|
|
|
call2str = trim(unpackcall(call2));
|
2023-01-08 13:03:29 -05:00
|
|
|
|
|
|
|
rst = 529 + 10 * rst;
|
|
|
|
|
|
|
|
int statei = serial - 8001;
|
|
|
|
std::string serialstr;
|
|
|
|
int nstates = sizeof(ru_states) / sizeof(ru_states[0]);
|
2023-01-19 17:51:43 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
if (serial > 8000 && statei < nstates)
|
|
|
|
{
|
|
|
|
serialstr = ru_states[statei];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char tmp[32];
|
|
|
|
sprintf(tmp, "%04d", serial);
|
2023-01-19 17:51:43 -05:00
|
|
|
serialstr = std::string(tmp);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string msg;
|
|
|
|
|
2023-01-19 17:51:43 -05:00
|
|
|
if (tu) {
|
2023-01-08 13:03:29 -05:00
|
|
|
msg += "TU; ";
|
|
|
|
}
|
2023-01-19 17:51:43 -05:00
|
|
|
|
2023-01-08 17:15:31 -05:00
|
|
|
msg += call1str + " " + call2str + " ";
|
2023-01-19 17:51:43 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
if (r)
|
|
|
|
{
|
|
|
|
msg += "R ";
|
|
|
|
}
|
|
|
|
{
|
|
|
|
char tmp[16];
|
|
|
|
sprintf(tmp, "%d ", rst);
|
2023-01-19 17:51:43 -05:00
|
|
|
msg += std::string(tmp);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
2023-01-19 17:51:43 -05:00
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
msg += serialstr;
|
|
|
|
|
2023-01-08 17:15:31 -05:00
|
|
|
remember_call(call1str);
|
|
|
|
remember_call(call2str);
|
2023-01-08 13:03:29 -05:00
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ARRL Field Day sections
|
2023-01-10 22:49:16 -05:00
|
|
|
const char *Packing::sections[] = {
|
2023-01-08 13:03:29 -05:00
|
|
|
"AB ", "AK ", "AL ", "AR ", "AZ ", "BC ", "CO ", "CT ", "DE ", "EB ",
|
|
|
|
"EMA", "ENY", "EPA", "EWA", "GA ", "GTA", "IA ", "ID ", "IL ", "IN ",
|
|
|
|
"KS ", "KY ", "LA ", "LAX", "MAR", "MB ", "MDC", "ME ", "MI ", "MN ",
|
|
|
|
"MO ", "MS ", "MT ", "NC ", "ND ", "NE ", "NFL", "NH ", "NL ", "NLI",
|
|
|
|
"NM ", "NNJ", "NNY", "NT ", "NTX", "NV ", "OH ", "OK ", "ONE", "ONN",
|
|
|
|
"ONS", "OR ", "ORG", "PAC", "PR ", "QC ", "RI ", "SB ", "SC ", "SCV",
|
|
|
|
"SD ", "SDG", "SF ", "SFL", "SJV", "SK ", "SNJ", "STX", "SV ", "TN ",
|
|
|
|
"UT ", "VA ", "VI ", "VT ", "WCF", "WI ", "WMA", "WNY", "WPA", "WTX",
|
|
|
|
"WV ", "WWA", "WY ", "DX "};
|
|
|
|
|
|
|
|
// i3 = 0, n3 = 3 or 4: ARRL Field Day
|
|
|
|
// 0.3 WA9XYZ KA1ABC R 16A EMA 28 28 1 4 3 7 71 ARRL Field Day
|
|
|
|
// 0.4 WA9XYZ KA1ABC R 32A EMA 28 28 1 4 3 7 71 ARRL Field Day
|
2023-01-10 22:49:16 -05:00
|
|
|
std::string Packing::unpack_0_3(int a77[], int n3, std::string& call1str, std::string& call2str, std::string& locstr)
|
2023-01-08 13:03:29 -05:00
|
|
|
{
|
2023-01-08 17:15:31 -05:00
|
|
|
(void) locstr;
|
2023-01-08 13:03:29 -05:00
|
|
|
int i = 0;
|
2023-01-10 22:49:16 -05:00
|
|
|
int call1 = un64(a77, i, 28);
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 28;
|
2023-01-10 22:49:16 -05:00
|
|
|
int call2 = un64(a77, i, 28);
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 28;
|
2023-01-10 22:49:16 -05:00
|
|
|
int R = un64(a77, i, 1);
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 1;
|
2023-01-10 22:49:16 -05:00
|
|
|
int n_transmitters = un64(a77, i, 4);
|
2023-01-08 17:15:31 -05:00
|
|
|
|
|
|
|
if (n3 == 4) {
|
2023-01-08 13:03:29 -05:00
|
|
|
n_transmitters += 16;
|
2023-01-08 17:15:31 -05:00
|
|
|
}
|
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 4;
|
2023-01-10 22:49:16 -05:00
|
|
|
int clss = un64(a77, i, 3); // class
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 3;
|
2023-01-10 22:49:16 -05:00
|
|
|
int section = un64(a77, i, 7); // ARRL section
|
2023-01-08 13:03:29 -05:00
|
|
|
i += 7;
|
|
|
|
|
|
|
|
std::string msg;
|
2023-01-08 17:15:31 -05:00
|
|
|
call1str = trim(unpackcall(call1));
|
|
|
|
msg += call1str;
|
2023-01-08 13:03:29 -05:00
|
|
|
msg += " ";
|
2023-01-08 17:15:31 -05:00
|
|
|
call2str = trim(unpackcall(call2));
|
|
|
|
msg += call2str;
|
2023-01-08 13:03:29 -05:00
|
|
|
msg += " ";
|
2023-01-08 17:15:31 -05:00
|
|
|
|
|
|
|
if (R) {
|
2023-01-08 13:03:29 -05:00
|
|
|
msg += "R ";
|
2023-01-08 17:15:31 -05:00
|
|
|
}
|
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
{
|
|
|
|
char tmp[16];
|
|
|
|
sprintf(tmp, "%d%c ", n_transmitters + 1, clss + 'A');
|
2023-01-19 17:51:43 -05:00
|
|
|
msg += std::string(tmp);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
2023-01-08 17:15:31 -05:00
|
|
|
|
|
|
|
if (section - 1 >= 0 && section - 1 < (int)(sizeof(sections) / sizeof(sections[0]))) {
|
2023-01-08 13:03:29 -05:00
|
|
|
msg += sections[section - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// unpack an FT8 message.
|
|
|
|
// a77 is 91 bits -- 77 plus the 14-bit CRC.
|
|
|
|
// CRC and LDPC have already been checked.
|
|
|
|
// details from wsjt-x's packjt77.f90 and 77bit.txt.
|
|
|
|
//
|
2023-01-28 05:39:44 -05:00
|
|
|
std::string Packing::unpack(int a77[], std::string& call1, std::string& call2, std::string& loc, std::string& type)
|
2023-01-08 13:03:29 -05:00
|
|
|
{
|
2023-01-10 22:49:16 -05:00
|
|
|
int i3 = un64(a77, 74, 3);
|
|
|
|
int n3 = un64(a77, 71, 3);
|
2023-01-28 05:39:44 -05:00
|
|
|
char tmp[64];
|
|
|
|
|
|
|
|
if (i3 == 0) {
|
|
|
|
sprintf(tmp, "%d.%d", i3, n3);
|
|
|
|
} else {
|
|
|
|
sprintf(tmp, "%d", i3);
|
|
|
|
}
|
|
|
|
|
|
|
|
type = std::string(tmp);
|
2023-01-08 13:03:29 -05:00
|
|
|
|
|
|
|
if (i3 == 0 && n3 == 0)
|
|
|
|
{
|
|
|
|
// free text
|
2023-01-08 17:15:31 -05:00
|
|
|
return unpack_0_0(a77, call1, call2, loc);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
2023-01-28 05:39:44 -05:00
|
|
|
if (i3 == 0 && n3 == 1)
|
|
|
|
{
|
|
|
|
// DXpedition
|
|
|
|
return unpack_0_1(a77, call1, call2, loc);
|
|
|
|
}
|
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
if (i3 == 0 && (n3 == 3 || n3 == 4))
|
|
|
|
{
|
|
|
|
// ARRL Field Day
|
2023-01-08 17:15:31 -05:00
|
|
|
return unpack_0_3(a77, n3, call1, call2, loc);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
2023-01-28 16:45:07 -05:00
|
|
|
if (i3 == 0 && n3 == 5)
|
|
|
|
{
|
|
|
|
// telemetry
|
|
|
|
return unpack_0_5(a77, call1, call2, loc);
|
|
|
|
}
|
|
|
|
|
2023-01-08 13:03:29 -05:00
|
|
|
if (i3 == 1 || i3 == 2)
|
|
|
|
{
|
2023-01-28 05:39:44 -05:00
|
|
|
// ordinary message or EU VHF
|
2023-01-08 17:15:31 -05:00
|
|
|
return unpack_1(a77, call1, call2, loc);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (i3 == 3)
|
|
|
|
{
|
|
|
|
// RTTY Round-Up
|
2023-01-08 17:15:31 -05:00
|
|
|
return unpack_3(a77, call1, call2, loc);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (i3 == 4)
|
|
|
|
{
|
2023-01-28 05:39:44 -05:00
|
|
|
// call that doesn't fit in 28 bits (non standard call)
|
2023-01-08 17:15:31 -05:00
|
|
|
return unpack_4(a77, call1, call2, loc);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
2023-01-28 16:45:07 -05:00
|
|
|
if (i3 == 5)
|
|
|
|
{
|
|
|
|
// EU VHF with 6 digits locator
|
|
|
|
return unpack_5(a77, call1, call2, loc);
|
|
|
|
}
|
2023-01-28 05:39:44 -05:00
|
|
|
|
2023-01-19 17:51:43 -05:00
|
|
|
call1 = "UNK";
|
2023-01-08 13:03:29 -05:00
|
|
|
sprintf(tmp, "UNK i3=%d n3=%d", i3, n3);
|
2023-01-19 17:51:43 -05:00
|
|
|
return std::string(tmp);
|
2023-01-08 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace FT8
|