The FT8/FT4 communication protocols use an 83x91 generator matrix for the low-density parity check code. I am interested in MATLAB recasting of portions of these protocols. Can anyone explain to me where the numbers in the generator matrix come from?
$\begingroup$
$\endgroup$
8
-
$\begingroup$ Hi Gerald, and welcome to ham.stackexchange.com! $\endgroup$– rclocher3Commented Aug 25, 2020 at 21:55
-
$\begingroup$ Hi! I've never looked in detail into the FEC in FT8. I don't even precisely know where to look for documentation. Is there some design document by the original inventor? $\endgroup$– Marcus MüllerCommented Aug 26, 2020 at 7:53
-
$\begingroup$ The July/August 2020 issue of ARRL QEX had an article (starting page 7) containing a full description of the FT8 design, with K9AN and K1JT listed as authors. See my answer here: ham.stackexchange.com/questions/9962/… $\endgroup$– hotpaw2Commented Aug 26, 2020 at 16:51
-
$\begingroup$ @hotpaw2 thanks! Helps a lot. $\endgroup$– Marcus MüllerCommented Aug 26, 2020 at 17:30
-
$\begingroup$ Gerald, before I draft an answer: How much do you understand about block codes? Where should I start? $\endgroup$– Marcus MüllerCommented Aug 27, 2020 at 12:19
|
Show 3 more comments
1 Answer
$\begingroup$
$\endgroup$
1
I wrote a matlab script that generates ft8 message CQ calls. I think the hex generator matrix might help you:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% FT8 Transmit Code %
% Reference credit to Dr. Jonathon Y. Cheah (NZ0C) %
% for the use of dome of this code %
% Output is audioband cp 4fsk FT8 message which can be fed to RF carrier modulation hardware or software %
% for transmission over the air %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
close all
clear all
clc
%%
% user entries ================== % call must have 6 letter
call='BC1DE ' % spaces for standard HAM
loc='JO01' % Radio callsign.
% ==================
%
% process various callsign types
if (chr_normf(call(3))>9)%right shift call sign
call(6)=call(5);
call(5)=call(4);
call(4)=call(3);
call(3)=call(2);
call(2)=call(1);
call(1)=' ';
end
%
calldec=zeros(size(call));
locdec=zeros(size(loc));
chrset=' 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
pad28=[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0];
calltype=[0 0 0 0 0 1]; %initialize calltype
%%
for(i=1:37)
if(chrset(i)==call(1))
calldec(1)=i-1;
end
end
chrset='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
for(i=1:36)
if(chrset(i)==call(2))
calldec(2)=i-1;
end
end
chrset='0123456789';
for(i=1:10)
if(chrset(i)==call(3))
calldec(3)=i-1;
end
end
chrset=' ABCDEFGHIJKLMNOPQRSTUVWXYZ';
for(ii=4:6)
for(i=1:27)
if(chrset(i)==call(ii))
calldec(ii)=i-1;
end
end
end
%%
varbase=[37 36 10 27 27 27];
decint=calldec(1)*36*10*27*27*27 + 10*27*27*27*calldec(2) + 27*27*27*calldec(3) + 27*27*calldec(4) + 27*calldec(5) + calldec(6);
decint=decint+6257896;
calltype(1)=mod(decint,2);
%%
chrset = 'ABCDEFGHIJKLMNOPQR';
for(ii=1:2)
for(i=1:18)
if(chrset(i)==loc(ii))
locdec(ii)=i-1;
end
end
end
chrset='0123456789';
for(ii=3:4)
for(i=1:10)
if(chrset(i)==loc(ii))
locdec(ii)=i-1;
end
end
end
locbase=[18 18 10 10];
locint=18*10*10*locdec(1)+10*10*locdec(2)+10*locdec(3)+locdec(4);
%%
binint=de2bi(decint,'left-msb',28);
binint=[0 binint(1:end-1)];%rotate right once not sure why but appears necessary
%while(size(binint)<28) %zero pad for 28 bits
%binint=[ 0 binint];
%end
%while(binint(end)==0)
% binint=[ 0 binint(1:end-1)];
%end
locbin=de2bi(locint);
while(size(locbin)<15)
locbin=[0 locbin ];
end
locbin=fliplr(locbin);
%%
chrset='0123456789'
%for(u=1:10)
% if(loc(4)==chrset(u))
% loc3=de2bi(u-1,4,'left-msb')
%break
%end
%end
%calltype(1:3)=loc3(2:4);
%locbin(end)=loc3(1);
MB=[ pad28 binint calltype(1:3) locbin calltype(4:end)];
%%
MB=[MB 0 0 0 0 0]
%CSMOD=[CS(8:36) CS(43:72)]
crc14poly =[ 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1 ]
%crc14poly =fliplr(crc14poly);
%crc14poly=fliplr(crc14poly);
%'z^14 + z^13 + z ^10+ z^9+z^8+z^6+z^4+z^2+z+1'
ft8_crc= comm.CRCGenerator('Polynomial',[1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1 ])
MB=MB';
CRC=ft8_crc(MB);
CRC=CRC';
CRC=[CRC(1:77) CRC(83:end)];
%%
% LDPC generator matrix from WSJT-X's ldpc_174_91_c_generator.f90.
% 83 rows, since LDPC(174,91) needs 83 parity bits.
% each row has 23 hex digits, to be turned into 91 bits,
% to be xor'd with the 91 data bits.
%
rawg = [
hexToBinaryVector( "8329ce11bf31eaf509f27fc",92),
hexToBinaryVector("761c264e25c259335493132",92),
hexToBinaryVector("dc265902fb277c6410a1bdc",92),
hexToBinaryVector("1b3f417858cd2dd33ec7f62",92),
hexToBinaryVector("09fda4fee04195fd034783a",92),
hexToBinaryVector("077cccc11b8873ed5c3d48a",92),
hexToBinaryVector("29b62afe3ca036f4fe1a9da",92),
hexToBinaryVector("6054faf5f35d96d3b0c8c3e",92),
hexToBinaryVector("e20798e4310eed27884ae90",92),
hexToBinaryVector("775c9c08e80e26ddae56318",92),
hexToBinaryVector("b0b811028c2bf997213487c",92),
hexToBinaryVector("18a0c9231fc60adf5c5ea32",92),
hexToBinaryVector("76471e8302a0721e01b12b8",92),
hexToBinaryVector("ffbccb80ca8341fafb47b2e",92),
hexToBinaryVector("66a72a158f9325a2bf67170",92),
hexToBinaryVector("c4243689fe85b1c51363a18",92),
hexToBinaryVector("0dff739414d1a1b34b1c270",92),
hexToBinaryVector("15b48830636c8b99894972e",92),
hexToBinaryVector("29a89c0d3de81d665489b0e",92),
hexToBinaryVector("4f126f37fa51cbe61bd6b94",92),
hexToBinaryVector("99c47239d0d97d3c84e0940",92),
hexToBinaryVector("1919b75119765621bb4f1e8",92),
hexToBinaryVector("09db12d731faee0b86df6b8",92),
hexToBinaryVector("488fc33df43fbdeea4eafb4",92),
hexToBinaryVector("827423ee40b675f756eb5fe",92),
hexToBinaryVector("abe197c484cb74757144a9a",92),
hexToBinaryVector("2b500e4bc0ec5a6d2bdbdd0",92),
hexToBinaryVector("c474aa53d70218761669360",92),
hexToBinaryVector("8eba1a13db3390bd6718cec",92),
hexToBinaryVector("753844673a27782cc42012e",92),
hexToBinaryVector("06ff83a145c37035a5c1268",92),
hexToBinaryVector("3b37417858cc2dd33ec3f62",92),
hexToBinaryVector("9a4a5a28ee17ca9c324842c",92),
hexToBinaryVector("bc29f465309c977e89610a4",92),
hexToBinaryVector("2663ae6ddf8b5ce2bb29488",92),
hexToBinaryVector("46f231efe457034c1814418",92),
hexToBinaryVector("3fb2ce85abe9b0c72e06fbe",92),
hexToBinaryVector("de87481f282c153971a0a2e",92),
hexToBinaryVector("fcd7ccf23c69fa99bba1412",92),
hexToBinaryVector("f0261447e9490ca8e474cec",92),
hexToBinaryVector("4410115818196f95cdd7012",92),
hexToBinaryVector("088fc31df4bfbde2a4eafb4",92),
hexToBinaryVector("b8fef1b6307729fb0a078c0",92),
hexToBinaryVector("5afea7acccb77bbc9d99a90",92),
hexToBinaryVector("49a7016ac653f65ecdc9076",92),
hexToBinaryVector("1944d085be4e7da8d6cc7d0",92),
hexToBinaryVector("251f62adc4032f0ee714002",92),
hexToBinaryVector("56471f8702a0721e00b12b8",92),
hexToBinaryVector("2b8e4923f2dd51e2d537fa0",92),
hexToBinaryVector("6b550a40a66f4755de95c26",92),
hexToBinaryVector("a18ad28d4e27fe92a4f6c84",92),
hexToBinaryVector("10c2e586388cb82a3d80758",92),
hexToBinaryVector("ef34a41817ee02133db2eb0",92),
hexToBinaryVector("7e9c0c54325a9c15836e000",92),
hexToBinaryVector("3693e572d1fde4cdf079e86",92),
hexToBinaryVector("bfb2cec5abe1b0c72e07fbe",92),
hexToBinaryVector("7ee18230c583cccc57d4b08",92),
hexToBinaryVector("a066cb2fedafc9f52664126",92),
hexToBinaryVector("bb23725abc47cc5f4cc4cd2",92),
hexToBinaryVector("ded9dba3bee40c59b5609b4",92),
hexToBinaryVector("d9a7016ac653e6decdc9036",92),
hexToBinaryVector("9ad46aed5f707f280ab5fc4",92),
hexToBinaryVector("e5921c77822587316d7d3c2",92),
hexToBinaryVector("4f14da8242a8b86dca73352",92),
hexToBinaryVector("8b8b507ad467d4441df770e",92),
hexToBinaryVector("22831c9cf1169467ad04b68",92),
hexToBinaryVector("213b838fe2ae54c38ee7180",92),
hexToBinaryVector("5d926b6dd71f085181a4e12",92),
hexToBinaryVector("66ab79d4b29ee6e69509e56",92),
hexToBinaryVector("958148682d748a38dd68baa",92),
hexToBinaryVector("b8ce020cf069c32a723ab14",92),
hexToBinaryVector("f4331d6d461607e95752746",92),
hexToBinaryVector("6da23ba424b9596133cf9c8",92),
hexToBinaryVector("a636bcbc7b30c5fbeae67fe",92),
hexToBinaryVector("5cb0d86a07df654a9089a20",92),
hexToBinaryVector("f11f106848780fc9ecdd80a",92),
hexToBinaryVector("1fbb5364fb8d2c9d730d5ba",92),
hexToBinaryVector("fcb86bc70a50c9d02a5d034",92),
hexToBinaryVector("a534433029eac15f322e34c",92),
hexToBinaryVector("c989d9c7c3d3b8c55d75130",92),
hexToBinaryVector("7bb38b2f0186d46643ae962",92),
hexToBinaryVector("2644ebadeb44b9467d1f42c",92),
hexToBinaryVector("608cc857594bfbb55d69600",92)
];
%%
%rawg=hex2dec(rawg);
%rawg=de2bi(rawg,'left-msb');
%rawg=fliplr(rawg);
%for(j=1:83)
%rawg(j,:)=hexToBinaryVector(rawg(j,:),'left-msb');
%end
rawg=rawg(:,1:end-1);
CRCMAT=zeros(size(rawg));
%CRC=CRC';
%col_mask=[1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0];
iii=0;
for(i=1:83)
CRCMAT(i,:)=CRC(:,:);
end
%OUT=bitand(rawg,CRCMAT);
%OUT=xor(rawg,CRCMAT);
OUT=rawg.*CRCMAT;
%%
out=zeros(1,83);
SUM=zeros(1,83);
%temp=[];
for(j=1:83)
SUM(1,j)=sum(OUT(j,:));
%%
out(1,j)=mod(SUM(1,j),2);
end
%%
%if(j==1)
% out(1,j)=mod(SUM(1,j),2); %check if we need to set a parity bit
%else
% out(1,j)=xor(out(1,j-1),mod(SUM(1,j),2));
%end
%if(out(1,j)==1)
%out=out|col_mask
%out=or(out,col_mask)
%end
% col_mask=[0 col_mask(1:end-1)];
%end
%CRC=[CRC fliplr(out)];
CRC=[CRC out];
%%
CRC58_3=reshape(CRC,3,58);
CRC58_3=CRC58_3';
CRCSYM=bi2de(CRC58_3,'left-msb');
graymap = [ 0, 1, 3, 2, 5, 6, 4, 7 ];
%graymap = [ 0 1 3 2 6 7 5 4 ];
key=[0:7];
for (ii=1:58)
for (jj=1:8)
if CRCSYM(ii)==key(jj)
CRCSYM(ii)=graymap(jj);
break
else
end
end
end
costas_symbols = [ 3, 1, 4, 0, 6, 5, 2 ];
costas_symbols=costas_symbols';
%%
CHANSYM=[costas_symbols;CRCSYM(1:29);costas_symbols;CRCSYM(30:end);costas_symbols];
%%
%%
sintablen = 7680;
SINTAB = sin(2*pi*(0:sintablen-1)./sintablen);
fs = 48000;
%%
F_required = 1200;
index = 1; step = (F_required/fs)*sintablen;
for i = 1:7680
sin1200Hz(i) = SINTAB(round(index));
index = index+step;
if index>sintablen
index = index-sintablen;
end
end
%%
F_required = 1200+(6.25);
index = 1; step = (F_required/fs)*sintablen;
for i = 1:7680
sin1206Hz(i) = SINTAB(round(index));
index = index+step;
if index>sintablen
index = index-sintablen;
end
end
%%
F_required = 1200+(12.5);
index = 1; step = (F_required/fs)*sintablen;
for i = 1:7680
sin1212Hz(i) = SINTAB(round(index));
index = index+step;
if index>sintablen
index = index-sintablen;
end
end
%%
F_required = 1200+(18.75);
index = 1; step = (F_required/fs)*sintablen;
for i = 1:7680
sin1218Hz(i) = SINTAB(round(index));
index = index+step;
if index>sintablen
index = index-sintablen;
end
end
%%
F_required = 1200+(25);
index = 1; step = (F_required/fs)*sintablen;
for i = 1:7680
sin1225Hz(i) = SINTAB(round(index));
index = index+step;
if index>sintablen
index = index-sintablen;
end
end
%%
F_required = 1200+(31.25);
index = 1; step = (F_required/fs)*sintablen;
for i = 1:7680
sin1231Hz(i) = SINTAB(round(index));
index = index+step;
if index>sintablen
index = index-sintablen;
end
end
%%
F_required = 1200+(37.5);
index = 1; step = (F_required/fs)*sintablen;
for i = 1:7680
sin1237Hz(i) = SINTAB(round(index));
index = index+step;
if index>sintablen
index = index-sintablen;
end
end
%%
F_required = 1200+(43.75);
index = 1; step = (F_required/fs)*sintablen;
for i = 1:7680
sin1243Hz(i) = SINTAB(round(index));
index = index+step;
if index>sintablen
index = index-sintablen;
end
end
%%
n=length(CHANSYM);
Xcpfsk=[];
for z=1:n
rep=1;
rep=rep+1;
%end
if(CHANSYM(z)==0)
Xcpfsk=[Xcpfsk,sin1200Hz(1:7680)];
z=z+1;
elseif(CHANSYM(z)==1)
Xcpfsk=[Xcpfsk,sin1206Hz(1:7680)];
z=z+1;
elseif(CHANSYM(z)==2)
Xcpfsk=[Xcpfsk,sin1212Hz(1:7680)];
z=z+1;
elseif(CHANSYM(z)==3)
Xcpfsk=[Xcpfsk,sin1218Hz(1:7680)];
z=z+1;
elseif(CHANSYM(z)==4)
Xcpfsk=[Xcpfsk,sin1225Hz(1:7680)];
z=z+1;
elseif(CHANSYM(z)==5)
Xcpfsk=[Xcpfsk,sin1231Hz(1:7680)];
z=z+1;
elseif(CHANSYM(z)==6)
Xcpfsk=[Xcpfsk,sin1237Hz(1:7680)];
z=z+1;
elseif(CHANSYM(z)==7)
Xcpfsk=[Xcpfsk,sin1243Hz(1:7680)];
z=z+1;
else
disp('error');
return
end
end
%% Manually execute this command in the command window on at the start of the minute and thereafter every 15 seconds
% to successfully transmit a FT8 message
% sound(Xcpfsk,48000)
-
$\begingroup$ Hi James, and welcome to ham.stackexchange.com! This site is not well-suited to sharing long blocks of code. In the future, please consider hosting the code somewhere else, such as GitHub, and then linking to it here. Thanks for sharing your code! $\endgroup$ Commented Jan 27, 2022 at 0:34