Building sfrx[.exe] in the wsjtx repository is now basically operational.

This commit is contained in:
Joe Taylor 2024-09-16 14:49:07 -04:00
parent 073e644f12
commit d845bd0466
32 changed files with 3113 additions and 11 deletions

View File

@ -353,6 +353,10 @@ set (wsjt_FSRCS
lib/wavhdr.f90
lib/qra/q65/q65_encoding_modules.f90
lib/ft8/ft8_a7.f90
lib/superfox/sfox_mod.f90
lib/superfox/julian.f90
lib/superfox/popen_module.f90
lib/superfox/qpc/qpc_mod.f90
# remaining non-module sources
lib/addit.f90
@ -597,8 +601,18 @@ set (wsjt_FSRCS
lib/fst4/get_crc24.f90
lib/fst4/fst4_baseline.f90
lib/77bit/hash22calc.f90
lib/superfox/qpc_decode2.f90
lib/superfox/qpc_likelihoods2.f90
lib/superfox/qpc_snr.f90
lib/superfox/qpc_sync.f90
lib/superfox/sfox_ana.f90
lib/superfox/sfox_demod.f90
lib/superfox/sfox_remove_ft8.f90
lib/superfox/sfox_unpack.f90
lib/superfox/sfox_wave.f90
lib/superfox/sfox_wave_gfsk.f90
lib/superfox/twkfreq2.f90
)
# temporary workaround for a gfortran v7.3 ICE on Fedora 27 64-bit
@ -641,6 +655,15 @@ set (wsjt_CSRCS
lib/wrapkarn.c
${ldpc_CSRCS}
${qra_CSRCS}
lib/superfox/qpc/dbgprintf.c
lib/superfox/qpc/nhash2.c
lib/superfox/qpc/np_qpc.c
lib/superfox/qpc/np_rnd.c
lib/superfox/qpc/qpc_fwht.c
lib/superfox/qpc/qpc_n127k50q128.c
lib/superfox/qpc/qpc_subs.c
)
set (wsjt_qt_UISRCS
@ -1226,6 +1249,9 @@ target_link_libraries (echosim wsjt_fort wsjt_cxx)
add_executable (ft8sim lib/ft8/ft8sim.f90)
target_link_libraries (ft8sim wsjt_fort wsjt_cxx)
add_executable (sfrx lib/superfox/sfrx.f90)
target_link_libraries (sfrx wsjt_fort wsjt_cxx)
add_executable (msk144sim lib/msk144sim.f90)
target_link_libraries (msk144sim wsjt_fort wsjt_cxx)
@ -1613,7 +1639,7 @@ install (TARGETS udp_daemon message_aggregator wsjtx_app_version
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
)
install (TARGETS jt9 wsprd fmtave fcal fmeasure
install (TARGETS jt9 wsprd fmtave fcal fmeasure sfrx
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
)
@ -1686,7 +1712,7 @@ if (WIN32)
endif ()
install (FILES
${sfox_dir}/sfrx.exe
# ${sfox_dir}/sfrx.exe
${sfox_dir}/sftx.exe
${sfox_dir}/foxchk.exe
DESTINATION ${CMAKE_INSTALL_BINDIR}
@ -1696,7 +1722,7 @@ endif (WIN32)
if (UNIX)
install (FILES
lib/superfox/linux/sfrx
# lib/superfox/linux/sfrx
lib/superfox/linux/sftx
lib/superfox/linux/foxchk
DESTINATION ${CMAKE_INSTALL_BINDIR}
@ -1709,7 +1735,7 @@ endif (UNIX)
if (APPLE)
install (FILES
lib/superfox/mac/sfrx
# lib/superfox/mac/sfrx
lib/superfox/mac/sftx
lib/superfox/mac/foxchk
DESTINATION ${CMAKE_INSTALL_BINDIR}
@ -1729,7 +1755,7 @@ execute_process(COMMAND
if(${CMAKE_DEB_HOST_ARCH} MATCHES "arm64")
install (FILES
lib/superfox/arm/sfrx
# lib/superfox/arm/sfrx
lib/superfox/arm/sftx
lib/superfox/arm/foxchk
DESTINATION ${CMAKE_INSTALL_BINDIR}
@ -1742,7 +1768,7 @@ endif()
if(${CMAKE_DEB_HOST_ARCH} MATCHES "armhf")
install (FILES
lib/superfox/arm32/sfrx
# lib/superfox/arm32/sfrx
lib/superfox/arm32/sftx
lib/superfox/arm32/foxchk
DESTINATION ${CMAKE_INSTALL_BINDIR}

View File

@ -8,10 +8,10 @@ subroutine ft8_downsample(dd,newdat,f0,c1)
logical newdat,first
complex c1(0:NFFT2-1)
complex cx(0:NFFT1/2)
real dd(NMAX),x(NFFT1),taper(0:100)
equivalence (x,cx)
real dd(NMAX),x(NFFT1+2),taper(0:100)
data first/.true./
save cx,first,taper
save x,cx,first,taper
equivalence (x,cx)
if(first) then
pi=4.0*atan(1.0)
@ -23,11 +23,10 @@ subroutine ft8_downsample(dd,newdat,f0,c1)
if(newdat) then
! Data in dd have changed, recompute the long FFT
x(1:NMAX)=dd
x(NMAX+1:NFFT1)=0. !Zero-pad the x array
x(NMAX+1:NFFT1+2)=0. !Zero-pad the x array
call four2a(cx,NFFT1,1,-1,0) !r2c FFT to freq domain
newdat=.false.
endif
df=12000.0/NFFT1
baud=12000.0/NSPS
i0=nint(f0/df)

View File

@ -0,0 +1,12 @@
! LDPC (174,91) code
parameter (KK=91) !Information bits (77 + CRC14)
parameter (ND=58) !Data symbols
parameter (NS=21) !Sync symbols (3 @ Costas 7x7)
parameter (NN=NS+ND) !Total channel symbols (79)
parameter (NSPS=1920) !Samples per symbol at 12000 S/s
parameter (NZ=NSPS*NN) !Samples in full 15 s waveform (151,680)
parameter (NMAX=15*12000) !Samples in iwave (180,000)
parameter (NFFT1=2*NSPS, NH1=NFFT1/2) !Length of FFTs for symbol spectra
parameter (NSTEP=NSPS/4) !Rough time-sync step size
parameter (NHSYM=NMAX/NSTEP-3) !Number of symbol spectra (1/4-sym steps)
parameter (NDOWN=60) !Downsample factor

3
lib/superfox/gtag.f90 Normal file
View File

@ -0,0 +1,3 @@
integer ntag
data ntag/z"1234567"/
!-----------------------------------------------------------------------

39
lib/superfox/julian.f90 Normal file
View File

@ -0,0 +1,39 @@
module julian
contains
integer*8 function itime8()
implicit integer (a-z)
! 1 2 3 4 5 6 7
integer it(8) !y m d nmin h m s
integer*8 secday
data secday/86400/
call date_and_time(values=it)
iyr=it(1)
imo=it(2)
iday=it(3)
days=JD(iyr,imo,iday) - 2440588 !Days since epoch Jan 1, 1970
ih=it(5)
im=it(6)-it(4) !it(4) corrects for time zone
is=it(7)
nsec=3600*ih + 60*im + is
itime8=days*secday + nsec
return
end function itime8
integer function JD(I,J,K)
! Return Julian Date for I=year, J=month, K=day
! Reference: Fliegel and Van Flandern, Comm. ACM 11, 10, 1968
JD = K - 32075 + 1461*(I + 4800 + (J - 14)/12)/4 &
+ 367*(J - 2 - (J - 14)/12*12)/12 - 3 &
*((I + 4900 + (J - 14)/12)/100)/4
return
end function JD
end module julian

View File

@ -0,0 +1,103 @@
!*******************************************************************************
!>
! A simple module for `popen`.
module popen_module
use,intrinsic :: iso_c_binding
implicit none
private
! interfaces to C functions
interface
function popen(command, mode) bind(C,name='popen')
!! initiate pipe streams to or from a process
import :: c_char, c_ptr
implicit none
character(kind=c_char),dimension(*) :: command
character(kind=c_char),dimension(*) :: mode
type(c_ptr) :: popen
end function popen
function fgets(s, siz, stream) bind(C,name='fgets')
!! get a string from a stream
import :: c_char, c_ptr, c_int
implicit none
type (c_ptr) :: fgets
character(kind=c_char),dimension(*) :: s
integer(kind=c_int),value :: siz
type(c_ptr),value :: stream
end function fgets
function pclose(stream) bind(C,name='pclose')
!! close a pipe stream to or from a process
import :: c_ptr, c_int
implicit none
integer(c_int) :: pclose
type(c_ptr),value :: stream
end function pclose
end interface
public :: c2f_string, get_command_as_string
contains
!*******************************************************************************
!>
! Convert a C string to a Fortran string.
function c2f_string(c) result(f)
character(len=*),intent(in) :: c !! C string
character(len=:),allocatable :: f !! Fortran string
integer :: i
i = index(c,c_null_char)
if (i<=0) then
f = c
else if (i==1) then
f = ''
else if (i>1) then
f = c(1:i-1)
end if
end function c2f_string
!*******************************************************************************
!>
! Return the result of the command as a string.
function get_command_as_string(command) result(str)
character(len=*),intent(in) :: command !! the command to run
character(len=:),allocatable :: str !! the result of that command
integer,parameter :: buffer_length = 1000 !! read stream in chunks of this size
type(c_ptr) :: h !! for `popen`
integer(c_int) :: istat !! `pclose` status
character(kind=c_char,len=buffer_length) :: line !! buffer to read from `fgets`
str = ''
h = c_null_ptr
h = popen(command//c_null_char,'r'//c_null_char)
if (c_associated(h)) then
do while (c_associated(fgets(line,buffer_length,h)))
str = str//c2f_string(line)
end do
istat = pclose(h)
end if
end function get_command_as_string
!*******************************************************************************
end module popen_module
!*******************************************************************************

View File

@ -0,0 +1,67 @@
// ------------------------------------------------------------------------------
// dbgprintf.c
// Functions for printing debug information
//
// (c) 2024 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
//
// This source 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.
// This file 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 this source distribution.
// If not, see <http://www.gnu.org/licenses/>.
// ------------------------------------------------------------------------------
#include "dbgprintf.h"
// print functions for debug purposes
void dbgprintf_vector_uchar(const char* name, const unsigned char* v, int vsize)
{
int k;
printf("%s=", name);
for (k = 0; k < vsize; k++) {
if ((k & 0x0F) == 0)
printf("\n");
printf("%02X ", v[k]);
}
printf("\n");
}
void dbgprintf_vector_float(const char* name, const float* v, int vsize)
{
int k;
printf("%s=", name);
for (k = 0; k < vsize; k++) {
if ((k & 0x07) == 0)
printf("\n");
printf("%10.3f ", v[k]);
}
printf("\n");
}
void dbgprintf_rows_float(const char* name, const float* v, int vsize, int nrows)
{
int k;
int j;
printf("%s=\n", name);
for (j = 0; j < nrows; j++) {
printf("r%d:", j);
for (k = 0; k < vsize; k++) {
if ((k & 0x07) == 0)
printf("\n");
printf("%7.3f ", v[k]);
}
printf("\n");
v += vsize;
}
}

View File

@ -0,0 +1,64 @@
// ------------------------------------------------------------------------------
// dbgprintf.h
// Functions for printing debug information
//
// (c) 2024 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
//
// This source 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.
// This file 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 this source distribution.
// If not, see <http://www.gnu.org/licenses/>.
// ------------------------------------------------------------------------------
#ifndef _dbgprintf_h_
#define _dbgprintf_h_
// Define DBGPRINTF_ANYWAY
// in order to print debug information also
// when _DEBUG is not defined
#define DBGPRINTF_ANYWAY
#ifdef _DEBUG
#define DBG_PRINTF
#else
#ifdef DBGPRINTF_ANYWAY
#define DBG_PRINTF
#endif
#endif
#include <stdio.h>
// print functions for debug purposes
#ifdef DBG_PRINTF
#ifdef __cplusplus
extern "C" {
#endif
void dbgprintf_vector_uchar(const char* name, const unsigned char* v, int vsize);
void dbgprintf_vector_float(const char* name, const float* v, int vsize);
void dbgprintf_rows_float(const char* name, const float* v, int vsize, int nrows);
#ifdef __cplusplus
}
#endif
#else
#define dbgprintf_vector_uchar(a,b)
#define dbgprintf_vector_float(a,b)
#define dbgprintf_rows_float(a, b, c, d)
#endif
#endif // _dbgprintf_h_

383
lib/superfox/qpc/nhash2.c Normal file
View File

@ -0,0 +1,383 @@
/*
File name: nhash2.c
*------------------------------------------------------------------------------
*
* This file is part of the WSPR application, Weak Signal Propogation Reporter
*
* File Name: nhash.c
* Description: Functions to produce 32-bit hashes for hash table lookup
*
* Copyright (C) 2008-2014 Joseph Taylor, K1JT
* License: GNU GPL v3+
*
* 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; either 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 for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
* Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Files: lookup3.c
* Copyright: Copyright (C) 2006 Bob Jenkins <bob_jenkins@burtleburtle.net>
* License: public-domain
* You may use this code any way you wish, private, educational, or commercial.
* It's free.
*
*-------------------------------------------------------------------------------
*/
/*
These are functions for producing 32-bit hashes for hash table lookup.
hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
are externally useful functions. Routines to test the hash are included
if SELF_TEST is defined. You can use this free for any purpose. It's in
the public domain. It has no warranty.
You probably want to use hashlittle(). hashlittle() and hashbig()
hash byte arrays. hashlittle() is is faster than hashbig() on
little-endian machines. Intel and AMD are little-endian machines.
On second thought, you probably want hashlittle2(), which is identical to
hashlittle() except it returns two 32-bit hashes for the price of one.
You could implement hashbig2() if you wanted but I haven't bothered here.
If you want to find a hash of, say, exactly 7 integers, do
a = i1; b = i2; c = i3;
mix(a,b,c);
a += i4; b += i5; c += i6;
mix(a,b,c);
a += i7;
final(a,b,c);
then use c as the hash value. If you have a variable length array of
4-byte integers to hash, use hashword(). If you have a byte array (like
a character string), use hashlittle(). If you have several byte arrays, or
a mix of things, see the comments above hashlittle().
Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
then mix those integers. This is fast (you can do a lot more thorough
mixing with 12*3 instructions on 3 integers than you can with 3 instructions
on 1 byte), but shoehorning those bytes into integers efficiently is messy.
*/
#define SELF_TEST 1
#include <stdio.h> /* defines printf for tests */
#include <time.h> /* defines time_t for timings in the test */
#include "nhash2.h"
//#include <sys/param.h> /* attempt to define endianness */
//#ifdef linux
//# include <endian.h> /* attempt to define endianness */
//#endif
#define HASH_LITTLE_ENDIAN 1
#define hashsize(n) ((uint32_t)1<<(n))
#define hashmask(n) (hashsize(n)-1)
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
/*
-------------------------------------------------------------------------------
mix -- mix 3 32-bit values reversibly.
This is reversible, so any information in (a,b,c) before mix() is
still in (a,b,c) after mix().
If four pairs of (a,b,c) inputs are run through mix(), or through
mix() in reverse, there are at least 32 bits of the output that
are sometimes the same for one pair and different for another pair.
This was tested for:
* pairs that differed by one bit, by two bits, in any combination
of top bits of (a,b,c), or in any combination of bottom bits of
(a,b,c).
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
is commonly produced by subtraction) look like a single 1-bit
difference.
* the base values were pseudorandom, all zero but one bit set, or
all zero plus a counter that starts at zero.
Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
satisfy this are
4 6 8 16 19 4
9 15 3 18 27 15
14 9 3 7 17 3
Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
for "differ" defined as + with a one-bit base and a two-bit delta. I
used http://burtleburtle.net/bob/hash/avalanche.html to choose
the operations, constants, and arrangements of the variables.
This does not achieve avalanche. There are input bits of (a,b,c)
that fail to affect some output bits of (a,b,c), especially of a. The
most thoroughly mixed value is c, but it doesn't really even achieve
avalanche in c.
This allows some parallelism. Read-after-writes are good at doubling
the number of bits affected, so the goal of mixing pulls in the opposite
direction as the goal of parallelism. I did what I could. Rotates
seem to cost as much as shifts on every machine I could lay my hands
on, and rotates are much kinder to the top and bottom bits, so I used
rotates.
-------------------------------------------------------------------------------
*/
#define mix(a,b,c) \
{ \
a -= c; a ^= rot(c, 4); c += b; \
b -= a; b ^= rot(a, 6); a += c; \
c -= b; c ^= rot(b, 8); b += a; \
a -= c; a ^= rot(c,16); c += b; \
b -= a; b ^= rot(a,19); a += c; \
c -= b; c ^= rot(b, 4); b += a; \
}
/*
-------------------------------------------------------------------------------
final -- final mixing of 3 32-bit values (a,b,c) into c
Pairs of (a,b,c) values differing in only a few bits will usually
produce values of c that look totally different. This was tested for
* pairs that differed by one bit, by two bits, in any combination
of top bits of (a,b,c), or in any combination of bottom bits of
(a,b,c).
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
is commonly produced by subtraction) look like a single 1-bit
difference.
* the base values were pseudorandom, all zero but one bit set, or
all zero plus a counter that starts at zero.
These constants passed:
14 11 25 16 4 14 24
12 14 25 16 4 14 24
and these came close:
4 8 15 26 3 22 24
10 8 15 26 3 22 24
11 8 15 26 3 22 24
-------------------------------------------------------------------------------
*/
#define final(a,b,c) \
{ \
c ^= b; c -= rot(b,14); \
a ^= c; a -= rot(c,11); \
b ^= a; b -= rot(a,25); \
c ^= b; c -= rot(b,16); \
a ^= c; a -= rot(c,4); \
b ^= a; b -= rot(a,14); \
c ^= b; c -= rot(b,24); \
}
/*
-------------------------------------------------------------------------------
hashlittle() -- hash a variable-length key into a 32-bit value
k : the key (the unaligned variable-length array of bytes)
length : the length of the key, counting by bytes
initval : can be any 4-byte value
Returns a 32-bit value. Every bit of the key affects every bit of
the return value. Two keys differing by one or two bits will have
totally different hash values.
The best hash table sizes are powers of 2. There is no need to do
mod a prime (mod is sooo slow!). If you need less than 32 bits,
use a bitmask. For example, if you need only 10 bits, do
h = (h & hashmask(10));
In which case, the hash table should have hashsize(10) elements.
If you are hashing n strings (uint8_t **)k, do it like this:
for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
code any way you wish, private, educational, or commercial. It's free.
Use for hash table lookup, or anything where one collision in 2^^32 is
acceptable. Do NOT use for cryptographic purposes.
-------------------------------------------------------------------------------
*/
uint32_t nhash2( const void *key, uint64_t length, uint32_t initval)
//int nhash( const char *key, int length, int initval)
{
uint32_t a,b,c; /* internal state */
union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
/* Set up the internal state */
a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
u.ptr = key;
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
while (length > 12)
{
a += k[0];
b += k[1];
c += k[2];
mix(a,b,c);
length -= 12;
k += 3;
}
// printf("AAA %x %d %x %d\n",c,c,c&32767,c&32767);
/*----------------------------- handle the last (probably partial) block */
/*
* "k[2]&0xffffff" actually reads beyond the end of the string, but
* then masks off the part it's not allowed to read. Because the
* string is aligned, the masked-off tail is in the same word as the
* rest of the string. Every machine with memory protection I've seen
* does it on word boundaries, so is OK with this. But VALGRIND will
* still catch it and complain. The masking trick does make the hash
* noticably faster for short strings (like English words).
*/
#ifndef VALGRIND
switch(length)
{
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
case 8 : b+=k[1]; a+=k[0]; break;
case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
case 6 : b+=k[1]&0xffff; a+=k[0]; break;
case 5 : b+=k[1]&0xff; a+=k[0]; break;
case 4 : a+=k[0]; break;
case 3 : a+=k[0]&0xffffff; break;
case 2 : a+=k[0]&0xffff; break;
case 1 : a+=k[0]&0xff; break;
case 0 : return c; /* zero length strings require no mixing */
}
#else /* make valgrind happy */
k8 = (const uint8_t *)k;
switch(length)
{
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
case 9 : c+=k8[8]; /* fall through */
case 8 : b+=k[1]; a+=k[0]; break;
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
case 5 : b+=k8[4]; /* fall through */
case 4 : a+=k[0]; break;
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
case 1 : a+=k8[0]; break;
case 0 : return c;
}
#endif /* !valgrind */
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
const uint8_t *k8;
/*--------------- all but last block: aligned reads and different mixing */
while (length > 12)
{
a += k[0] + (((uint32_t)k[1])<<16);
b += k[2] + (((uint32_t)k[3])<<16);
c += k[4] + (((uint32_t)k[5])<<16);
mix(a,b,c);
length -= 12;
k += 6;
}
/*----------------------------- handle the last (probably partial) block */
k8 = (const uint8_t *)k;
switch(length)
{
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
case 10: c+=k[4];
b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 9 : c+=k8[8]; /* fall through */
case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
case 6 : b+=k[2];
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 5 : b+=k8[4]; /* fall through */
case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
case 2 : a+=k[0];
break;
case 1 : a+=k8[0];
break;
case 0 : return c; /* zero length requires no mixing */
}
} else { /* need to read the key one byte at a time */
const uint8_t *k = (const uint8_t *)key;
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
while (length > 12)
{
a += k[0];
a += ((uint32_t)k[1])<<8;
a += ((uint32_t)k[2])<<16;
a += ((uint32_t)k[3])<<24;
b += k[4];
b += ((uint32_t)k[5])<<8;
b += ((uint32_t)k[6])<<16;
b += ((uint32_t)k[7])<<24;
c += k[8];
c += ((uint32_t)k[9])<<8;
c += ((uint32_t)k[10])<<16;
c += ((uint32_t)k[11])<<24;
mix(a,b,c);
length -= 12;
k += 12;
}
/*-------------------------------- last block: affect all 32 bits of (c) */
switch(length) /* all the case statements fall through */
{
case 12: c+=((uint32_t)k[11])<<24;
/* fall through */
case 11: c+=((uint32_t)k[10])<<16;
/* fall through */
case 10: c+=((uint32_t)k[9])<<8;
/* fall through */
case 9 : c+=k[8];
/* fall through */
case 8 : b+=((uint32_t)k[7])<<24;
/* fall through */
case 7 : b+=((uint32_t)k[6])<<16;
/* fall through */
case 6 : b+=((uint32_t)k[5])<<8;
/* fall through */
case 5 : b+=k[4];
/* fall through */
case 4 : a+=((uint32_t)k[3])<<24;
/* fall through */
case 3 : a+=((uint32_t)k[2])<<16;
/* fall through */
case 2 : a+=((uint32_t)k[1])<<8;
/* fall through */
case 1 : a+=k[0];
break;
case 0 : return c;
}
}
final(a,b,c);
return c;
}

22
lib/superfox/qpc/nhash2.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef NHASH_H_
#define NHASH_H_
#ifdef Win32
#include "win_stdint.h" /* defines uint32_t etc */
#else
#include <stddef.h>
#include <stdint.h> /* defines uint32_t etc */
#endif
#ifdef __cplusplus
extern "C" {
#endif
uint32_t nhash( const void * key, uint64_t length, uint32_t initval);
//int nhash(const char *key, int length, int initval);
#ifdef __cplusplus
}
#endif
#endif

329
lib/superfox/qpc/np_qpc.c Normal file
View File

@ -0,0 +1,329 @@
// ------------------------------------------------------------------------------
// np_qpc.h
// Q-Ary Polar Codes encoding/decoding functions
//
// (c) 2024 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
//
// This source 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.
// This file 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 this source distribution.
// If not, see <http://www.gnu.org/licenses/>.
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include "dbgprintf.h"
#include "qpc_fwht.h"
#include "np_qpc.h"
// static constant / functions
static const float knorm = 1.0f / QPC_Q;
static float* pdf_conv(float *dst, float* pdf1, float* pdf2)
{
// convolution between two pdf
float fwht_pdf1[QPC_Q];
float fwht_pdf2[QPC_Q];
int k;
qpc_fwht(fwht_pdf1, pdf1);
qpc_fwht(fwht_pdf2, pdf2);
for (k = 0; k < QPC_Q; k++)
fwht_pdf1[k] *= fwht_pdf2[k];
qpc_fwht(dst, fwht_pdf1);
for (k = 0; k < QPC_Q; k++)
dst[k] *= knorm;
return dst;
}
static void pdfarray_conv(float* dstarray, float* pdf1array, float* pdf2array, int numrows)
{
int k;
// convolutions between rows of pdfs
for (k = 0; k < numrows; k++) {
pdf_conv(dstarray, pdf1array, pdf2array);
dstarray += QPC_Q;
pdf1array += QPC_Q;
pdf2array += QPC_Q;
}
}
static float _pdfuniform[QPC_Q];
static const float* _pdf_uniform1();
static const float* _pdf_uniform0();
typedef const float*(*ptr_pdfuniform)(void);
static ptr_pdfuniform _ptr_pdf_uniform = _pdf_uniform0;
static const float* _pdf_uniform1()
{
return _pdfuniform;
};
static const float* _pdf_uniform0()
{
// compute uniform pdf once for all
int k;
for (k = 0; k < QPC_Q; k++)
_pdfuniform[k] = knorm;
// next call to _qpc_pdfuniform
// will be handled directly by _pqc_pdfuniform1
_ptr_pdf_uniform = _pdf_uniform1;
return _pdfuniform;
};
static const float* pdf_uniform()
{
return _ptr_pdf_uniform();
}
static float * pdf_mul(float *dst, float* pdf1, float* pdf2)
{
int k;
float v;
float norm = 0;
for (k = 0; k < QPC_Q; k++) {
v = pdf1[k] * pdf2[k];
dst[k] = v;
norm += v;
}
// if norm of the result is not positive
// return in dst a uniform distribution
if (norm <= 0)
memcpy(dst, pdf_uniform(), QPC_Q * sizeof(float));
else {
norm = 1.0f / norm;
for (k = 0; k < QPC_Q; k++)
dst[k] = dst[k] * norm;
}
return dst;
}
static void pdfarray_mul(float* dstarray, float* pdf1array, float* pdf2array, int numrows)
{
int k;
// products between rows of pdfs
for (k = 0; k < numrows; k++) {
pdf_mul(dstarray, pdf1array, pdf2array);
dstarray += QPC_Q;
pdf1array += QPC_Q;
pdf2array += QPC_Q;
}
}
static float* pdf_convhard(float* dst, const float* pdf, unsigned char hd)
{
// convolution between a pdf and a hard-decision feedback
int k;
for (k=0;k<QPC_Q;k++)
dst[k] = pdf[k^hd];
return dst;
}
static void pdfarray_convhard(float* dstarray, const float* pdfarray, const unsigned char *hdarray, int numrows)
{
int k;
// hard convolutions between rows
for (k = 0; k < numrows; k++) {
pdf_convhard(dstarray, pdfarray, hdarray[k]);
dstarray += QPC_Q;
pdfarray += QPC_Q;
}
}
static unsigned char pdf_max(const float* pdf)
{
int k;
unsigned char imax = 0;
float pdfmax = pdf[0];
for (k=1;k<QPC_Q;k++)
if (pdf[k] > pdfmax) {
pdfmax = pdf[k];
imax = k;
}
return imax;
}
// local stack functions ---------------------------------------
static float _qpc_stack[QPC_N * QPC_Q * 2];
static float* _qpc_stack_base = _qpc_stack;
static float* _qpc_stack_push(int numfloats)
{
float* addr = _qpc_stack_base;
_qpc_stack_base += numfloats;
return addr;
}
static void _qpc_stack_pop(int numfloats)
{
_qpc_stack_base -= numfloats;
}
// qpc encoder function (internal use) ----------------------------------------------------------
unsigned char* _qpc_encode(unsigned char* y, unsigned char* x)
{
// Non recursive polar encoder
// Same architecture of a fast fourier transform
// in which the fft butteflies are replaced by the polar transform
// butterflies
int k, j, m;
int groups;
int bfypergroup;
int stepbfy;
int stepgroup;
int basegroup;
int basebfy;
memcpy(y, x, QPC_N);
for (k = 0; k < QPC_LOG2N; k++) {
groups = 1 << (QPC_LOG2N - 1 - k);
stepbfy = bfypergroup = 1 << k;
stepgroup = stepbfy << 1;
basegroup = 0;
for (j = 0; j < groups; j++) {
basebfy = basegroup;
for (m = 0; m < bfypergroup; m++) {
// polar transform
y[basebfy + stepbfy] = y[basebfy + stepbfy] ^ y[basebfy];
basebfy = basebfy + 1;
}
basegroup = basegroup + stepgroup;
}
}
return y;
}
// qpc polar decoder (internal use )--------------------------------------------------
void _qpc_decode(unsigned char* xdec, unsigned char* ydec, const float* py, const unsigned char* f, const unsigned char* fsize, const int numrows)
{
if (numrows == 1) {
if (fsize[0] == 0) {
// dbgprintf_vector_float("py", py, QPC_Q);
// frozen symbol
xdec[0] = pdf_max(py);
ydec[0] = f[0];
}
else {
// fsize = 1 => information symbol
xdec[0] = pdf_max(py);
ydec[0] = xdec[0];
}
}
else {
int k;
int nextrows = numrows >> 1;
int size = nextrows << QPC_LOG2Q;
// upper block variables
unsigned char* xdech = xdec + nextrows;
unsigned char* ydech = ydec + nextrows;
const unsigned char* fh = f + nextrows;
const unsigned char* fsizeh = fsize + nextrows;
// Step 1.
// stack and init variables used in the recursion
float* pyl = _qpc_stack_push(size);
memcpy(pyl, py, size * sizeof(float));
float* pyh = _qpc_stack_push(size);
memcpy(pyh, py + size, size * sizeof(float));
// Step 2. Recursion on upper block
// Forward pdf convolutions for the upper block
// (place in the lower part of py the convolution of lower and higher blocks)
// float* pyh = py + size;
// pdfarray_conv(py, pyl, pyh, nextrows); // convolution overwriting the lower block of py which is not needed
pdfarray_conv(pyh, pyl, pyh, nextrows);
_qpc_decode(xdech, ydech, pyh, fh, fsizeh, nextrows);
// Step 3. compute pdfs in the lower block
pdfarray_convhard(pyh, py+size, ydech,nextrows); // dst ptr must be different form src ptr
pdfarray_mul(pyl, pyl, pyh, nextrows);
// we don't need pyh anymore
_qpc_stack_pop(size);
// Step 4. Recursion on the lower block
_qpc_decode(xdec, ydec, pyl, f, fsize, nextrows);
// we don't need pyl anymore
_qpc_stack_pop(size);
// Step 5. Update backward results
// xdec is already ok, we need just to update ydech
for (k = 0; k < nextrows; k++)
ydech[k] = ydech[k] ^ ydec[k];
}
}
// Public encoding/decoding functions ------------------------------------------------
void qpc_encode(unsigned char* y, const unsigned char* x)
{
// map information symbols
int kk, pos;
for (kk = 0; kk < QPC_K; kk++) {
pos = qpccode.xpos[kk];
qpccode.f[pos] = x[kk];
}
// do polar encoding
_qpc_encode(y, qpccode.f);
}
void qpc_decode(unsigned char* xdec, unsigned char* ydec, float* py)
{
int k;
unsigned char x[QPC_N];
// set the first py row with know frozen (punctured) symbol
if (qpccode.np < qpccode.n) {
// assume that we punctured only the first output symbol
memset(py, 0, QPC_Q * sizeof(float));
py[qpccode.f[0]] = 1.0f;
}
// decode
_qpc_decode(x, ydec, py, qpccode.f, qpccode.fsize, QPC_N);
// demap information symbols
for (k = 0; k < QPC_K; k++)
xdec[k] = x[qpccode.xpos[k]];
}

62
lib/superfox/qpc/np_qpc.h Normal file
View File

@ -0,0 +1,62 @@
// ------------------------------------------------------------------------------
// np_qpc.h
// Q-Ary Polar Codes encoding/decoding functions
//
// (c) 2024 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
//
// This source 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.
// This file 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 this source distribution.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef _np_qpc_h_
#define _np_qpc_h_
#define QPC_LOG2N 7 // log2(codeword length) (not punctured)
#define QPC_N (1<<QPC_LOG2N) // codeword length (not punctured)
#define QPC_LOG2Q 7 // bits per symbol
#define QPC_Q (1<<QPC_LOG2Q) // alphabet size
#define QPC_K 50 // number of information symbols
typedef struct {
int n; // codeword length (unpunctured)
int np; // codeword length (punctured)
int k; // number of information symbols
int q; // alphabet size
int xpos[QPC_N]; // info symbols mapping/demapping tables
unsigned char f[QPC_N]; // frozen symbols values
unsigned char fsize[QPC_N]; // frozen symbol flag (fsize==0 => frozen)
} qpccode_ds;
#ifdef __cplusplus
extern "C"
{
#endif
void qpc_encode(unsigned char* y, const unsigned char* x);
void qpc_decode(unsigned char* xdec, unsigned char* ydec, float* py);
unsigned char* _qpc_encode(unsigned char* y, unsigned char* x);
void _qpc_decode(unsigned char* xdec, unsigned char* ydec,
const float* py, const unsigned char* f, const unsigned char* fsize,
const int numrows);
extern qpccode_ds qpccode;
#ifdef __cplusplus
}
#endif
#endif // _np_qpc_h_

135
lib/superfox/qpc/np_rnd.c Normal file
View File

@ -0,0 +1,135 @@
// ------------------------------------------------------------------------------
// np_rnd.c
// Functions to generate random numbers with uniform/gaussian probability distributions
//
// (c) 2024 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
//
// This source 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.
// This file 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 this source distribution.
// If not, see <http://www.gnu.org/licenses/>.
#include "np_rnd.h"
#if _WIN32 // note the underscore: without it, it's not msdn official!
// Windows (x64 and x86)
#include <windows.h> // required only for GetTickCount(...)
#define K_RAND_MAX UINT_MAX
#elif _SVID_SOURCE || _XOPEN_SOURCE || __unix__ || (defined (__APPLE__) && defined(__MACH__)) /* POSIX or Unix or Apple */
#include <stdlib.h>
#define rand_s(x) (*x)=(unsigned int)lrand48() // returns unsigned integers in the range 0..0x7FFFFFFF
#define K_RAND_MAX 0x7FFFFFFF // that's the max number
// generated by lrand48
#else
#error "No good quality PRNG found"
#endif
void np_normrnd_real(float *dst, int nitems, float mean, float stdev)
{
// for odd or even nitems
unsigned int r;
float phi=0, u=0;
int set = 0;
float scalephi = (M_2PI / (1.0f + K_RAND_MAX));
float scaleu = (1.0f / (1.0f + K_RAND_MAX));
while (nitems--)
if (set==1) {
// generate a second normally distributed number
*dst++ = sinf(phi)*u*stdev+mean;
set = 0;
}
else {
// generate a uniform distributed phase phi in the range [0,2*PI)
rand_s((unsigned int*)&r); phi = scalephi * r;
// generate a uniform distributed random number u in the range (0,1]
rand_s((unsigned int*)&r); u = scaleu * (1.0f + r);
// generate normally distributed number
u = (float)sqrt(-2.0f * log(u));
*dst++ = cosf(phi) * u * stdev + mean;
set=1;
}
}
void np_normrnd_cpx(float* dst, int nitems, float mean, float stdev)
{
// as qpc_normrnd_real, but generates always nitems complex numbers (2*nitems reals)
unsigned int r;
float phi = 0, u = 0;
// JHT int set = 0;
float scalephi = (M_2PI / (1.0f + K_RAND_MAX));
float scaleu = (1.0f / (1.0f + K_RAND_MAX));
while (nitems--) {
// generate a uniform distributed phase phi in the range [0,2*PI)
rand_s((unsigned int*)&r); phi = scalephi * r;
// generate a uniform distributed random number u in the range (0,1]
rand_s((unsigned int*)&r); u = scaleu * (1.0f + r);
// generate normally distributed real and imaginary parts
u = (float)sqrt(-2.0f * log(u));
*dst++ = cosf(phi) * u * stdev + mean;
*dst++ = sinf(phi) * u * stdev + mean;
}
}
void np_unidrnd(unsigned int* dst, int nitems, unsigned int nsetsize)
{
// generate uniform unsigned int random numbers in the range [0..nsetsize)
unsigned int r;
float u;
float scaleu = (1.0f / (1.0f + K_RAND_MAX));
while (nitems--) {
rand_s((unsigned int*)&r); u = scaleu * nsetsize * r;
*dst++ = (unsigned int)floorf(u);
}
}
void np_unidrnd_uc(unsigned char* dst, int nitems, unsigned char nsetsize)
{
// generate uniform unsigned char random numbers in the range [0..nsetsize)
unsigned int r;
float u;
float scaleu = (1.0f / (1.0f + K_RAND_MAX));
while (nitems--) {
rand_s((unsigned int*)&r); u = scaleu * nsetsize * r;
*dst++ = (unsigned char)floorf(u);
}
}
void np_unifrnd(float* dst, int nitems, float fmax)
{
// generate uniform float random numbers in the range [0..fmax)
unsigned int r;
float u;
float scaleu = (1.0f / (1.0f + K_RAND_MAX));
while (nitems--) {
rand_s((unsigned int*)&r); u = scaleu * fmax * r;
*dst++ = u;
}
}

59
lib/superfox/qpc/np_rnd.h Normal file
View File

@ -0,0 +1,59 @@
// ------------------------------------------------------------------------------
// np_rnd.h
// Functions to generate random numbers with uniform/gaussian probability distributions
//
// (c) 2024 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
//
// This source 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.
// This file 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 this source distribution.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef _np_rnd_h_
#define _np_rnd_h_
#define _CRT_RAND_S
#include <stdlib.h>
#define _USE_MATH_DEFINES
#include <math.h>
#define M_2PI (2.0f*(float)M_PI)
#ifdef __cplusplus
extern "C" {
#endif
// generate a random array of real numbers with a gaussian distribution of given mean and stdev
void np_normrnd_real(float *dst, int nitems, float mean, float stdev);
// generate a random array of nitems complex numbers with a gaussian distribution of given mean and stdev
void np_normrnd_cpx(float* dst, int nitems, float mean, float stdev);
// generate a random array of nitems unsigned int numbers with uniform distribution
// in the range [0 .. nsetsize)
void np_unidrnd(unsigned int* dst, int nitems, unsigned int nsetsize);
// generate a random array of nitems unsigned chars numbers with uniform distribution
// in the range [0 .. nsetsize)
void np_unidrnd_uc(unsigned char* dst, int nitems, unsigned char nsetsize);
// generate a random array of nitems float numbers with uniform distribution
// in the range [0 .. fmax)
void np_unifrnd(float* dst, int nitems, float fmax);
#ifdef __cplusplus
}
#endif
#endif // _np_rnd_h_

233
lib/superfox/qpc/qpc_fwht.c Normal file
View File

@ -0,0 +1,233 @@
// ------------------------------------------------------------------------------
// qpc_fwht.c
// Fast Walsh-Hadamard Transforms for q-ary polar codes
//
// (c) 2024 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
//
// This source 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.
// This file 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 this source distribution.
// If not, see <http://www.gnu.org/licenses/>.
#include "qpc_fwht.h"
static void _qpc_sumdiff8_16(float* y, float* t)
{
y[0] = t[0] + t[16];
y[16] = t[0] - t[16];
y[1] = t[1] + t[17];
y[17] = t[1] - t[17];
y[2] = t[2] + t[18];
y[18] = t[2] - t[18];
y[3] = t[3] + t[19];
y[19] = t[3] - t[19];
y[4] = t[4] + t[20];
y[20] = t[4] - t[20];
y[5] = t[5] + t[21];
y[21] = t[5] - t[21];
y[6] = t[6] + t[22];
y[22] = t[6] - t[22];
y[7] = t[7] + t[23];
y[23] = t[7] - t[23];
}
static void _qpc_sumdiff8_32(float* y, float* t)
{
y[0] = t[0] + t[32];
y[32] = t[0] - t[32];
y[1] = t[1] + t[33];
y[33] = t[1] - t[33];
y[2] = t[2] + t[34];
y[34] = t[2] - t[34];
y[3] = t[3] + t[35];
y[35] = t[3] - t[35];
y[4] = t[4] + t[36];
y[36] = t[4] - t[36];
y[5] = t[5] + t[37];
y[37] = t[5] - t[37];
y[6] = t[6] + t[38];
y[38] = t[6] - t[38];
y[7] = t[7] + t[39];
y[39] = t[7] - t[39];
}
static void _qpc_sumdiff8_64(float* y, float* t)
{
y[0] = t[0] + t[64];
y[64] = t[0] - t[64];
y[1] = t[1] + t[65];
y[65] = t[1] - t[65];
y[2] = t[2] + t[66];
y[66] = t[2] - t[66];
y[3] = t[3] + t[67];
y[67] = t[3] - t[67];
y[4] = t[4] + t[68];
y[68] = t[4] - t[68];
y[5] = t[5] + t[69];
y[69] = t[5] - t[69];
y[6] = t[6] + t[70];
y[70] = t[6] - t[70];
y[7] = t[7] + t[71];
y[71] = t[7] - t[71];
}
float* qpc_fwht8(float* y, float* x)
{
float t[8];
// first stage
y[0] = x[0] + x[1];
y[1] = x[0] - x[1];
y[2] = x[2] + x[3];
y[3] = x[2] - x[3];
y[4] = x[4] + x[5];
y[5] = x[4] - x[5];
y[6] = x[6] + x[7];
y[7] = x[6] - x[7];
// second stage
t[0] = y[0] + y[2];
t[2] = y[0] - y[2];
t[1] = y[1] + y[3];
t[3] = y[1] - y[3];
t[4] = y[4] + y[6];
t[6] = y[4] - y[6];
t[5] = y[5] + y[7];
t[7] = y[5] - y[7];
// third stage
y[0] = t[0] + t[4];
y[4] = t[0] - t[4];
y[1] = t[1] + t[5];
y[5] = t[1] - t[5];
y[2] = t[2] + t[6];
y[6] = t[2] - t[6];
y[3] = t[3] + t[7];
y[7] = t[3] - t[7];
return y;
}
float* qpc_fwht16(float* y, float* x)
{
float t[16];
qpc_fwht8(t, x);
qpc_fwht8(t + 8, x + 8);
y[0] = t[0] + t[8];
y[8] = t[0] - t[8];
y[1] = t[1] + t[9];
y[9] = t[1] - t[9];
y[2] = t[2] + t[10];
y[10] = t[2] - t[10];
y[3] = t[3] + t[11];
y[11] = t[3] - t[11];
y[4] = t[4] + t[12];
y[12] = t[4] - t[12];
y[5] = t[5] + t[13];
y[13] = t[5] - t[13];
y[6] = t[6] + t[14];
y[14] = t[6] - t[14];
y[7] = t[7] + t[15];
y[15] = t[7] - t[15];
return y;
}
float* qpc_fwht32(float* y, float* x)
{
float t[32];
qpc_fwht16(t, x);
qpc_fwht16(t + 16, x + 16);
_qpc_sumdiff8_16(y, t);
_qpc_sumdiff8_16(y + 8, t + 8);
return y;
}
float* qpc_fwht64(float* y, float* x)
{
float t[64];
qpc_fwht32(t, x);
qpc_fwht32(t + 32, x + 32);
_qpc_sumdiff8_32(y, t);
_qpc_sumdiff8_32(y + 8, t + 8);
_qpc_sumdiff8_32(y + 16, t + 16);
_qpc_sumdiff8_32(y + 24, t + 24);
return y;
}
float* qpc_fwht128(float* y, float* x)
{
float t[128];
qpc_fwht64(t, x);
qpc_fwht64(t + 64, x + 64);
_qpc_sumdiff8_64(y, t);
_qpc_sumdiff8_64(y + 8, t + 8);
_qpc_sumdiff8_64(y + 16, t + 16);
_qpc_sumdiff8_64(y + 24, t + 24);
_qpc_sumdiff8_64(y + 32, t + 32);
_qpc_sumdiff8_64(y + 40, t + 40);
_qpc_sumdiff8_64(y + 48, t + 48);
_qpc_sumdiff8_64(y + 56, t + 56);
return y;
}
// functions over pdfs used by the decoder -----------------------------------------------------------

View File

@ -0,0 +1,57 @@
// ------------------------------------------------------------------------------
// qpc_fwht.h
// Fast Walsh-Hadamard Transforms for q-ary polar codes
//
// (c) 2024 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
//
// This source 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.
// This file 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 this source distribution.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef _qpc_fwht_h_
#define _qpc_fwht_h_
#include "np_qpc.h"
#if QPC_LOG2Q==7
#define qpc_fwht(a,b) qpc_fwht128(a,b)
#endif
#if QPC_LOG2Q==6
#define qpc_fwht(a,b) qpc_fwht64(a,b)
#endif
#if QPC_LOG2Q==5
#define qpc_fwht(a,b) qpc_fwht32(a,b)
#endif
#if QPC_LOG2Q==4
#define qpc_fwht(a,b) qpc_fwht16(a,b)
#endif
// Note that it makes no sense to use fast convolutions
// for transforms that are less than 16 symbols in size.
// For such cases direct convolutions are faster.
#if QPC_LOG2Q==3
#define qpc_fwht(a,b) qpc_fwht8(a,b)
#endif
#ifdef __cplusplus
extern "C" {
#endif
float* qpc_fwht8(float* y, float* x);
float* qpc_fwht16(float* y, float* x);
float* qpc_fwht32(float* y, float* x);
float* qpc_fwht64(float* y, float* x);
float* qpc_fwht128(float* y, float* x);
#ifdef __cplusplus
}
#endif
#endif // _qpc_fwht_h_

View File

@ -0,0 +1,198 @@
// ------------------------------------------------------------------------------
// qpc_main.cpp
//
// Test the WER performance of the QPC (127,50) Q=128 code
//
// (c) 2024 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include "dbgprintf.h"
#include "qpc_fwht.h"
#include "np_qpc.h"
// for random numbers generation and NCFSK Rayleigh channel simulation
#include "np_rnd.h"
void qpc_channel(float* yout, unsigned char* y, float EsNo)
{
// compute channel outputs amplitudes
int k;
// generate noise samples -----------------------------------------
float sigman = 1.0f / sqrtf(2.0f);
np_normrnd_cpx(yout, QPC_N * QPC_Q, 0, sigman);
// dbgprintf_rows_float("yout", yout, QPC_Q*2, QPC_N);
// generate rayleigh distributed signal amplitudes -----------------
float symamps[QPC_N * 2];
np_normrnd_cpx(symamps, QPC_N, 0, sigman);
// dbgprintf_vector_float("symamps", symamps, QPC_N*2);
// normalize signal amps to unity ----------------------------------
float pwr = 0.0f;
float* psymamps = symamps;
// compute sig power
for (k = 0; k < QPC_N; k++) {
pwr += psymamps[0] * psymamps[0] + psymamps[1] * psymamps[1];
psymamps += 2;
}
pwr = pwr / QPC_N;
// normalize to avg EsNo
float norm = sqrtf(EsNo/pwr);
psymamps = symamps;
for (k = 0; k < QPC_N; k++) {
psymamps[0] *= norm;
psymamps[1] *= norm;
psymamps += 2;
}
// dbgprintf_vector_float("symamps norm", symamps, QPC_N * 2);
// add signal amplitudes to noise -----------------------------------
float *pyout = yout;
psymamps= symamps;
for (k = 0; k < QPC_N; k++) {
pyout[y[k]<<1] += psymamps[0];
pyout[(y[k]<<1)+1] += psymamps[1];
pyout += (QPC_Q*2);
psymamps += 2;
}
// dbgprintf_rows_float("s+n out", yout, QPC_Q * 2, QPC_N);
return;
}
void qpc_likelihoods(float* py, float* yout, float EsNo, float No)
{
// compute symbols likelihoods
// (rayleigh channel)
int k, j;
float norm = EsNo / (EsNo + 1) / No;
// compute likelihoods from energies ----------------------------
float* pybase;
float* ppyout = yout;
float normpwr;
float normpwrmax;
float pynorm;
for (k = 0; k < QPC_N; k++) {
// compute loglikelihoods and largest one from energies
pybase = py + k * QPC_Q;
normpwrmax = 0.0f;
for (j = 0; j < QPC_Q; j++) {
normpwr = norm * (ppyout[0] * ppyout[0] + ppyout[1] * ppyout[1]);
pybase[j] = normpwr;
if (normpwr > normpwrmax)
normpwrmax = normpwr;
ppyout += 2;
}
// subtract largest exponent
pynorm = 0.0f;
for (j = 0; j < QPC_Q; j++) {
pybase[j] = expf(pybase[j] - normpwrmax);
pynorm += pybase[j];
}
// normalize to probabilities
pynorm = 1.0f / pynorm;
for (j = 0; j < QPC_Q; j++)
pybase[j] = pybase[j] * pynorm;
}
return;
}
int main()
{
int k;
int NT = 20000; // number of transmissions to simultate
float EbNodB = 3.7f; // Eb/No to simulate
int kc = qpccode.k;
int np = qpccode.np;
float Rc = 1.0f * kc / np;
float Tm = 10.0f; // message duration
float Rb = 1.0f / Tm * (QPC_K - 2) * QPC_LOG2Q; // Bit rate assuming two symbols for CRC
float EsNodB = EbNodB + 10.0f * log10f(Rc * QPC_LOG2Q);
static unsigned char xin[QPC_K]; // input information symbols
static unsigned char xdec[QPC_K]; // decoded information symbols
static unsigned char y[QPC_N]; // encoded codeword
static unsigned char ydec[QPC_N]; // decoded codeword
static float yout[QPC_N * QPC_Q * 2]; // channel complex amplitutes output
static float py[QPC_N * QPC_Q]; // channel output probabilities
float EsNo = powf(10.0f, EsNodB / 10.0f);
float No = 1.0f;
//JHT static float we;
static float wer;
int kk;
printf("QPC Word Error Rate Test\n");
printf("Polar Code (%d,%d) Q=%d for the Rayleigh channel\n", qpccode.np, qpccode.k, qpccode.q);
printf("Eb/No = %.1f dB\n", EbNodB);
printf("Es/No = %.1f dB\n", EsNodB);
printf("Tmsg = %.1f s\n", Tm);
printf("Bit Rate = %.1f bit/s\n", Rb);
printf("SNR(2500) = %.1f dB\n\n", EbNodB + 10.0f * log10f(Rb/ 2500));
for (k = 0; k < NT; k++) {
np_unidrnd_uc(xin, QPC_K, QPC_Q); // generate random information symbols
qpc_encode(y, xin); // encode
// compute channel outputs and probabilities
// Note that if the code is punctured (i.e. N=127)
// the first codeword symbol must not be transmitted over the channel
// Here, in order to avoid useless copies,
// the vector of likelihoods py continues to be a QPC_N*QPC_Q vector of floats.
// The first received symbol likelihoods should start always at offset QPC_Q.
// The first QPC_Q positions of py will be ignored (and set) by the decoder.
qpc_channel(yout, y, EsNo);
// The decoder can't estimate the EsNo easily
// so we assume that in any case it is 5 dB
float EsNoDec = 3.16;
qpc_likelihoods(py, yout, EsNoDec, No);
qpc_decode(xdec, ydec, py); // decode
// count words in errors
for (kk = 0; kk < QPC_K; kk++)
if (xin[kk] ^ xdec[kk]) {
wer += 1;
break;
}
// show the decoder performance every 200 codewords transmissions
if ((k % 200) == 0 && k>0) {
printf("k=%6d/%6d wer = %8.2E\n", k, NT, wer / k);
fflush(stdout);
}
}
// show the final result
printf("k=%6d/%6d wer = %8.2E\n", k, NT, wer / k);
printf("\nAll done.\n");
return 0;
}

View File

@ -0,0 +1,38 @@
module qpc_mod
interface
integer(c_int32_t) function nhash2 (xin, length, initval) bind(C, &
name="nhash2")
use iso_c_binding, only: c_signed_char, c_int64_t, c_int32_t
integer(c_int64_t), intent(in), value :: length
integer(c_signed_char), intent(in) :: xin(length)
integer(c_int32_t), intent(in), value :: initval
end function nhash2
end interface
interface
subroutine qpc_encode(y, xin) bind(C,name="qpc_encode")
use iso_c_binding, only: c_ptr, c_signed_char
integer(c_signed_char), intent(out) :: y(127)
integer(c_signed_char), intent(in) :: xin(50)
end subroutine qpc_encode
end interface
interface
subroutine qpc_channel(yout, y, EsNo) bind(C,name="qpc_channel")
use iso_c_binding, only: c_float_complex, c_float, c_signed_char
complex(c_float_complex), intent(out) :: yout(128,128)
integer(c_signed_char), intent(out) :: y(127)
real(c_float), intent(in), value :: EsNo
end subroutine qpc_channel
end interface
interface
subroutine qpc_decode(xdec, ydec, py) bind(C,name="qpc_decode")
use iso_c_binding, only: c_float, c_signed_char
real(c_float), intent(in) :: py(128,128)
integer(c_signed_char), intent(out) :: ydec(127)
integer(c_signed_char), intent(out) :: xdec(50)
end subroutine qpc_decode
end interface
end module qpc_mod

View File

@ -0,0 +1,60 @@
// ------------------------------------------------------------------------------
// qpc_n127k50q128.c
//
// Defines the parameters of the q-ary polar code (127,50) Q=128
// for WSJT-X
// ------------------------------------------------------------------------------
// (c) 2024 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
//
// WARNING:
// This source is NOT free software and it is licensed only for use with WSJT-X.
// No derived work is authorized to use this code without the written
// permission of his author (Nico Palermo / IV3NWV) who owns all the rights
// of this IP (intellectual property).
//
// This file is distributed ONLY for the purpose of documenting and making of
// public domain the encoding scheme used in WSJT-X so that the transmitted
// messages can be decoded by anybody.
// Anyway this does not imply that one could use the following tables in a
// derived work without an explicit and written authorization from his author.
// Any unauthorized use, as for any intellectual property, is simply
// illegal.
// -------------------------------------------------------------------------------
#include "np_qpc.h"
qpccode_ds qpccode = {
128, //n
127, //np
50, //k
128, //q
{
1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 16, 32, 17, 18, 64, 20,
33, 34, 24, 7, 11, 36, 13, 19, 14, 65, 40, 21, 66, 22, 35, 68,
25, 48, 37, 26, 72, 15, 38, 28, 41, 67, 23, 80, 42, 69, 49, 96,
44, 27, 70, 50, 73, 39, 29, 52, 74, 30, 56, 81, 76, 43, 82, 84,
97, 45, 71, 88, 98, 46, 100, 51, 104, 53, 75, 112, 54, 57, 99, 119,
92, 77, 58, 117, 59, 83, 106, 31, 85, 108, 115, 116, 122, 125, 124, 91,
61, 90, 89, 111, 78, 93, 94, 126, 86, 107, 110, 118, 121, 62, 120, 87,
105, 55, 114, 60, 127, 63, 103, 101, 123, 95, 102, 47, 109, 79, 113, 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, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0,
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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
}
};

115
lib/superfox/qpc/qpc_subs.c Normal file
View File

@ -0,0 +1,115 @@
// ------------------------------------------------------------------------------
// qpc_test.cpp
//
// Test the WER performance of the QPC (127,50) Q=128 code
//
// (c) 2024 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <time.h>
#include "dbgprintf.h"
#include "qpc_fwht.h"
#include "np_qpc.h"
#include "nhash2.h"
// for random numbers generation and NCFSK Rayleigh channel simulation
#include "np_rnd.h"
void qpc_channel(float* yout, unsigned char* y, float EsNo)
{
// compute channel outputs amplitudes
int k;
// generate noise samples -----------------------------------------
float sigman = 1.0f / sqrtf(2.0f);
np_normrnd_cpx(yout, QPC_N * QPC_Q, 0, sigman);
// dbgprintf_rows_float("yout", yout, QPC_Q*2, QPC_N);
// generate rayleigh distributed signal amplitudes -----------------
float symamps[QPC_N * 2];
np_normrnd_cpx(symamps, QPC_N, 0, sigman);
// dbgprintf_vector_float("symamps", symamps, QPC_N*2);
// normalize signal amps to unity ----------------------------------
float pwr = 0.0f;
float* psymamps = symamps;
// compute sig power
for (k = 0; k < QPC_N; k++) {
pwr += psymamps[0] * psymamps[0] + psymamps[1] * psymamps[1];
psymamps += 2;
}
pwr = pwr / QPC_N;
// normalize to avg EsNo
float norm = sqrtf(EsNo/pwr);
psymamps = symamps;
for (k = 0; k < QPC_N; k++) {
psymamps[0] *= norm;
psymamps[1] *= norm;
psymamps += 2;
}
// dbgprintf_vector_float("symamps norm", symamps, QPC_N * 2);
// add signal amplitudes to noise -----------------------------------
float *pyout = yout;
psymamps= symamps;
for (k = 0; k < QPC_N; k++) {
pyout[y[k]<<1] += psymamps[0];
pyout[(y[k]<<1)+1] += psymamps[1];
pyout += (QPC_Q*2);
psymamps += 2;
}
// dbgprintf_rows_float("s+n out", yout, QPC_Q * 2, QPC_N);
return;
}
void qpc_likelihoods(float* py, float* yout, float EsNo, float No)
{
// compute symbols likelihoods
// (rayleigh channel)
int k, j;
float norm = EsNo / (EsNo + 1) / No;
// compute likelihoods from energies ----------------------------
float* pybase;
float* ppyout = yout;
float normpwr;
float normpwrmax;
float pynorm;
for (k = 0; k < QPC_N; k++) {
// compute loglikelihoods and largest one from energies
pybase = py + k * QPC_Q;
normpwrmax = 0.0f;
for (j = 0; j < QPC_Q; j++) {
normpwr = norm * (ppyout[0] * ppyout[0] + ppyout[1] * ppyout[1]);
pybase[j] = normpwr;
if (normpwr > normpwrmax)
normpwrmax = normpwr;
ppyout += 2;
}
// subtract largest exponent
pynorm = 0.0f;
for (j = 0; j < QPC_Q; j++) {
pybase[j] = expf(pybase[j] - normpwrmax);
pynorm += pybase[j];
}
// normalize to probabilities
pynorm = 1.0f / pynorm;
for (j = 0; j < QPC_Q; j++)
pybase[j] = pybase[j] * pynorm;
}
return;
}

View File

@ -0,0 +1,218 @@
subroutine qpc_decode2(c0,fsync,ftol,xdec,ndepth,dth,damp,crc_ok, &
snrsync,fbest,tbest,snr)
use qpc_mod
parameter(NMAX=15*12000,NFT=365,NZ=100)
complex c0(NMAX) !Signal as received
complex c(NMAX) !Signal as received
real py(0:127,0:127) !Probabilities for received synbol values
real py0(0:127,0:127) !Probabilities for strong signal
real pyd(0:127,0:127) !Dithered values for py
real s2(0:127,0:151) !Symbol spectra, including sync
real s3(0:127,0:127) !Synchronized symbol spectra
real No
integer crc_chk,crc_sent
integer*8 n47
integer idf(NZ),idt(NZ)
integer nseed(33)
integer*1 xdec(0:49) !Decoded message
integer*1 ydec(0:127) !Decoded symbols
logical crc_ok
integer maxdither(8)
integer isync(24) !Symbol numbers for sync tones
data isync/1,2,4,7,11,16,22,29,37,39,42,43,45,48,52,57,63,70,78,80,83, &
84,86,89/
data n47/47/,maxdither/20,50,100,200,500,1000,2000,5000/
data nseed/ &
321278106, -658879006, 1239150429, -941466001, -698554454, &
1136210962, 1633585627, 1261915021, -1134191465, -487888229, &
2131958895, -1429290834, -1802468092, 1801346659, 1966248904, &
402671397, -1961400750, -1567227835, 1895670987, -286583128, &
-595933665, -1699285543, 1518291336, 1338407128, 838354404, &
-2081343776, -1449416716, 1236537391, -133197638, 337355509, &
-460640480, 1592689606, 0/
data idf/0, 0, -1, 0, -1, 1, 0, -1, 1, -2, 0, -1, 1, -2, 2, &
0, -1, 1, -2, 2, -3, 0, -1, 1, -2, 2, -3, 3, 0, -1, &
1, -2, 2, -3, 3, -4, 0, -1, 1, -2, 2, -3, 3, -4, 4, &
0, -1, 1, -2, 2, -3, 3, -4, 4, -5, -1, 1, -2, 2, -3, &
3, -4, 4, -5, 1, -2, 2, -3, 3, -4, 4, -5, -2, 2, -3, &
3, -4, 4, -5, 2, -3, 3, -4, 4, -5, -3, 3, -4, 4, -5, &
3, -4, 4, -5, -4, 4, -5, 4, -5, -5/
data idt/0 , -1, 0, 1, -1, 0, -2, 1, -1, 0, 2, -2, 1, -1, 0, &
-3, 2, -2, 1, -1, 0, 3, -3, 2, -2, 1, -1, 0, -4, 3, &
-3, 2, -2, 1, -1, 0, 4, -4, 3, -3, 2, -2, 1, -1, 0, &
-5, 4, -4, 3, -3, 2, -2, 1, -1, 0, -5, 4, -4, 3, -3, &
2, -2, 1, -1, -5, 4, -4, 3, -3, 2, -2, 1, -5, 4, -4, &
3, -3, 2, -2, -5, 4, -4, 3, -3, 2, -5, 4, -4, 3, -3, &
-5, 4, -4, 3, -5, 4, -4, -5, 4, -5/
fsample=12000.0
baud=12000.0/1024.0
nstype=1
n47=47
mask21=2**21 - 1
crc_ok=.false.
call qpc_sync(c0,fsample,isync,fsync,ftol,f2,t2,snrsync)
f00=1500.0 + f2
t00=t2
fbest=f00
tbest=t00
maxd=1
if(ndepth.gt.0) maxd=maxdither(ndepth)
maxft=NZ
if(snrsync.lt.4.0 .or. ndepth.le.0) maxft=1
do idith=1,maxft
if(idith.ge.2) maxd=1
deltaf=idf(idith)*0.5
deltat=idt(idith)*8.0/1024.0
f=f00+deltaf
t=t00+deltat
fshift=1500.0 - (f+baud) !Shift frequencies down by f + 1 bin
call twkfreq2(c0,c,NMAX,fsample,fshift)
a=1.0
b=0.0
do kk=1,4
if(kk.eq.2) b=0.4
if(kk.eq.3) b=0.5
if(kk.eq.4) b=0.6
call sfox_demod(c,1500.0,t,isync,s2,s3) !Compute s2 and s3
if(b.gt.0.0) then
do j=0,127
call smo121a(s3(:,j),128,a,b)
enddo
endif
call pctile(s3,128*128,50,base3)
s3=s3/base3
EsNoDec=3.16
No=1.
py0=s3
call qpc_likelihoods2(py,s3,EsNoDec,No) !For weak signals
call random_seed(put=nseed)
do kkk=1,maxd
if(kkk.eq.1) then
pyd=py0
else
pyd=0.
if(kkk.gt.2) then
call random_number(pyd)
pyd=2.0*(pyd-0.5)
endif
where(py.gt.dth) pyd=0. !Don't perturb large likelihoods
pyd=py*(1.0 + damp*pyd) !Compute dithered likelihood
endif
do j=0,127
ss=sum(pyd(:,j))
if(ss.gt.0.0) then
pyd(:,j)=pyd(:,j)/ss
else
pyd(:,j)=0.0
endif
enddo
call qpc_decode(xdec,ydec,pyd)
xdec=xdec(49:0:-1)
crc_chk=iand(nhash2(xdec,n47,571),mask21) !Compute crc_chk
crc_sent=128*128*xdec(47) + 128*xdec(48) + xdec(49)
crc_ok=crc_chk.eq.crc_sent
if(crc_ok) then
call qpc_snr(s3,ydec,snr)
if(snr.lt.-16.5) crc_ok=.false.
! write(61,3061) idith,kk,kkk,idf(idith),idt(idith),a,b
!3061 format(5i5,2f8.3)
return
endif
enddo !kk: dither of smoothing weights
enddo !kkk: dither of probabilities
enddo !idith: dither of frequency and time
return
end subroutine qpc_decode2
subroutine smo121a(x,nz,a,b)
real x(nz)
fac=1.0/(a+2*b)
x0=x(1)
do i=2,nz-1
x1=x(i)
x(i)=fac*(a*x(i) + b*(x0+x(i+1)))
x0=x1
enddo
return
end subroutine smo121a
subroutine remove_tone(c0,npts)
parameter (NFILT=8000)
complex c0(npts)
complex cwindow(15*12000)
complex cref(npts)
complex cfilt(npts)
real window(-NFILT/2:NFILT/2)
! real endcorrection(NFILT/2+1)
real s(npts/4)
integer ipk(1)
logical first
data first/.true./
save cwindow,first,pi
if(first) then
pi=4.0*atan(1.0)
fac=1.0/float(npts)
sumw=0.0
do j=-NFILT/2,NFILT/2
window(j)=cos(pi*j/NFILT)**2
sumw=sumw+window(j)
enddo
cwindow=0.
cwindow(1:NFILT+1)=window/sumw
cwindow=cshift(cwindow,NFILT/2+1)
call four2a(cwindow,npts,1,-1,1)
cwindow=cwindow*fac ! frequency domain smoothing filter
first=.false.
endif
fsample=12000.0
df=fsample/npts
fac=1.0/npts
cfilt=fac*c0
call four2a(cfilt,npts,1,-1,1) ! fourier transform of input data
iz=npts/4
do i=1,iz
s(i)=real(cfilt(i))**2 + aimag(cfilt(i))**2
enddo
ia=nint(700.0/df)
ib=nint(800.0/df)
ipk=maxloc(s(ia:ib))
i0=ipk(1) + ia - 1
f2=df*i0
write(*,*) 'remove_tone - frequency: ',f2
dt=1.0/fsample
do i=1, npts
arg=2*pi*f2*i*dt
cref(i)=cmplx(cos(arg),sin(arg))
enddo
cfilt=c0*conjg(cref) ! baseband to be filtered
call four2a(cfilt,npts,1,-1,1)
cfilt=cfilt*cwindow
call four2a(cfilt,npts,1,1,1)
nframe=50*3456
do i=1,nframe
cref(i)=cfilt(i)*cref(i)
c0(i)=c0(i)-cref(i)
enddo
return
end subroutine remove_tone

View File

@ -0,0 +1,28 @@
subroutine qpc_likelihoods2(py,s3,EsNo,No)
integer QQ,QN
parameter(QQ=128,QN=128)
real py(0:QQ-1,0:QN-1)
real s3(0:QQ-1,0:QN-1)
real No,norm,normpwr,normpwrmax
norm=(EsNo/(EsNo+1.0))/No
! Compute likelihoods for symbol values, from the symbol power spectra
do k=0,QN-1
normpwrmax=0.
do j=0,QQ-1
normpwr=norm*s3(j,k)
py(j,k)=normpwr
normpwrmax=max(normpwr,normpwrmax)
enddo
pynorm=0.
do j=0,QQ-1
py(j,k)=exp(py(j,k)-normpwrmax)
pynorm=pynorm + py(j,k)
enddo
py(0:QQ-1,k)=py(0:QQ-1,k)/pynorm !Normalize to probabilities
enddo
return
end subroutine qpc_likelihoods2

17
lib/superfox/qpc_snr.f90 Normal file
View File

@ -0,0 +1,17 @@
subroutine qpc_snr(s3,y,snr)
use qpc_mod
! real s2(0:NQ-1,0:151) !Symbol spectra, including sync
real s3(0:127,0:127) !Synchronized symbol spectra
integer*1 y(0:127) !Encoded symbols
! integer isync(24)
p=0.
do j=1,127
i=y(j)
p=p + s3(i,j)
enddo
snr=db(p/127.0) - db(127.0) - 4.0
return
end subroutine qpc_snr

101
lib/superfox/qpc_sync.f90 Normal file
View File

@ -0,0 +1,101 @@
subroutine qpc_sync(crcvd0,fsample,isync,fsync,ftol,f2,t2,snrsync)
parameter(N9SEC=9*12000,NMAX=15*12000,NDOWN=16,NZ=N9SEC/NDOWN)
complex crcvd0(NMAX) !Signal as received
complex c0(0:N9SEC-1) !For long FFT
complex c1(0:NZ-1)
complex c1sum(0:NZ-1)
complex z
real s(N9SEC/4)
real p(-1125:1125)
integer ipk(1)
integer isync(24)
baud=12000.0/1024.0
df2=fsample/N9SEC
fac=1.0/N9SEC
c0=fac*crcvd0(1:N9SEC)
call four2a(c0,N9SEC,1,-1,1) !Forward c2c FFT
iz=N9SEC/4
do i=1,iz
s(i)=real(c0(i))**2 + aimag(c0(i))**2
enddo
do i=1,4 !Smooth the spectrum a bit
call smo121(s,iz)
enddo
ia=nint((fsync-ftol)/df2)
ib=nint((fsync+ftol)/df2)
ipk=maxloc(s(ia:ib))
i0=ipk(1) + ia - 1
f2=df2*i0-750.0 ! f2 is the offset from nominal 750 Hz.
ia=nint(i0-baud/df2)
ib=nint(i0+baud/df2)
s1=0.0
s0=0.0
do i=ia,ib
s0=s0+s(i)
s1=s1+(i-i0)*s(i)
enddo
delta=s1/s0
i0=nint(i0+delta)
f2=i0*df2-750.0
c1=0.
ia=nint(i0-baud/df2)
ib=nint(i0+baud/df2)
do i=ia,ib
j=i-i0
if(j.ge.0) c1(j)=c0(i)
if(j.lt.0) c1(j+NZ)=c0(i)
enddo
call four2a(c1,NZ,1,1,1) !Reverse c2c FFT: back to time domain
c1sum(0)=c1(0)
do i=1,NZ-1
c1sum(i)=c1sum(i-1) + c1(i)
enddo
nspsd=1024/NDOWN
dt=NDOWN/12000.0
lagmax=1.5/dt
i0=nint(0.5*fsample/NDOWN) !Nominal start time is 0.5 s
pmax=0.
lagpk=0
do lag=-lagmax,lagmax
sp=0.
do j=1,24
i1=i0 + (isync(j)-1)*nspsd + lag
i2=i1 + nspsd
if(i1.lt.0 .or. i1.gt.NZ-1) cycle
if(i2.lt.0 .or. i2.gt.NZ-1) cycle
z=c1sum(i2)-c1sum(i1)
sp=sp + real(z)**2 + aimag(z)**2
enddo
if(sp.gt.pmax) then
pmax=sp
lagpk=lag
endif
p(lag)=sp
enddo
t2=lagpk*dt
snrsync=0.
sp=0.
sq=0.
nsum=0
tsym=1024/12000.0
do lag=-lagmax,lagmax
t=(lag-lagpk)*dt
if(abs(t).lt.tsym) cycle
nsum=nsum+1
sp=sp + p(lag)
sq=sq + p(lag)*p(lag)
enddo
ave=sp/nsum
rms=sqrt(sq/nsum-ave*ave)
snrsync=(pmax-ave)/rms
return
end subroutine qpc_sync

17
lib/superfox/sfox_ana.f90 Normal file
View File

@ -0,0 +1,17 @@
subroutine sfox_ana(dd,npts,c0,npts2)
real dd(npts) !Raw data at 12000 Hz
complex c0(0:npts2-1) !Complex data at 12000 Hz
save
nfft1=npts
nfft2=nfft1
fac=2.0/(32767.0*nfft1)
c0(0:npts-1)=fac*dd(1:npts)
call four2a(c0,nfft1,1,-1,1) !Forward c2c FFT
c0(nfft2/2+1:nfft2-1)=0. !Remove negative frequencies
c0(0)=0.5*c0(0) !Scale the DC term to 1/2
call four2a(c0,nfft2,1,1,1) !Inverse c2c FFT; c0 is analytic sig
return
end subroutine sfox_ana

View File

@ -0,0 +1,79 @@
subroutine sfox_demod(crcvd,f,t,isync,s2,s3)
use sfox_mod
complex crcvd(NMAX) !Signal as received
complex c(0:NSPS-1) !Work array, one symbol long
real s2(0:NQ-1,0:151) !Symbol spectra, including sync
real s3(0:NQ-1,0:NN) !Synchronized symbol spectra
integer isync(24)
integer ipk(1)
integer hist1(0:NQ-1),hist2(0:NQ-1)
j0=nint(12000.0*(t+0.5))
df=12000.0/NSPS
i0=nint(f/df)-NQ/2
k2=0
s2(:,0)=0. !The punctured symbol
s3(:,0)=0. !The punctured symbol
do n=1,NDS !Loop over all symbols
jb=n*NSPS + j0
ja=jb-NSPS+1
k2=k2+1
if(ja.lt.1 .or. jb.gt.NMAX) cycle
c=crcvd(ja:jb)
call four2a(c,NSPS,1,-1,1) !Compute symbol spectrum
do i=0,NQ-1
s2(i,k2)=real(c(i0+i))**2 + aimag(c(i0+i))**2
enddo
enddo
call pctile(s2,NQ*151,50,base2)
s2=s2/base2
hist1=0
hist2=0
do j=0,151
ipk=maxloc(s2(1:NQ-1,j)) !Find the spectral peak
i=ipk(1)-1
hist1(i)=hist1(i)+1
enddo
hist1(0)=0 !Don't treat sync tone as a birdie
do i=0,123
hist2(i)=sum(hist1(i:i+3))
enddo
ipk=maxloc(hist1)
i1=ipk(1)-1
m1=maxval(hist1)
ipk=maxloc(hist2)
i2=ipk(1)-1
m2=maxval(hist2)
if(m1.gt.12) then
do i=0,127
if(hist1(i).gt.12) then
s2(i,:)=1.0
endif
enddo
endif
if(m2.gt.20) then
if(i2.ge.1) i2=i2-1
if(i2.gt.120) i2=120
s2(i2:i2+7,:)=1.0
endif
k3=0
do n=1,NDS !Copy symbol spectra from s2 into s3
if(any(isync(1:NS).eq.n)) cycle !Skip the sync symbols
k3=k3+1
s3(:,k3)=s2(:,n)
enddo
call pctile(s3,NQ*NN,50,base3)
s3=s3/base3
return
end subroutine sfox_demod

73
lib/superfox/sfox_mod.f90 Normal file
View File

@ -0,0 +1,73 @@
module sfox_mod
parameter (NMAX=15*12000) !Samples in iwave (180,000)
integer MM,NQ,NN,KK,NS,NDS,NFZ,NSPS,NSYNC,NZ,NFFT1
real baud,tsym,bw
contains
subroutine sfox_init(mm0,nn0,kk0,itu,fspread,delay,fsample,ns0)
character*2 itu
integer isps(54)
integer iloc(1)
data isps/ 896, 960, 972, 980,1000,1008,1024,1029,1050,1080, &
1120,1125,1134,1152,1176,1200,1215,1225,1250,1260, &
1280,1296,1323,1344,1350,1372,1400,1440,1458,1470, &
1500,1512,1536,1568,1575,1600,1620,1680,1701,1715, &
1728,1750,1764,1792,1800,1875,1890,1920,1944,1960, &
2000,2016,2025,2048/
MM=mm0 !Bits per symbol
NQ=2**MM !Q, number of MFSK tones
NN=nn0 !Codeword length
KK=kk0 !Number of information symbols
NS=ns0 !Number of sync symbols
NDS=NN+NS !Total number of channel symbols
NFZ=3 !First zero
jsps=nint(12.8*fsample/NDS)
iloc=minloc(abs(isps-jsps))
NSPS=isps(iloc(1)) !Samples per symbol
NSYNC=NS*NSPS !Samples in sync waveform
NZ=NSPS*NDS !Samples in full Tx waveform
NFFT1=2*NSPS !Length of FFTs for symbol spectra
baud=fsample/NSPS
tsym=1.0/baud
bw=NQ*baud
fspread=0.0
delay=0.0
if(itu.eq.'LQ') then
fspread=0.5
delay=0.5
else if(itu.eq.'LM') then
fspread=1.5
delay=2.0
else if(itu.eq.'LD') then
fspread=10.0
delay=6.0
else if(itu.eq.'MQ') then
fspread=0.1
delay=0.5
else if(itu.eq.'MM') then
fspread=0.5
delay=1.0
else if(itu.eq.'MD') then
fspread=1.0
delay=2.0
else if(itu.eq.'HQ') then
fspread=0.5
delay=1.0
else if(itu.eq.'HM') then
fspread=10.0
delay=3.0
else if(itu.eq.'HD') then
fspread=30.0
delay=7.0
endif
return
end subroutine sfox_init
end module sfox_mod

View File

@ -0,0 +1,55 @@
subroutine sfox_prob(s3,rxdat,rxprob,rxdat2,rxprob2)
! Demodulate the 64-bin spectra for each of 63 symbols in a frame.
! Parameters
! rxdat most reliable symbol value
! rxdat2 second most likely symbol value
! rxprob probability that rxdat was the transmitted value
! rxprob2 probability that rxdat2 was the transmitted value
use sfox_mod
implicit real*8 (a-h,o-z)
real*4 s3(0:NQ-1,0:NN-1)
integer rxdat(0:NN-1),rxprob(0:NN-1),rxdat2(0:NN-1),rxprob2(0:NN-1)
afac=1.1
! scale=255.999
scale=2047.999
! Compute average spectral value
ave=sum(s3)/(NQ*ND)
i1=1 !Silence warning
i2=1
! Compute probabilities for most reliable symbol values
do j=0,NN-1 !Loop over all symbols
s1=-1.e30
psum=0.
do i=0,NQ-1 !Loop over frequency bins
x=min(afac*s3(i,j)/ave,50.d0)
psum=psum+s3(i,j)
if(s3(i,j).gt.s1) then
s1=s3(i,j) !Find max signal+noise power
i1=i !Find most reliable symbol value
endif
enddo
if(psum.eq.0.0) psum=1.e-6 !Guard against zero signal+noise
s2=-1.e30
do i=0,NQ-1
if(i.ne.i1 .and. s3(i,j).gt.s2) then
s2=s3(i,j) !Second largest signal+noise power
i2=i !Bin number for second largest power
endif
enddo
p1=s1/psum !p1, p2 are symbol metrics for ftrsd
p2=s2/psum
rxdat(j)=i1
rxdat2(j)=i2
rxprob(j)=scale*p1 !Scaled probabilities, 0 - 255
rxprob2(j)=scale*p2
enddo
return
end subroutine sfox_prob

View File

@ -0,0 +1,229 @@
subroutine sfox_remove_ft8(dd,npts)
use packjt77
include 'ft8_params.f90'
parameter (MAXCAND=100)
parameter (NP2=2812)
integer itone(NN)
integer iloc(1), ip(1)
integer icos7(0:6)
integer graymap(0:7)
integer*1 cw(174),apmask(174)
integer*1 message91(91)
integer*1 message77(77)
character*77 c77
character*37 msg37
real s8(0:7,NN)
real s2(0:7)
real ss(9)
real candidate(3,MAXCAND)
real sbase(NH1)
real dd(npts)
real bmeta(174),bmetd(174)
real llra(174),llrd(174)
complex cd0(0:3199)
complex csymb(32)
complex ctwk(32)
complex cs(0:7,NN)
data icos7/3,1,4,0,6,5,2/
logical first,newdat
logical one(0:7,0:2)
logical unpk77_success
data first/.true./
data graymap/0,1,3,2,5,6,4,7/
save one,first,twopi
if(first) then
one=.false.
do i=0,7
do j=0,2
if(iand(i,2**j).ne.0) one(i,j)=.true.
enddo
enddo
first=.false.
twopi=8.0*atan(1.0)
endif
fs2=12000.0/NDOWN
dt2=1.0/fs2
nfa=500
nfb=2500
syncmin=1.5
nfqso=750
call sync8(dd,npts,nfa,nfb,syncmin,nfqso,MAXCAND,candidate,ncand,sbase)
newdat=.true.
do icand=1,ncand
f1=candidate(1,icand)
xdt=candidate(2,icand)
xbase=10.0**(0.1*(sbase(nint(f1/3.125))-40.0))
call ft8_downsample(dd,newdat,f1,cd0)
newdat=.false.
i0=nint((xdt+0.5)*fs2)
smax=0.0
do idt=i0-10,i0+10
call sync8d(cd0,idt,ctwk,0,sync)
if(sync.gt.smax) then
smax=sync
ibest=idt
endif
enddo
smax=0.0
delfbest=0.0
do ifr=-5,5 !Search over +/- 2.5 Hz
delf=ifr*0.5
dphi=twopi*delf*dt2
phi=0.0
do i=1,32
ctwk(i)=cmplx(cos(phi),sin(phi))
phi=mod(phi+dphi,twopi)
enddo
call sync8d(cd0,ibest,ctwk,1,sync)
if( sync .gt. smax ) then
smax=sync
delfbest=delf
endif
enddo
f1=f1+delfbest !Improved estimate of DF
call ft8_downsample(dd,newdat,f1,cd0) !Mix f1 to baseband and downsample
smax=0.0
do idt=-4,4 !Search over +/- one quarter symbol
call sync8d(cd0,ibest+idt,ctwk,0,sync)
ss(idt+5)=sync
enddo
smax=maxval(ss)
iloc=maxloc(ss)
ibest=iloc(1)-5+ibest
xdt=(ibest-1)*dt2
sync=smax
do k=1,NN
i1=ibest+(k-1)*32
csymb=cmplx(0.0,0.0)
if( i1.ge.0 .and. i1+31 .le. NP2-1 ) csymb=cd0(i1:i1+31)
call four2a(csymb,32,1,-1,1)
cs(0:7,k)=csymb(1:8)/1e3
s8(0:7,k)=abs(csymb(1:8))
enddo
! sync quality check
is1=0
is2=0
is3=0
do k=1,7
ip=maxloc(s8(:,k))
if(icos7(k-1).eq.(ip(1)-1)) is1=is1+1
ip=maxloc(s8(:,k+36))
if(icos7(k-1).eq.(ip(1)-1)) is2=is2+1
ip=maxloc(s8(:,k+72))
if(icos7(k-1).eq.(ip(1)-1)) is3=is3+1
enddo
! hard sync sum - max is 21
nsync=is1+is2+is3
if(nsync .le. 6) then ! bail out
nbadcrc=1
cycle
endif
nsym=1
nt=2**(3*nsym)
do ihalf=1,2
do k=1,29,nsym
if(ihalf.eq.1) ks=k+7
if(ihalf.eq.2) ks=k+43
amax=-1.0
do i=0,nt-1
i3=iand(i,7)
s2(i)=abs(cs(graymap(i3),ks))
enddo
i32=1+(k-1)*3+(ihalf-1)*87
ibmax=2
do ib=0,ibmax
bm=maxval(s2(0:nt-1),one(0:nt-1,ibmax-ib)) - &
maxval(s2(0:nt-1),.not.one(0:nt-1,ibmax-ib))
if(i32+ib .gt.174) cycle
bmeta(i32+ib)=bm
den=max(maxval(s2(0:nt-1),one(0:nt-1,ibmax-ib)), &
maxval(s2(0:nt-1),.not.one(0:nt-1,ibmax-ib)))
if(den.gt.0.0) then
cm=bm/den
else ! erase it
cm=0.0
endif
bmetd(i32+ib)=cm
enddo
enddo
enddo
call normalizebmet(bmeta,174)
call normalizebmet(bmetd,174)
scalefac=2.83
llra=scalefac*bmeta
llrd=scalefac*bmetd
cw=0
dmin=0
norder=2
maxosd=-1
Keff=91
apmask=0
message91=0
cw=0
call decode174_91(llra,Keff,maxosd,norder,apmask,message91,cw, &
ntype,nharderrors,dmin)
if(nharderrors.ge.0) then
message77=message91(1:77)
else
cycle
endif
if(count(cw.eq.0).eq.174) cycle
write(c77,'(77i1)') message77
read(c77(72:74),'(b3)') n3
read(c77(75:77),'(b3)') i3
if(i3.gt.5 .or. (i3.eq.0.and.n3.gt.6)) cycle
if(i3.eq.0 .and. n3.eq.2) cycle
call unpack77(c77,1,msg37,unpk77_success)
! write(77,*) 'FT8 interference: ',msg37
if(.not.unpk77_success) cycle
! Message structure: S7 D29 S7 D29 S7
itone(1:7)=icos7
itone(36+1:36+7)=icos7
itone(NN-6:NN)=icos7
k=7
do j=1,ND
i=3*j -2
k=k+1
if(j.eq.30) k=k+7
indx=cw(i)*4 + cw(i+1)*2 + cw(i+2)
itone(k)=graymap(indx)
enddo
call subtractft8(dd,itone,f1,xdt,.true.)
! return
enddo
return
end subroutine sfox_remove_ft8
subroutine normalizebmet(bmet,n)
real bmet(n)
bmetav=sum(bmet)/real(n)
bmet2av=sum(bmet*bmet)/real(n)
var=bmet2av-bmetav*bmetav
if( var .gt. 0.0 ) then
bmetsig=sqrt(var)
else
bmetsig=sqrt(bmet2av)
endif
bmet=bmet/bmetsig
return
end subroutine normalizebmet

View File

@ -0,0 +1,125 @@
subroutine sfox_unpack(nutc,x,nsnr,f0,dt0,foxcall,nsignature)
use packjt77
integer*1 x(0:49)
integer*8 n58
logical success
character*336 msgbits
character*22 msg(10) !### only msg(1) is used ??? ###
character*13 foxcall,c13
character*10 ssignature
character*4 crpt(5),grid4
character*26 freeTextMsg
character*38 c
logical use_otp
data c/' 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/'/
ncq=0
if (nsignature.eq.0) then
use_otp = .FALSE.
else
use_otp = .TRUE.
endif
write(msgbits,1000) x(0:46)
1000 format(47b7.7)
read(msgbits(327:329),'(b6)') i3 !Message type
read(msgbits(1:28),'(b28)') n28 !Standard Fox call
call unpack28(n28,foxcall,success)
! print*,'aa',i3,foxcall
if(i3.eq.1) then !Compound Fox callsign
! read(msgbits(87:101),'(b15)') n15
! call unpackgrid(n15,grid4)
! msg(1)='CQ '//trim(foxcall)//' '//grid4
! write(*,1100) nutc,nsnr,dt0,nint(f0),trim(msg(1))
! go to 100
else if(i3.eq.2) then !Up to 4 Hound calls and free text
call unpacktext77(msgbits(161:231),freeTextMsg(1:13))
call unpacktext77(msgbits(232:302),freeTextMsg(14:26))
do i=26,1,-1
if(freeTextMsg(i:i).ne.'.') exit
freeTextMsg(i:i)=' '
enddo
! print*,'aa1 ',freeTextMsg
write(*,1100) nutc,nsnr,dt0,nint(f0),freeTextMsg
1100 format(i6.6,i4,f5.1,i5,1x,"~",2x,a)
else if(i3.eq.3) then !CQ FoxCall Grid
read(msgbits(1:58),'(b58)') n58 !FoxCall
do i=11,1,-1
j=mod(n58,38)+1
foxcall(i:i)=c(j:j)
n58=n58/38
enddo
foxcall(12:13)=' '
read(msgbits(59:73),'(b15)') n15
call unpackgrid(n15,grid4)
msg(1)='CQ '//trim(foxcall)//' '//grid4
write(*,1100) nutc,nsnr,dt0,nint(f0),trim(msg(1))
call unpacktext77(msgbits(74:144),freeTextMsg(1:13))
call unpacktext77(msgbits(145:215),freeTextMsg(14:26))
do i=26,1,-1
if(freeTextMsg(i:i).ne.'.') exit
freeTextMsg(i:i)=' '
enddo
if(len(trim(freeTextMsg)).gt.0) write(*,1100) nutc,nsnr,dt0,&
nint(f0),freeTextMsg
go to 100
endif
j=281
iz=4 !Max number of reports
if(i3.eq.2) j=141
! print*,'aa2',j,iz
do i=1,iz !Extract the reports
read(msgbits(j:j+4),'(b5)') n
if(n.eq.31) then
crpt(i)='RR73'
else
write(crpt(i),1006) n-18
1006 format(i3.2)
if(crpt(i)(1:1).eq.' ') crpt(i)(1:1)='+'
endif
! print*,'aa3',i,n,crpt(i)
j=j+5
enddo
! Unpack Hound callsigns and format user-level messages:
iz=9 !Max number of hound calls
if(i3.eq.2 .or. i3.eq.3) iz=4
! print*,'bb',i3,iz
! print*,msgbits
do i=1,iz
j=28*i + 1
read(msgbits(j:j+27),'(b28)') n28
call unpack28(n28,c13,success)
! print*,'cc',i,j,n28
if(n28.eq.0) cycle
msg(i)=trim(c13)//' '//trim(foxcall)
if(msg(i)(1:3).eq.'CQ ') then
ncq=ncq+1
else
if(i3.eq.2) then
! print*,'dd',i,crpt(i)
msg(i)=trim(msg(i))//' '//crpt(i)
else
if(i.le.5) msg(i)=trim(msg(i))//' RR73'
if(i.gt.5) msg(i)=trim(msg(i))//' '//crpt(i-5)
endif
endif
if(ncq.le.1 .or. msg(i)(1:3).ne.'CQ ') then
write(*,1100) nutc,nsnr,dt0,nint(f0),trim(msg(i))
endif
enddo
if(msgbits(306:306).eq.'1' .and. ncq.lt.1) then
write(*,1100) nutc,nsnr,dt0,nint(f0),'CQ '//foxcall
endif
100 read(msgbits(307:326),'(b20)') nsignature
! print*,'i3:',i3
if (use_otp) then
write(ssignature,'(I0)') nsignature
write(*,1100) nutc,nsnr,dt0,nint(f0),'$VERIFY$ '//trim(foxcall)//' '//trim(ssignature)
endif
return
end subroutine sfox_unpack

137
lib/superfox/sfrx.f90 Normal file
View File

@ -0,0 +1,137 @@
program sfrx
use sfox_mod
use julian
use popen_module, only: get_command_as_string
integer*2 iwave(NMAX)
integer ihdr(11)
integer*8 secday,ntime8,ntime8_chk
integer*1 xdec(0:49)
character*120 fname,cmnd,cresult
character*13 foxcall,foxcall_chk
character*256 ppath
complex c0(NMAX) !Complex form of signal as received
real dd(NMAX)
logical crc_ok
logical use_otp
data secday/86400/
include 'gtag.f90'
use_otp = .FALSE.
fsync=750.0
ftol=100.0
narg=iargc()
if(narg.lt.1) then
! print*,'Usage: sfrx [fsync [ftol]] infile [...]'
! print*,'Examples: sfrx 230305_011230.wav'
! print*,' sfrx 775 10 240811_102400.wav'
! print*,'Reads one or more .wav files and calls SuperFox decoder on each.'
! print*,'Defaults: fsync=750, ftol=100'
print '(" Git tag: ",z9)',ntag
go to 999
endif
ifile1=1
call getarg(1,fname)
read(fname,*,err=1) fsync
ifile1=2
call getarg(2,fname)
read(fname,*,err=1) ftol
ifile1=3
1 nf=0
nd=0
nv=0
fsample=12000.0
call sfox_init(7,127,50,'no',fspread,delay,fsample,24)
npts=15*12000
do ifile=ifile1,narg
call getarg(ifile,fname)
if(fname.eq.'OTP') then
use_otp = .TRUE.
cycle
endif
open(10,file=trim(fname),status='old',access='stream',err=4)
go to 5
4 print*,'Cannot open file ',trim(fname)
go to 999
5 read(10) ihdr,iwave
close(10)
nz=len(trim(fname))
nyymmdd=ihdr(1)
nutc=ihdr(2)
if(fname(nz-3:nz).eq.'.wav') then
read(fname(nz-16:nz-11),*) nyymmdd
read(fname(nz-9:nz-4),*) nutc
endif
if(nyymmdd.eq.-1) then
ntime8=itime8()/30
ntime8=30*ntime8
else
iyr=2000+nyymmdd/10000
imo=mod(nyymmdd/100,100)
iday=mod(nyymmdd,100)
ih=nutc/10000
im=mod(nutc/100,100)
is=mod(nutc,100)
ntime8=secday*(JD(iyr,imo,iday)-2440588) + 3600*ih + 60*im + is
endif
dd=iwave
call sfox_remove_ft8(dd,npts)
call sfox_ana(dd,npts,c0,npts)
ndepth=3
dth=0.5
damp=1.0
call qpc_decode2(c0,fsync,ftol, xdec,ndepth,dth,damp,crc_ok, &
snrsync,fbest,tbest,snr)
if(crc_ok) then
nsnr=nint(snr)
if (use_otp) then
nsignature = 1
else
nsignature = 0
endif
call sfox_unpack(nutc,xdec,nsnr,fbest-750.0,tbest,foxcall,nsignature)
! Execute 'foxchk <foxcall> <ntime8>' to get the correct signature value.
write(cmnd,1102) trim(foxcall),ntime8
1102 format('foxchk ',a,i12)
call getarg(0,ppath)
lindex=index(ppath,'sfrx')-1
if (.not.use_otp) then
cmnd=ppath(1:lindex)//cmnd
cresult=get_command_as_string(trim(cmnd))
read(cresult,1104) foxcall_chk,ntime8_chk,nsignature_chk
1104 format(a11,i13,i10.7)
if(nsignature.eq.nsignature_chk) write(*,1110) trim(foxcall)
1110 format(a,' verified')
if(nsignature.eq.nsignature_chk) nv=nv+1
endif
nd=nd+1
! else
! i0=index(fname,'.wav')
! write(60,3060) trim(fname)
!3060 format('cp ',a,' nodecode')
endif
nf=nf+1
enddo
ncarg=narg
if (use_otp) then
ncarg=ncarg-1
endif
if(ncarg.gt.ifile1) write(*,1999) nf,nd,nv
1999 format('nfiles:',i5,' ndecodes:',i5,' nverified:',i5)
999 end program sfrx

19
lib/superfox/twkfreq2.f90 Normal file
View File

@ -0,0 +1,19 @@
subroutine twkfreq2(c3,c4,npts,fsample,fshift)
! Adjust frequency of complex waveform
complex c3(npts)
complex c4(npts)
complex w,wstep
data twopi/6.283185307/
w=1.0
dphi=fshift*twopi/fsample
wstep=cmplx(cos(dphi),sin(dphi))
do i=1,npts
w=w*wstep
c4(i)=w*c3(i)
enddo
return
end subroutine twkfreq2