diff --git a/README.md b/README.md index 5a9a935..a012ab2 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,13 @@ the spectrum from "classical" spectrum view to "waterfall" (and back). This feature applies to both the display showing the full spectrum as the display showing the "decoder" spectrum. +-------------------------------------------------------------------------- +New: ft8 decoder +-------------------------------------------------------------------------- + +One of the new elements in the implementation is the addition of a +decoder for FT8 data. See the section on FT* + -------------------------------------------------------------------------- Device selection -------------------------------------------------------------------------- @@ -187,15 +194,27 @@ The decoder has three control elements * the indicator for the search range, selectable to 5.5 KHz +The current version has as option sending (some parts of) the messages to +the pskreporter, i.e. using the decoder as "Monitor", the map on pskreporter.info will then show you as "monitor". + +The pskreporter (see report.pskreporter.info) obviously wants to know +who is sending the data, so there is a possibility to fill in your +callsign (I do not have a callsign, not being a "real" amateur, but I have an official number as "listener", obtained from the VERON, the association of radio amateurs in the Netherlands), the maiddenhead grid indication of your locatio +(which helps the pskreportet to position your description on the map) and +the antenna. + * Touching the "set identity" button will show a small form +where these data can be filled in). + + * the psk reporter can be switched on and off, if switched on and +the software could initialize the connection to the psk reporter site +the label is colored greem. + * a file button for selecting a file where the received messages are stored. Note: the decoder is experimental and will definitely not catch all transmitted messages. ![swradio-8](/swradio-ft8-widget.png?raw=true) -In the current version one can set a **callsign** and a **home grid**, -if these are set, the decoder will upload details to the PSKReporter, -so you can see yourself as monitor on the PSKreporter map. ------------------------------------------------------------------------- a note on the weatherfax decoder diff --git a/decoders/ft8-decoder-win/PSKReporter.h b/decoders/ft8-decoder-win/PSKReporter.h new file mode 100644 index 0000000..a6c52c4 --- /dev/null +++ b/decoders/ft8-decoder-win/PSKReporter.h @@ -0,0 +1,127 @@ +#pragma once + +// Main header file for the external interface to the PSK Reporter API +// For documentation see http://psk.gladstonefamily.net/PSKReporterAPI.pdf + +/* + +Copyright (c) 2008 Philip Gladstone + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + */ + + +#ifdef _DLL_OPTION_PSKREPORTER_EXPORT +#define DllImportExport __declspec ( dllexport ) +#else +#define DllImportExport __declspec ( dllimport ) +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +#define REPORTER_SOURCE_MASK 0x07 +#define REPORTER_SOURCE_AUTOMATIC 0x01 +#define REPORTER_SOURCE_LOG 0x02 +#define REPORTER_SOURCE_MANUAL 0x03 +#define REPORTER_SOURCE_TENTATIVE 0x40 +#define REPORTER_SOURCE_TEST 0x80 + +typedef struct { + wchar_t hostname[256]; + wchar_t port[32]; + bool connected; + unsigned int callsigns_sent; + unsigned int callsigns_buffered; + unsigned int callsigns_discarded; + unsigned int last_send_time; + unsigned int next_send_time; + wchar_t last_callsign_queued[24]; + unsigned int bytes_sent; + unsigned int bytes_sent_total; + unsigned int packets_sent; + unsigned int packets_sent_total; +} REPORTER_STATISTICS; + + + + +unsigned long DllImportExport __cdecl ReporterInitialize( + const wchar_t *hostname, + const wchar_t *port +); + +unsigned long DllImportExport __cdecl ReporterSeenCallsign( + const wchar_t *remoteInformation, + const wchar_t *localInformation, + unsigned long flags +); + +unsigned long DllImportExport __cdecl ReporterTickle( +); + +unsigned long DllImportExport __cdecl ReporterGetInformation( + wchar_t *buffer, + unsigned long maxlen +); + +unsigned long DllImportExport __cdecl ReporterGetStatistics( + REPORTER_STATISTICS *buffer, + unsigned long maxlen +); + +unsigned long DllImportExport __cdecl ReporterUninitialize( +); + + +unsigned long DllImportExport __stdcall ReporterInitializeSTD( + const char *hostname, + const char *port +); + +unsigned long DllImportExport __stdcall ReporterSeenCallsignSTD( + const char *remoteInformation, + const char *localInformation, + unsigned long flags +); + +unsigned long DllImportExport __stdcall ReporterTickleSTD( +); + +unsigned long DllImportExport __stdcall ReporterGetInformationSTD( + char *buffer, + unsigned long maxlen +); + +unsigned long DllImportExport __stdcall ReporterGetStatisticsSTD( + REPORTER_STATISTICS *buffer, + unsigned long maxlen +); + +unsigned long DllImportExport __stdcall ReporterUninitializeSTD( +); + +#ifdef __cplusplus +} +#endif + + diff --git a/decoders/ft8-decoder-win/cache.h b/decoders/ft8-decoder-win/cache.h new file mode 100644 index 0000000..0bf7dc3 --- /dev/null +++ b/decoders/ft8-decoder-win/cache.h @@ -0,0 +1,64 @@ +# +/* + * Copyright (C) 2010, 2011, 2012, 2013 + * Jan van Katwijk (J.vanKatwijk@gmail.com) + * Lazy Chair Programming + * + * This file is part of swradio + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +// +// to be included by the main program of the decoder + +#include +#include + +class Cache { +private: + int nrows; + int ncolums; + std::complex **data; + +public: + Cache (int16_t ncolums, int16_t nrows) { +int16_t i; + + this -> ncolums = ncolums; + this -> nrows = nrows; + data = new std::complex *[nrows]; + for (i = 0; i < nrows; i++) + data [i] = new std::complex [ncolums]; + fprintf (stderr, "new cache with %d rows and %d colums\n", + nrows, ncolums); +} + + ~Cache (void) { +int i; + for (i = 0; i < nrows; i ++) + delete[] data [i]; + + delete[] data; +} + +std::complex *cacheLine (int16_t n) { + return data [n]; +} + +std::complex cacheElement (int16_t line, int16_t element) { + return data [line] [element]; +} +}; + diff --git a/decoders/ft8-decoder-win/dl-cache.h b/decoders/ft8-decoder-win/dl-cache.h new file mode 100644 index 0000000..beaa391 --- /dev/null +++ b/decoders/ft8-decoder-win/dl-cache.h @@ -0,0 +1,83 @@ +# +/* + * Copyright (C) 2010, 2011, 2012, 2013 + * Jan van Katwijk (J.vanKatwijk@gmail.com) + * Lazy Chair Computing + * + * This file is part of swradio + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __DL_CACHE__ +#define __DL_CACHE__ + +#include +#include +typedef struct { + float strength; + float frequency; + std::string theMessage; +} cacheElement; + +class dlCache { +private: +std::vector cache; +int p; +int size; +#define CACHE_SIZE 32 +#define CACHE_MASK (CACHE_SIZE - 1) +public: + dlCache (int size) { + (void)size; + cache. resize (CACHE_SIZE); + this -> size = CACHE_SIZE; + p = 0; + } + + ~dlCache () { + } + +void add (const std::string &s, + float strength, float frequency) { + cache [p]. strength = strength; + cache [p]. frequency = frequency; + cache [p]. theMessage = s; + p = (p + 1) & CACHE_MASK; +} + +bool update (float strength, + float frequency, const std::string &s) { + for (uint16_t i = p; i < p + CACHE_SIZE; i ++) { + if (cache [i & CACHE_MASK]. theMessage == s) { + if (cache [i & CACHE_MASK]. strength < strength) { + for (uint16_t j = i; j < (p - 1) + CACHE_SIZE; j ++) + cache [j & CACHE_MASK] = cache [(j + 1) & CACHE_MASK]; + cache [(p - 1 + CACHE_SIZE) & CACHE_MASK]. theMessage = s; + cache [(p - 1 + CACHE_SIZE) & CACHE_MASK]. strength = strength; + cache [(p - 1 + CACHE_SIZE) & CACHE_MASK]. frequency = + frequency; + + } + return true; + } + } + cache [p]. theMessage = s; + cache [p]. strength = strength; + cache [p]. frequency = frequency; + p = (p + 1) & CACHE_MASK; + return false; +} +}; +#endif diff --git a/decoders/ft8-decoder-win/fft/_kiss_fft_guts.h b/decoders/ft8-decoder-win/fft/_kiss_fft_guts.h new file mode 100644 index 0000000..bf5d53c --- /dev/null +++ b/decoders/ft8-decoder-win/fft/_kiss_fft_guts.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. + * This file is part of KISS FFT - https://github.com/mborgerding/kissfft + * + * SPDX-License-Identifier: BSD-3-Clause + * See COPYING file for more information. + */ + +/* kiss_fft.h + defines kiss_fft_scalar as either short or a float type + and defines + typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */ +#include "kiss_fft.h" +#include + +#define MAXFACTORS 32 +/* e.g. an fft of length 128 has 4 factors + as far as kissfft is concerned + 4*4*4*2 + */ + +struct kiss_fft_state{ + int nfft; + int inverse; + int factors[2*MAXFACTORS]; + kiss_fft_cpx twiddles[1]; +}; + +/* + Explanation of macros dealing with complex math: + + C_MUL(m,a,b) : m = a*b + C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise + C_SUB( res, a,b) : res = a - b + C_SUBFROM( res , a) : res -= a + C_ADDTO( res , a) : res += a + * */ +#ifdef FIXED_POINT +#if (FIXED_POINT==32) +# define FRACBITS 31 +# define SAMPPROD int64_t +#define SAMP_MAX 2147483647 +#else +# define FRACBITS 15 +# define SAMPPROD int32_t +#define SAMP_MAX 32767 +#endif + +#define SAMP_MIN -SAMP_MAX + +#if defined(CHECK_OVERFLOW) +# define CHECK_OVERFLOW_OP(a,op,b) \ + if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \ + fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); } +#endif + + +# define smul(a,b) ( (SAMPPROD)(a)*(b) ) +# define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS ) + +# define S_MUL(a,b) sround( smul(a,b) ) + +# define C_MUL(m,a,b) \ + do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \ + (m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0) + +# define DIVSCALAR(x,k) \ + (x) = sround( smul( x, SAMP_MAX/k ) ) + +# define C_FIXDIV(c,div) \ + do { DIVSCALAR( (c).r , div); \ + DIVSCALAR( (c).i , div); }while (0) + +# define C_MULBYSCALAR( c, s ) \ + do{ (c).r = sround( smul( (c).r , s ) ) ;\ + (c).i = sround( smul( (c).i , s ) ) ; }while(0) + +#else /* not FIXED_POINT*/ + +# define S_MUL(a,b) ( (a)*(b) ) +#define C_MUL(m,a,b) \ + do{ (m).r = (a).r*(b).r - (a).i*(b).i;\ + (m).i = (a).r*(b).i + (a).i*(b).r; }while(0) +# define C_FIXDIV(c,div) /* NOOP */ +# define C_MULBYSCALAR( c, s ) \ + do{ (c).r *= (s);\ + (c).i *= (s); }while(0) +#endif + +#ifndef CHECK_OVERFLOW_OP +# define CHECK_OVERFLOW_OP(a,op,b) /* noop */ +#endif + +#define C_ADD( res, a,b)\ + do { \ + CHECK_OVERFLOW_OP((a).r,+,(b).r)\ + CHECK_OVERFLOW_OP((a).i,+,(b).i)\ + (res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \ + }while(0) +#define C_SUB( res, a,b)\ + do { \ + CHECK_OVERFLOW_OP((a).r,-,(b).r)\ + CHECK_OVERFLOW_OP((a).i,-,(b).i)\ + (res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \ + }while(0) +#define C_ADDTO( res , a)\ + do { \ + CHECK_OVERFLOW_OP((res).r,+,(a).r)\ + CHECK_OVERFLOW_OP((res).i,+,(a).i)\ + (res).r += (a).r; (res).i += (a).i;\ + }while(0) + +#define C_SUBFROM( res , a)\ + do {\ + CHECK_OVERFLOW_OP((res).r,-,(a).r)\ + CHECK_OVERFLOW_OP((res).i,-,(a).i)\ + (res).r -= (a).r; (res).i -= (a).i; \ + }while(0) + + +#ifdef FIXED_POINT +# define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase)) +# define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase)) +# define HALF_OF(x) ((x)>>1) +#elif defined(USE_SIMD) +# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) ) +# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) ) +# define HALF_OF(x) ((x)*_mm_set1_ps(.5)) +#else +# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase) +# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase) +# define HALF_OF(x) ((x)*.5) +#endif + +#define kf_cexp(x,phase) \ + do{ \ + (x)->r = KISS_FFT_COS(phase);\ + (x)->i = KISS_FFT_SIN(phase);\ + }while(0) + + +/* a debugging function */ +#define pcpx(c)\ + fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) ) + + +#ifdef KISS_FFT_USE_ALLOCA +// define this to allow use of alloca instead of malloc for temporary buffers +// Temporary buffers are used in two case: +// 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5 +// 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform. +#include +#define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes) +#define KISS_FFT_TMP_FREE(ptr) +#else +#define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes) +#define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr) +#endif diff --git a/decoders/ft8-decoder-win/fft/kiss_fft.c b/decoders/ft8-decoder-win/fft/kiss_fft.c new file mode 100644 index 0000000..9bee8a6 --- /dev/null +++ b/decoders/ft8-decoder-win/fft/kiss_fft.c @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. + * This file is part of KISS FFT - https://github.com/mborgerding/kissfft + * + * SPDX-License-Identifier: BSD-3-Clause + * See COPYING file for more information. + */ + + +#include "_kiss_fft_guts.h" +/* The guts header contains all the multiplication and + * addition macros that are defined for + * fixed or floating point complex numbers. + * It also delares the kf_ internal functions. + */ + +static +void kf_bfly2 (kiss_fft_cpx * Fout, const size_t fstride, + const kiss_fft_cfg st, int m) { + +kiss_fft_cpx *Fout2; +kiss_fft_cpx *tw1 = st -> twiddles; +kiss_fft_cpx t; + + Fout2 = Fout + m; + do { + C_FIXDIV (*Fout, 2); + C_FIXDIV (*Fout2, 2); + + C_MUL (t, *Fout2 , *tw1); + tw1 += fstride; + C_SUB (*Fout2, *Fout ,t); + C_ADDTO (*Fout, t); + Fout2 ++; + Fout ++; + } while (--m); +} + +static void kf_bfly4( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + const size_t m + ) +{ + kiss_fft_cpx *tw1,*tw2,*tw3; + kiss_fft_cpx scratch[6]; + size_t k=m; + const size_t m2=2*m; + const size_t m3=3*m; + + + tw3 = tw2 = tw1 = st->twiddles; + + do { + C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4); + + C_MUL(scratch[0],Fout[m] , *tw1 ); + C_MUL(scratch[1],Fout[m2] , *tw2 ); + C_MUL(scratch[2],Fout[m3] , *tw3 ); + + C_SUB( scratch[5] , *Fout, scratch[1] ); + C_ADDTO(*Fout, scratch[1]); + C_ADD( scratch[3] , scratch[0] , scratch[2] ); + C_SUB( scratch[4] , scratch[0] , scratch[2] ); + C_SUB( Fout[m2], *Fout, scratch[3] ); + tw1 += fstride; + tw2 += fstride*2; + tw3 += fstride*3; + C_ADDTO( *Fout , scratch[3] ); + + if(st->inverse) { + Fout[m].r = scratch[5].r - scratch[4].i; + Fout[m].i = scratch[5].i + scratch[4].r; + Fout[m3].r = scratch[5].r + scratch[4].i; + Fout[m3].i = scratch[5].i - scratch[4].r; + }else{ + Fout[m].r = scratch[5].r + scratch[4].i; + Fout[m].i = scratch[5].i - scratch[4].r; + Fout[m3].r = scratch[5].r - scratch[4].i; + Fout[m3].i = scratch[5].i + scratch[4].r; + } + ++Fout; + }while(--k); +} + +static void kf_bfly3( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + size_t m + ) +{ + size_t k=m; + const size_t m2 = 2*m; + kiss_fft_cpx *tw1,*tw2; + kiss_fft_cpx scratch[5]; + kiss_fft_cpx epi3; + epi3 = st->twiddles[fstride*m]; + + tw1=tw2=st->twiddles; + + do{ + C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3); + + C_MUL(scratch[1],Fout[m] , *tw1); + C_MUL(scratch[2],Fout[m2] , *tw2); + + C_ADD(scratch[3],scratch[1],scratch[2]); + C_SUB(scratch[0],scratch[1],scratch[2]); + tw1 += fstride; + tw2 += fstride*2; + + Fout[m].r = Fout->r - HALF_OF(scratch[3].r); + Fout[m].i = Fout->i - HALF_OF(scratch[3].i); + + C_MULBYSCALAR( scratch[0] , epi3.i ); + + C_ADDTO(*Fout,scratch[3]); + + Fout[m2].r = Fout[m].r + scratch[0].i; + Fout[m2].i = Fout[m].i - scratch[0].r; + + Fout[m].r -= scratch[0].i; + Fout[m].i += scratch[0].r; + + ++Fout; + }while(--k); +} + +static void kf_bfly5( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m + ) +{ + kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; + int u; + kiss_fft_cpx scratch[13]; + kiss_fft_cpx * twiddles = st->twiddles; + kiss_fft_cpx *tw; + kiss_fft_cpx ya,yb; + ya = twiddles[fstride*m]; + yb = twiddles[fstride*2*m]; + + Fout0=Fout; + Fout1=Fout0+m; + Fout2=Fout0+2*m; + Fout3=Fout0+3*m; + Fout4=Fout0+4*m; + + tw=st->twiddles; + for ( u=0; ur += scratch[7].r + scratch[8].r; + Fout0->i += scratch[7].i + scratch[8].i; + + scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r); + scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r); + + scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i); + scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i); + + C_SUB(*Fout1,scratch[5],scratch[6]); + C_ADD(*Fout4,scratch[5],scratch[6]); + + scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r); + scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r); + scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i); + scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i); + + C_ADD(*Fout2,scratch[11],scratch[12]); + C_SUB(*Fout3,scratch[11],scratch[12]); + + ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; + } +} + +/* perform the butterfly for one stage of a mixed radix FFT */ +static void kf_bfly_generic( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m, + int p + ) +{ + int u,k,q1,q; + kiss_fft_cpx * twiddles = st->twiddles; + kiss_fft_cpx t; + int Norig = st->nfft; + + kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p); + + for ( u=0; u=Norig) twidx-=Norig; + C_MUL(t,scratch[q] , twiddles[twidx] ); + C_ADDTO( Fout[ k ] ,t); + } + k += m; + } + } + KISS_FFT_TMP_FREE(scratch); +} + +static +void kf_work( + kiss_fft_cpx * Fout, + const kiss_fft_cpx * f, + const size_t fstride, + int in_stride, + int * factors, + const kiss_fft_cfg st + ) +{ + kiss_fft_cpx * Fout_beg=Fout; + const int p=*factors++; /* the radix */ + const int m=*factors++; /* stage's fft length/p */ + const kiss_fft_cpx * Fout_end = Fout + p*m; + +#ifdef _OPENMP + // use openmp extensions at the + // top-level (not recursive) + if (fstride==1 && p<=5) + { + int k; + + // execute the p different work units in different threads +# pragma omp parallel for + for (k=0;k floor_sqrt) + p = n; /* no more factors, skip to end */ + } + n /= p; + *facbuf++ = p; + *facbuf++ = n; + } while (n > 1); +} + +/* + * + * User-callable function to allocate all necessary storage space for the fft. + * + * The return value is a contiguous block of memory, allocated with malloc. As such, + * It can be freed with free(), rather than a kiss_fft-specific function. + * */ +kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem ) +{ + kiss_fft_cfg st=NULL; + size_t memneeded = sizeof(struct kiss_fft_state) + + sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/ + + if ( lenmem==NULL ) { + st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded ); + }else{ + if (mem != NULL && *lenmem >= memneeded) + st = (kiss_fft_cfg)mem; + *lenmem = memneeded; + } + if (st) { + int i; + st->nfft=nfft; + st->inverse = inverse_fft; + + for (i=0;iinverse) + phase *= -1; + kf_cexp(st->twiddles+i, phase ); + } + + kf_factor(nfft,st->factors); + } + return st; +} + + +void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride) +{ + if (fin == fout) { + //NOTE: this is not really an in-place FFT algorithm. + //It just performs an out-of-place FFT into a temp buffer + kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft); + kf_work(tmpbuf,fin,1,in_stride, st->factors,st); + memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft); + KISS_FFT_TMP_FREE(tmpbuf); + }else{ + kf_work( fout, fin, 1,in_stride, st->factors,st ); + } +} + +void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) +{ + kiss_fft_stride(cfg,fin,fout,1); +} + + +void kiss_fft_cleanup(void) +{ + // nothing needed any more +} + +int kiss_fft_next_fast_size(int n) +{ + while(1) { + int m=n; + while ( (m%2) == 0 ) m/=2; + while ( (m%3) == 0 ) m/=3; + while ( (m%5) == 0 ) m/=5; + if (m<=1) + break; /* n is completely factorable by twos, threes, and fives */ + n++; + } + return n; +} diff --git a/decoders/ft8-decoder-win/fft/kiss_fft.h b/decoders/ft8-decoder-win/fft/kiss_fft.h new file mode 100644 index 0000000..45c3a6a --- /dev/null +++ b/decoders/ft8-decoder-win/fft/kiss_fft.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. + * This file is part of KISS FFT - https://github.com/mborgerding/kissfft + * + * SPDX-License-Identifier: BSD-3-Clause + * See COPYING file for more information. + */ + +#ifndef KISS_FFT_H +#define KISS_FFT_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + ATTENTION! + If you would like a : + -- a utility that will handle the caching of fft objects + -- real-only (no imaginary time component ) FFT + -- a multi-dimensional FFT + -- a command-line utility to perform ffts + -- a command-line utility to perform fast-convolution filtering + + Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c + in the tools/ directory. +*/ + +#ifdef USE_SIMD +# include +# define kiss_fft_scalar __m128 +#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16) +#define KISS_FFT_FREE _mm_free +#else +#define KISS_FFT_MALLOC malloc +#define KISS_FFT_FREE free +#endif + + +#ifdef FIXED_POINT +#include +# if (FIXED_POINT == 32) +# define kiss_fft_scalar int32_t +# else +# define kiss_fft_scalar int16_t +# endif +#else +# ifndef kiss_fft_scalar +/* default is float */ +# define kiss_fft_scalar float +# endif +#endif + +typedef struct { + kiss_fft_scalar r; + kiss_fft_scalar i; +}kiss_fft_cpx; + +typedef struct kiss_fft_state* kiss_fft_cfg; + +/* + * kiss_fft_alloc + * + * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. + * + * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL); + * + * The return value from fft_alloc is a cfg buffer used internally + * by the fft routine or NULL. + * + * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc. + * The returned value should be free()d when done to avoid memory leaks. + * + * The state can be placed in a user supplied buffer 'mem': + * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, + * then the function places the cfg in mem and the size used in *lenmem + * and returns mem. + * + * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), + * then the function returns NULL and places the minimum cfg + * buffer size in *lenmem. + * */ + +kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); + +/* + * kiss_fft(cfg,in_out_buf) + * + * Perform an FFT on a complex input buffer. + * for a forward FFT, + * fin should be f[0] , f[1] , ... ,f[nfft-1] + * fout will be F[0] , F[1] , ... ,F[nfft-1] + * Note that each element is complex and can be accessed like + f[k].r and f[k].i + * */ +void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); + +/* + A more generic version of the above function. It reads its input from every Nth sample. + * */ +void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); + +/* If kiss_fft_alloc allocated a buffer, it is one contiguous + buffer and can be simply free()d when no longer needed*/ +#define kiss_fft_free KISS_FFT_FREE + +/* + Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up + your compiler output to call this before you exit. +*/ +void kiss_fft_cleanup(void); + + +/* + * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5) + */ +int kiss_fft_next_fast_size(int n); + +/* for real ffts, we need an even size */ +#define kiss_fftr_next_fast_size_real(n) \ + (kiss_fft_next_fast_size( ((n)+1)>>1)<<1) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/decoders/ft8-decoder-win/fft/kiss_fftr.c b/decoders/ft8-decoder-win/fft/kiss_fftr.c new file mode 100644 index 0000000..8102132 --- /dev/null +++ b/decoders/ft8-decoder-win/fft/kiss_fftr.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. + * This file is part of KISS FFT - https://github.com/mborgerding/kissfft + * + * SPDX-License-Identifier: BSD-3-Clause + * See COPYING file for more information. + */ + +#include "kiss_fftr.h" +#include "_kiss_fft_guts.h" + +struct kiss_fftr_state{ + kiss_fft_cfg substate; + kiss_fft_cpx * tmpbuf; + kiss_fft_cpx * super_twiddles; +#ifdef USE_SIMD + void * pad; +#endif +}; + +kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem) +{ + int i; + kiss_fftr_cfg st = NULL; + size_t subsize = 0, memneeded; + + if (nfft & 1) { + fprintf(stderr,"Real FFT optimization must be even.\n"); + return NULL; + } + nfft >>= 1; + + kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize); + memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2); + + if (lenmem == NULL) { + st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded); + } else { + if (*lenmem >= memneeded) + st = (kiss_fftr_cfg) mem; + *lenmem = memneeded; + } + if (!st) + return NULL; + + st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */ + st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize); + st->super_twiddles = st->tmpbuf + nfft; + kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize); + + for (i = 0; i < nfft/2; ++i) { + double phase = + -3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5); + if (inverse_fft) + phase *= -1; + kf_cexp (st->super_twiddles+i,phase); + } + return st; +} + +void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata) +{ + /* input buffer timedata is stored row-wise */ + int k,ncfft; + kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc; + + if ( st->substate->inverse) { + fprintf(stderr,"kiss fft usage error: improper alloc\n"); + exit(1); + } + + ncfft = st->substate->nfft; + + /*perform the parallel fft of two real signals packed in real,imag*/ + kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf ); + /* The real part of the DC element of the frequency spectrum in st->tmpbuf + * contains the sum of the even-numbered elements of the input time sequence + * The imag part is the sum of the odd-numbered elements + * + * The sum of tdc.r and tdc.i is the sum of the input time sequence. + * yielding DC of input time sequence + * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1... + * yielding Nyquist bin of input time sequence + */ + + tdc.r = st->tmpbuf[0].r; + tdc.i = st->tmpbuf[0].i; + C_FIXDIV(tdc,2); + CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i); + CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i); + freqdata[0].r = tdc.r + tdc.i; + freqdata[ncfft].r = tdc.r - tdc.i; +#ifdef USE_SIMD + freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0); +#else + freqdata[ncfft].i = freqdata[0].i = 0; +#endif + + for ( k=1;k <= ncfft/2 ; ++k ) { + fpk = st->tmpbuf[k]; + fpnk.r = st->tmpbuf[ncfft-k].r; + fpnk.i = - st->tmpbuf[ncfft-k].i; + C_FIXDIV(fpk,2); + C_FIXDIV(fpnk,2); + + C_ADD( f1k, fpk , fpnk ); + C_SUB( f2k, fpk , fpnk ); + C_MUL( tw , f2k , st->super_twiddles[k-1]); + + freqdata[k].r = HALF_OF(f1k.r + tw.r); + freqdata[k].i = HALF_OF(f1k.i + tw.i); + freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r); + freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i); + } +} + +void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata) +{ + /* input buffer timedata is stored row-wise */ + int k, ncfft; + + if (st->substate->inverse == 0) { + fprintf (stderr, "kiss fft usage error: improper alloc\n"); + exit (1); + } + + ncfft = st->substate->nfft; + + st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r; + st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r; + C_FIXDIV(st->tmpbuf[0],2); + + for (k = 1; k <= ncfft / 2; ++k) { + kiss_fft_cpx fk, fnkc, fek, fok, tmp; + fk = freqdata[k]; + fnkc.r = freqdata[ncfft - k].r; + fnkc.i = -freqdata[ncfft - k].i; + C_FIXDIV( fk , 2 ); + C_FIXDIV( fnkc , 2 ); + + C_ADD (fek, fk, fnkc); + C_SUB (tmp, fk, fnkc); + C_MUL (fok, tmp, st->super_twiddles[k-1]); + C_ADD (st->tmpbuf[k], fek, fok); + C_SUB (st->tmpbuf[ncfft - k], fek, fok); +#ifdef USE_SIMD + st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0); +#else + st->tmpbuf[ncfft - k].i *= -1; +#endif + } + kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata); +} diff --git a/decoders/ft8-decoder-win/fft/kiss_fftr.h b/decoders/ft8-decoder-win/fft/kiss_fftr.h new file mode 100644 index 0000000..588948d --- /dev/null +++ b/decoders/ft8-decoder-win/fft/kiss_fftr.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. + * This file is part of KISS FFT - https://github.com/mborgerding/kissfft + * + * SPDX-License-Identifier: BSD-3-Clause + * See COPYING file for more information. + */ + +#ifndef KISS_FTR_H +#define KISS_FTR_H + +#include "kiss_fft.h" +#ifdef __cplusplus +extern "C" { +#endif + + +/* + + Real optimized version can save about 45% cpu time vs. complex fft of a real seq. + + + + */ + +typedef struct kiss_fftr_state *kiss_fftr_cfg; + + +kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem); +/* + nfft must be even + + If you don't care to allocate space, use mem = lenmem = NULL +*/ + + +void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata); +/* + input timedata has nfft scalar points + output freqdata has nfft/2+1 complex points +*/ + +void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata); +/* + input freqdata has nfft/2+1 complex points + output timedata has nfft scalar points +*/ + +#define kiss_fftr_free KISS_FFT_FREE + +#ifdef __cplusplus +} +#endif +#endif diff --git a/decoders/ft8-decoder-win/ft8-constants.h b/decoders/ft8-decoder-win/ft8-constants.h new file mode 100644 index 0000000..8298025 --- /dev/null +++ b/decoders/ft8-decoder-win/ft8-constants.h @@ -0,0 +1,40 @@ +# +/* + * Copyright (C) 2010, 2011, 2012, 2013 + * Jan van Katwijk (J.vanKatwijk@gmail.com) + * Lazy Chair Programming + * + * This file is part of swradio + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __FT8_CONSTANTS__ +#define __FT8_CONSTANTS__ + +#define nrTONES 79 +#define FRAMES_PER_TONE 5 +#define nrBUFFERS (3 * nrTONES * FRAMES_PER_TONE) +#define BINWIDTH (12000.0 / 3840) + +#define FT8_M_BYTES (10) +#define FT8_M_BITS (77) +#define FT8_LDPC_BITS 174 +#define FTX_LDPC_N (174) +#define FTX_LDPC_K (91) +#define FTX_LDPC_M (83) ///< Number of LDPC checksum bits (FTX_LDPC_N - FTX_LDPC_K) +#define FTX_LDPC_N_BYTES ((FTX_LDPC_N + 7) / 8) ///< Number of whole bytes needed to store 174 bits (full message) +#define FTX_LDPC_K_BYTES ((FTX_LDPC_K + 7) / 8) ///< Number of whole bytes needed to store 91 bits (payload + CRC only) +#endif + diff --git a/decoders/ft8-decoder-win/ft8-decoder.cpp b/decoders/ft8-decoder-win/ft8-decoder.cpp new file mode 100644 index 0000000..c87aa3c --- /dev/null +++ b/decoders/ft8-decoder-win/ft8-decoder.cpp @@ -0,0 +1,472 @@ +# +/* + * Copyright (C) 2022 + * Jan van Katwijk (J.vanKatwijk@gmail.com) + * Lazy Chair Computing + * + * This file is part of the swradio + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +# +#include +#include +#include "ft8-decoder.h" +#include "radio.h" +#include "pack-handler.h" +#include +#include "radio.h" +#include "identity-dialog.h" + +#define LOG_BASE 240 + + ft8_Decoder::ft8_Decoder (RadioInterface *mr, + int32_t inRate, + RingBuffer> *buffer, + QSettings *settings) : + pskReady(false), + virtualDecoder (inRate, buffer), + myFrame (nullptr), + theProcessor (this, 20, settings) { + ft8Settings = settings; + this -> mr = mr; + setupUi (&myFrame); + myFrame. show (); + this -> inputRate = 12000; + this -> outputRate = 12000; + this -> toneLength = inputRate / 6.25; + for (int i = 0; i < nrBUFFERS; i ++) + theBuffer [i] = new float [2 * toneLength]; + window = new float [toneLength]; + inputBuffer = new std::complex [toneLength]; + inBuffer = new std::complex [toneLength / FRAMES_PER_TONE]; + fftVector_in = new kiss_fft_cpx [2 * toneLength]; + fftVector_out = new kiss_fft_cpx [2 * toneLength]; + plan = kiss_fft_alloc (2 * toneLength, false, 0, 0); + + for (int i = 0; i < toneLength; i ++) { + window [i] = (0.42 - + 0.5 * cos (2 * M_PI * (float)i / toneLength) + + 0.08 * cos (4 * M_PI * (float)i / toneLength)); + } + + memset (is_syncTable, 0, 79); + for (int i = 0; i < 7; i ++) { + is_syncTable [i] = true; + is_syncTable [36 + i] = true; + is_syncTable [72 + i] = true; + } + fillIndex = 0; + readIndex = 0; + inPointer = 0; + lineCounter = 0; + + filePointer. store (nullptr); + ft8Settings -> beginGroup ("ft8Settings"); + int val = ft8Settings -> value ("maxIterations", 20). toInt (); + maxIterations. store (val); + theProcessor. set_maxIterations (val); + iterationSelector -> setValue (val); + val = ft8Settings -> value ("width", 4000). toInt (); + spectrumWidth. store (val); + spectrumwidthSelector -> setValue (val); + ft8Settings -> endGroup (); + + connect (iterationSelector, SIGNAL (valueChanged (int)), + this, SLOT (set_maxIterations (int))); + connect (spectrumwidthSelector, SIGNAL (valueChanged (int)), + this, SLOT (set_spectrumWidth (int))); + connect (filesaveButton, SIGNAL (clicked ()), + this, SLOT (handle_filesaveButton ())); + connect (identityButton, SIGNAL (clicked ()), + this, SLOT (handle_identityButton ())); + connect (pskReporterButton, SIGNAL (clicked ()), + this, SLOT (handle_pskReporterButton ())); + + show_pskStatus (false); + teller = 0; +} + + ft8_Decoder::~ft8_Decoder () { + if (pskReady) + ReporterUninitializeSTD (); + for (int i = 0; i < nrBUFFERS; i ++) + delete [] theBuffer [i]; + delete [] window; + delete [] fftVector_in; + delete [] fftVector_out; + ft8Settings -> beginGroup ("ft8Settings"); + ft8Settings -> setValue ("maxIterations", maxIterations. load ()); + ft8Settings -> setValue ("width", spectrumWidth. load ()); + ft8Settings -> endGroup (); +} + +void ft8_Decoder::processBuffer (std::complex *buffer, + int32_t amount) { + for (int i = 0; i < amount; i ++) + process (buffer [i]); +} + +static inline +float abs (kiss_fft_cpx x) { + return x.r * x.r + x.i * x.i; +} + +// +void ft8_Decoder::process (std::complex z) { + inBuffer [inPointer ++] = z; + if (inPointer < toneLength / FRAMES_PER_TONE) + return; + int content = (FRAMES_PER_TONE - 1) * toneLength / FRAMES_PER_TONE; + int newAmount = toneLength / FRAMES_PER_TONE; + inPointer = 0; + teller ++; +// If the PSKReporter is "on", we send messages (if any) each +// 60 seconds + if (pskReady && (teller > 400)) { + teller = 0; + fprintf (stderr, "tickle\n"); + int yyy = ReporterTickleSTD (); + if (yyy < 0) { + char buffer [512]; + int yyy = ReporterGetInformationSTD (buffer, 255); + fprintf (stderr, "reporter after tickle %s\n", buffer); + } + } + +// shift the inputBuffer to left + memmove (inputBuffer, &inputBuffer [newAmount], + content * sizeof (std::complex)); +// +// copy the data that is read, into the buffer + memcpy (&inputBuffer [content], inBuffer, + newAmount * sizeof (std::complex)); +// +// prepare for fft + for (int i = 0; i < toneLength; i ++) { + fftVector_in [i]. r = real (inputBuffer [i]) * window [i]; + fftVector_in [i]. i = imag (inputBuffer [i]) * window [i]; + fftVector_in [toneLength + i]. r = 0; + fftVector_in [toneLength + i]. i = 0; + } + + kiss_fft (plan, fftVector_in, fftVector_out); +// +// compute avg strength + float sum = 0; + for (int i = 0; i < 2 * toneLength; i ++) + sum += abs (fftVector_out [i]); + sum /= (2 * toneLength); + +// +// copy the spectrum into the buffer, negative frequencies first + for (int i = 0; i < toneLength; i ++) { + float x = abs (fftVector_out [i]); + if ((x < 0) || (x > 10000000)) { + fprintf (stderr, "ellende\n"); + x = 0; + } + float x_p = 10 * log10 (0.0001 + x); + + float y = abs (fftVector_out [toneLength + i]); + if ((y < 0) || (y > 10000000)) { + fprintf (stderr, "ellende\n"); + y = 0; + } + float x_n = 10 * log10 (0.0001 + y); + + if (x_p < 0) x_p = 0; + if (x_n < 0) x_n = 0; + theBuffer [fillIndex][toneLength + i] = x_p + LOG_BASE; + theBuffer [fillIndex][i] = x_n + LOG_BASE; + } + theBuffer [fillIndex] [0] = 10 * log10 (0.0001 + sum); + + fillIndex = (fillIndex + 1) % nrBUFFERS; + lineCounter ++; + if (lineCounter < FRAMES_PER_TONE * nrTONES) { + return; + } +// +// there is a constant relationship between the fill- and read-index + readIndex = fillIndex - FRAMES_PER_TONE * nrTONES; + if (readIndex < 0) + readIndex += nrBUFFERS; + processLine (readIndex); +} +// +// we apply a costas test on selected elements in the line +// and if we are (more or less) convinced the data is recorded +// in the cache +void ft8_Decoder::processLine (int lineno) { +std::vector cache; + + int lowBin = - spectrumWidth. load () / 2 / BINWIDTH + toneLength; + int highBin = spectrumWidth. load () / 2 / BINWIDTH + toneLength; + float xxx [2 * toneLength]; + for (int bin = lowBin + 10; bin < highBin- 10; bin ++) { + float tmp = testCostas (lineno, bin); + xxx [bin] = tmp; + } + + peakFinder (xxx, lowBin, highBin, cache); + + float log174 [FT8_LDPC_BITS]; + for (auto it: cache) { +// int errors = 0; + it. value = decodeTones (lineno, it. index, log174); + theProcessor . PassOn (lineCounter, it.value, + (int)(it. index - toneLength) * BINWIDTH, log174); + } +} + +// with an FFT of 3840 over een band of 12 K, the binwidth = 3.125, +// i.e. one tone fits takes 2 bins +static +int costasPattern [] = {3, 1, 4, 0, 6, 5, 2}; + +static +float getScore (float *p8, int bin, int tone) { + int index = bin + 2 * costasPattern [tone]; + float res = 8 * (p8 [index] + p8 [index + 1]); + for (int i = -3; i < +3; i ++) + res -= p8 [index + 2 * i] + p8 [index + 2 * i];; + return res < 0 ? 0 : res; +} + +float ft8_Decoder::testCostas (int row, int bin) { +float *p8; +float score = 0; + + for (int tone = 0; tone < 7; tone ++) { + p8 = theBuffer [(row + FRAMES_PER_TONE * tone) % nrBUFFERS]; + score += getScore (p8, bin, tone); + } + + for (int tone = 0; tone < 7; tone ++) { + p8 = theBuffer [(row + FRAMES_PER_TONE * (36 + tone)) % nrBUFFERS]; + score += getScore (p8, bin, tone); + } + + for (int tone = 0; tone < 7; tone ++) { + p8 = theBuffer [(row + FRAMES_PER_TONE * (72 + tone)) % nrBUFFERS]; + score += getScore (p8, bin, tone); + } + + return score / 21; +} +// +// we compute the "strength" of the tone bins as relation beetween +// the bin with the most energy compared to the average of the +// bins for each tone, and take the average over the tones. +float ft8_Decoder::decodeTones (int row, int bin, float *log174) { +int filler = 0; +float strength = 0; + + for (int i = 0; i < nrTONES; i ++) { + if (is_syncTable [i]) + continue; + + int theRow = (row + FRAMES_PER_TONE * i) % nrBUFFERS; + float f = decodeTone (theRow, bin, &log174 [filler]); +// strength += f - 10 * log10 (8192); + strength += f - theBuffer [theRow][0]; + filler += 3; + } + normalize_logl (log174); + return strength / (79 - 21); +} + +static inline +float max2 (float a, float b) { + return (a >= b) ? a : b; +} + +static inline +float max4 (float a, float b, float c, float d) { + return max2 (max2 (a, b), max2 (c, d)); +} + +static +const uint8_t FT8_Gray_map [8] = {0, 1, 3, 2, 5, 6, 4, 7}; +float ft8_Decoder::decodeTone (int row, int bin, float *logl) { +float s1 [8]; +float s2 [8]; +float strength = 0; + + for (int i = 0; i < 8; i ++) { + float a1 = theBuffer [row][bin + 2 * i]; + float a2 = theBuffer [row][bin + 2 * i + 1]; + if ((a1 + a2) / 2 > strength) + strength = (a1 + a2) / 2; + s1 [i] = (a1 + a2) / 2; + } + + for (int i = 0; i < 8; i ++) + s2 [i] = s1 [FT8_Gray_map [i]]; + + logl [0] = max4 (s2 [4], s2 [5], s2 [6], s2 [7]) - + max4 (s2 [0], s2 [1], s2 [2], s2 [3]); + logl [1] = max4 (s2 [2], s2 [3], s2 [6], s2 [7]) - + max4 (s2 [0], s2 [1], s2 [4], s2 [5]); + logl [2] = max4 (s2 [1], s2 [3], s2 [5], s2 [7]) - + max4 (s2 [0], s2 [2], s2 [4], s2 [6]); + + return strength - LOG_BASE; +} + +void ft8_Decoder::normalize_logl (float *log174) { +float sum = 0; +float sum2 = 0; +// Compute the variance of log174 + + for (int i = 0; i < 174; ++i) { + sum += log174 [i]; + sum2 += log174 [i] * log174 [i]; + } + + float inv_n = 1.0f / FT8_LDPC_BITS; + float variance = (sum2 - (sum * sum * inv_n)) * inv_n; + +// Normalize log174 distribution and scale it with +// experimentally found coefficient + float norm_factor = sqrtf (48.0f / variance); +// float norm_factor = sqrtf(24.0f / variance); + for (int i = 0; i < FT8_LDPC_BITS; ++i) { + log174 [i] *= norm_factor; + } +} + +// +void ft8_Decoder::set_maxIterations (int n) { + maxIterations. store (n); + theProcessor. set_maxIterations (n); +} + +void ft8_Decoder::showText (const QStringList &resList) { + model. clear (); + for (auto res : resList) + model. appendRow (new QStandardItem (res)); + display -> setModel (&model); +} + +void ft8_Decoder::set_spectrumWidth (int n) { + spectrumWidth. store (n); +} + +void ft8_Decoder::printLine (const QString &s) { + if (theResults. size () >= 50) + theResults. pop_front (); + theResults += s; + + if (filePointer. load () != nullptr) + fprintf (filePointer, "%s\n", s. toUtf8 (). data ()); + fprintf (stderr, "%s\n", s. toLatin1 (). data ()); + showText (theResults); +} + +static inline +float sum (float *V, int index) { + return V [index - 1] + V [index] + V [index + 1]; +} + +void ft8_Decoder::peakFinder (float *V, int begin, int end, + std::vector &cache) { +costasValue E; +float workVector [2 * toneLength]; + + for (int i = begin; i < end; i ++) + workVector [i] = sum (V, i); + + for (int index = begin + 5; index < end - 5; index ++) { + if ((1.1 * workVector [index - 5] < workVector [index]) && + (1.1 * workVector [index + 5] < workVector [index]) ) + { + E. index = index; + E. value = theBuffer [readIndex] [index]; + cache. push_back (E); + E. index = index + 1; + E. value = theBuffer [readIndex] [index + 1]; + cache. push_back (E); + index += 10; + } + } +} + +void ft8_Decoder::handle_filesaveButton () { + if (filePointer. load () != nullptr) { + fclose (filePointer); + filesaveButton -> setText ("save file"); + filePointer. store (nullptr); + } + + QString saveName = + QFileDialog::getSaveFileName (nullptr, + tr ("save file as .."), + QDir::homePath (), + tr ("Images (*.txt)")); + if (saveName == "") + return; + + filePointer. store (fopen (saveName. toUtf8 (). data (), "w")); + if (filePointer. load () == nullptr) + return; + + filesaveButton -> setText ("saving"); +} + +int ft8_Decoder::getVFO () { + return mr -> tunedFrequency (); +} + +bool ft8_Decoder::pskReporterReady () { + return pskReady; +} + +void ft8_Decoder::handle_pskReporterButton () { + if (!pskReady) { + int status = ReporterInitializeSTD ("report.pskreporter.info", + "4739"); + char Rbuffer [256]; + ReporterGetInformationSTD (Rbuffer, 256); + if (status != 0) + fprintf (stderr, "report: %s\n", Rbuffer); + pskReady = status == 0; + show_pskStatus (status == 0); + return; + } + + if (pskReady) { + ReporterUninitializeSTD (); + pskReady = false; + show_pskStatus (false); + } +} + +void ft8_Decoder::handle_identityButton () { +identityDialog Identity (ft8Settings); + Identity. QDialog::exec (); +} + + +void ft8_Decoder::show_pskStatus (bool b) { + if (b) + pskStatus -> setStyleSheet ("QLabel {background-color : green}"); + else + pskStatus -> setStyleSheet ("QLabel {background-color : red}"); +} + +void ft8_Decoder::print_statistics () { +} + diff --git a/decoders/ft8-decoder-win/ft8-decoder.h b/decoders/ft8-decoder-win/ft8-decoder.h new file mode 100644 index 0000000..c40371c --- /dev/null +++ b/decoders/ft8-decoder-win/ft8-decoder.h @@ -0,0 +1,120 @@ +# +/* + * Copyright (C) 2022 + * Jan van Katwijk (J.vanKatwijk@gmail.com) + * Lazy Chair Computing + * + * This file is part of the swradio + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __FT8_DECODER__ +#define __FT8_DECODER__ +#include "radio-constants.h" +#include +#include +#include +#include +#include +#include "virtual-decoder.h" +#include "ui_ft8-decoder.h" +#include "kiss_fft.h" +#include "ft8-constants.h" +#include "ldpc.h" +#include "ft8-processor.h" +#include "PSKReporter.h" + +typedef struct { + int index; + float value; +} costasValue; + +class RadioInterface; + +class ft8_Decoder: public virtualDecoder, public Ui_ft8_widget { +Q_OBJECT +public: + ft8_Decoder (RadioInterface *, + int32_t, + RingBuffer> *, + QSettings *); + ~ft8_Decoder (void); + void processBuffer (std::complex *, int32_t); + void process (std::complex z); + int getVFO (); + bool pskReporterReady (); + +private: + bool pskReady; + QFrame myFrame; + ldpc ldpcHandler; + ft8_processor theProcessor; + QSettings *ft8Settings; + RadioInterface *mr; + int32_t inputRate; + int32_t outputRate; + int32_t samplesperSymbol; + std::atomic filePointer; + void peakFinder (float *V, int begin, int end, + std::vector &cache); + + void processLine (int); + float testCostas (int, int); + float decodeTones (int row, int bin, + float *log174); + float decodeTone (int row, int bin, float *logl); + void normalize_logl (float *); + int addCache (float, + int, std::vector &); + + kiss_fft_cfg plan; + kiss_fft_cpx *fftVector_in; + kiss_fft_cpx *fftVector_out; + float *window; + + int toneLength; + int messageLength; + int costasLength; + float *theBuffer [nrBUFFERS]; + std::complex *inputBuffer; + std::complex *inBuffer; + int fillIndex; + int readIndex; + int inPointer; + int lineCounter; + std::atomic maxIterations; + std::atomic cacheDepth; + std::atomic spectrumWidth; + bool is_syncTable [nrTONES]; + + QStandardItemModel model; + void showText (const QStringList &); + QStringList theResults; + int teller; + void print_statistics (); +public slots: + void printLine (const QString &); + void show_pskStatus (bool); +private slots: + void set_maxIterations (int); + void set_spectrumWidth (int); + void handle_filesaveButton (); + void handle_identityButton (); + void handle_pskReporterButton (); +}; + +#endif + diff --git a/decoders/ft8-decoder-win/ft8-decoder.ui b/decoders/ft8-decoder-win/ft8-decoder.ui new file mode 100644 index 0000000..3b090d2 --- /dev/null +++ b/decoders/ft8-decoder-win/ft8-decoder.ui @@ -0,0 +1,99 @@ + + + ft8_widget + + + + 0 + 0 + 445 + 305 + + + + ft8 decoder + + + + + + + + <html><head/><body><p>Select the maximum number of iterations for the ldpc decoder</p><p><br/></p></body></html> + + + 5 + + + 83 + + + 1 + + + + + + + iterations + + + + + + + 1000 + + + 5500 + + + 100 + + + + + + + searchwidth + + + + + + + set identity + + + + + + + start/stop pskReport + + + + + + + psk status + + + + + + + save button + + + + + + + + + + + + + diff --git a/decoders/ft8-decoder-win/ft8-processor.cpp b/decoders/ft8-decoder-win/ft8-processor.cpp new file mode 100644 index 0000000..4c7f13e --- /dev/null +++ b/decoders/ft8-decoder-win/ft8-processor.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2022 + * Jan van Katwijk (J.vanKatwijk@gmail.com) + * Lazy Chair Computing + * + * This file is part of the swradio + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ft8-constants.h" +#include "ft8-decoder.h" +#include "ldpc.h" +#include "pack-handler.h" +#include "ft8-processor.h" +#include "PSKReporter.h" +#include + + ft8_processor::ft8_processor (ft8_Decoder *theDecoder, + int maxIterations, + QSettings *ft8Settings): + theCache (30), + freeSlots (nrBlocks) { + + this -> theDecoder = theDecoder; + this -> maxIterations = maxIterations; + this -> blockToRead = 0; + this -> blockToWrite = 0; + ft8Settings -> beginGroup ("ft8Settings"); + this -> homeCall = + ft8Settings -> value ("homeCall", "NL14157"). toString (). toStdString (); + this -> homeGrid = + ft8Settings -> value ("homeGrid", "JO22fa"). toString (). toStdString (); +// this -> programName = "swRadio-9"; +// this -> antenna = +// settings -> value ("antenna", "home built loop"). toString (). toStdString (); + ft8Settings -> endGroup (); + + if ((homeCall != "") && (homeGrid != "")) + localAdif = "STATION_CALLSIGN," + homeCall + + ",my_gridsquare," + homeGrid + + ",programid,swRadio-9"; + else + localAdif = ""; + + running. store (false); + connect (this, + SIGNAL (printLine (const QString &)), + theDecoder, + SLOT (printLine (const QString &))); + connect (this, SIGNAL (show_pskStatus (bool)), + theDecoder, SLOT (show_pskStatus (bool))); + threadHandle = std::thread (&ft8_processor::run, this); +} + + ft8_processor::~ft8_processor () { + if (running. load ()) { + running. store (false); + sleep (1); + threadHandle. join (); + } +} +// +// entry for processing +void ft8_processor::PassOn (int lineno, + float refVal, int frequency, float *log174) { + while (!freeSlots. tryAcquire (200)) + if (!running. load ()) + return; + theBuffer [blockToWrite]. lineno = lineno; + theBuffer [blockToWrite]. value = refVal; + theBuffer [blockToWrite]. frequency = frequency; + for (int i = 0; i < FT8_LDPC_BITS; i ++) + theBuffer [blockToWrite]. log174 [i] = log174 [i]; + blockToWrite = (blockToWrite + 1 ) % nrBlocks;; + usedSlots. Release (); +} + +void ft8_processor::set_maxIterations (int n) { + maxIterations. store (n); +} + +void insertString (char *target, int pos, const QString &s) { + for (int i = 0; s.toLatin1 (). data () [i] != 0; i ++) + target [pos + i] = s. toLatin1 (). data () [i]; +} + +void insertNumber (char *target, int pos, int number) { +QString s = QString::number (number); + insertString (target, pos, s); +} + +void insert_2_Number (char *target, int pos, int number) { +QString s; + if ((number >= 100) || (number < 0)) { + insertString (target, pos, QString::number (number)); + return; + } + if (number >= 10) { + s. push_back ('0' + number / 10); + s. push_back ('0' + number % 10); + insertString (target, pos, s); + return; + } + s. push_back (' '); + s. push_back ('0' + number); + insertString (target, pos, s); +} + +QString makeLine (QString time, + int value, int freq, + QString message) { +char res [256]; + +static +int posTable [] = {0, 20, 30, 45, 75}; + + for (int i = 0; i < 256; i ++) + res [i] = ' '; + + insertString (res, posTable [0], time); + insert_2_Number (res, posTable [1], value > 100 ? 101 : value); + insertNumber (res, posTable [2], freq); + insertString (res, posTable [3], message); + res [posTable [4]] = 0; + return QString (res); +} + +void ft8_processor::run () { +uint8_t ldpcBits [FT8_LDPC_BITS]; +ldpc ldpcHandler; +int errors; + + usleep (1000000); + + fprintf (stderr, "The processor gaat draaien\n"); + running. store (true); + while (running. load()) { + while (!usedSlots. tryAcquire (200)) + if (!running. load ()) + return; +// ldpcHandler. bp_decode (theBuffer [blockToRead]. log174, +// maxIterations. load (), +// ldpcBits, &errors); + ldpcHandler. ldpc_decode (theBuffer [blockToRead]. log174, + maxIterations. load (), + ldpcBits, &errors); + +// Release the buffer now + freeSlots. Release (); + blockToRead = (blockToRead + 1) % (nrBlocks); +// + if (errors != 0) + continue; +// the check bits are to be moved from 77 up to 82 up +// and 77 .. 82 should be 0 + for (int i = 96; i > 82; i --) + ldpcBits [i] = ldpcBits [i - 14]; + + for (int i = 77; i < 82; i ++) + ldpcBits [i] = 0; + + if (check_crc_bits (ldpcBits, 96)) { +// fprintf (stderr, "crc OK\n"); +// crc is correct, unpack the message + QString res = unpackHandler. unpackMessage (ldpcBits); + if (res != "") { + showLine (theBuffer [blockToRead]. lineno, + theBuffer [blockToRead]. value, + theBuffer [blockToRead]. frequency, + res); + if (theDecoder -> pskReporterReady () && + (localAdif != "")) { + QStringList call = unpackHandler. extractCall (ldpcBits); + std::string adifString; + int xxx = 0; +// +// if the return > 0 then we create an adif string + if (call.size () > 0) { + adifString = "call," + call.at(0). toStdString () + + ",mode,FT8,"; + if (call. size () == 2) + adifString += "gridsquare," + call.at (1). toStdString () + ","; + adifString += std::string ("freq,") + + std::to_string(theBuffer[blockToRead].frequency + theDecoder -> getVFO ()) + ","; + adifString += "snr," + + std::to_string(theBuffer[blockToRead].value); +// + fprintf (stderr, "localadif = %s\n", + localAdif. c_str ()); + fprintf (stderr, "adif = %s\n", adifString. c_str ()); + xxx = ReporterSeenCallsignSTD ( + adifString. c_str (), + localAdif. c_str (), + REPORTER_SOURCE_AUTOMATIC); + if (xxx != 0) { + char buffer [512]; + int yyy = ReporterGetInformationSTD (buffer, 255); + fprintf (stderr, "reporter after tickle %s\n", buffer); + } + } + + } + } + } +// prepare for the next round +// freeSlots. Release (); +// blockToRead = (blockToRead + 1) % (nrBlocks); + } +} + +void ft8_processor::print_statistics () { +} + +void ft8_processor::showLine (int line, int val, + int freq, const QString &res) { + if (theCache. update (val, freq, res. toStdString ())) + return; + + int currentFreq = theDecoder -> getVFO (); + time_t rawTime; + struct tm *timeinfo; + char buffer [100]; + time (&rawTime); + timeinfo = localtime (&rawTime); + strftime (buffer, 80, "%I:%M:%S%p", timeinfo); + QString s = QString (buffer); + QString result = makeLine (s, val, currentFreq + freq, res); + printLine (result); +} + +static +uint8_t crcPolynome [] = + {1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1}; // MSB .. LSB +bool ft8_processor::check_crc_bits (uint8_t *in, int nrBits) { +uint8_t b [14]; +int16_t Sum = 0; + + memset (b, 0, 14); + + for (int i = nrBits - 14; i < nrBits; i ++) + in [i] = ~in [i]; + + for (int i = 0; i < nrBits; i++) { + if ((b [0] ^ in [i]) == 1) { + for (int f = 0; f < 13; f++) + b [f] = b [f + 1]; + b [13] = 0; + for (int f = 0; f < 14; f ++) + b [f] ^= crcPolynome [f]; + } + else { + memmove (&b [0], &b[1], sizeof (uint8_t ) * 13); // Shift + b [13] = 0; + } + } + for (int i = 0; i < 14; i ++) + Sum += b [i]; + return Sum == 0; +} + diff --git a/decoders/ft8-decoder-win/ft8-processor.h b/decoders/ft8-decoder-win/ft8-processor.h new file mode 100644 index 0000000..7d0897e --- /dev/null +++ b/decoders/ft8-decoder-win/ft8-processor.h @@ -0,0 +1,89 @@ +# +/* + * Copyright (C) 2022 + * Jan van Katwijk (J.vanKatwijk@gmail.com) + * Lazy Chair Computing + * + * This file is part of the swradio + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __FT8_PROCESSOR__ +#define __FT8_PROCESSOR__ +#include +#include +#include +#include +#include "semaphore.h" +#include +#include "ft8-constants.h" +#include "pack-handler.h" +#include "dl-cache.h" +#include "PSKReporter.h" + +class ft8_Decoder; +class reporterWriter; +#define nrBlocks 100 + +class ft8_processor: public QObject { +Q_OBJECT +public: + ft8_processor (ft8_Decoder *, int, QSettings *); + ~ft8_processor (); + + void PassOn (int, float, int, float *); + void set_maxIterations (int); + +private: + ft8_Decoder *theDecoder; + QSettings *ft8Settings; + packHandler unpackHandler; + reporterWriter *theWriter; + void run (); + bool check_crc_bits (uint8_t *message, int nrBits); + void showLine (int, int, int, const QString &); + dlCache theCache; + struct { + int lineno; + int value; + int frequency; + float log174 [174]; + } theBuffer [nrBlocks]; + + std::string homeCall; + std::string homeGrid; + std::string localAdif; + + Semaphore freeSlots; + Semaphore usedSlots; + + std::atomic running; + + std::atomic maxIterations; + int amount; + int blockToRead; + int blockToWrite; + std::thread threadHandle; + uint8_t a91 [FT8_M_BYTES]; + + void print_statistics (); +signals: + void printLine (const QString &); + void show_pskStatus (bool); +}; +#endif + diff --git a/decoders/ft8-decoder-win/hashHandler.cpp b/decoders/ft8-decoder-win/hashHandler.cpp new file mode 100644 index 0000000..48b0c46 --- /dev/null +++ b/decoders/ft8-decoder-win/hashHandler.cpp @@ -0,0 +1,86 @@ +# +/* + * Copyright (C) 2010, 2011, 2012, 2013 + * Jan van Katwijk (J.vanKatwijk@gmail.com) + * Lazy Chair Computing + * + * This file is part of swradio + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "hashHandler.h" +#include + + hashHandler::hashHandler (const std::string &f) { + this -> saveFile = f; + reader (f); +} + + hashHandler::~hashHandler () { + writer (this -> saveFile); +} + +void hashHandler::add_hash (uint32_t key, + const QString &s) { + for (int i = 0; i < hashTable. size (); i++) + if (hashTable.at (i). key == key) + return; + hashElement h; + h. key = key; + h. value = s; + hashTable. push_back (h); +// fprintf (stderr, "adding %X %s\n", +// key, s. toLatin1 (). data ()); +} + +QString hashHandler::lookup (uint32_t key) { + for (int i = 0; i < hashTable. size (); i ++) + if (hashTable. at (i). key == key) + return hashTable. at (i). value; +// fprintf (stderr, "key %X not found\n", key); + return "<....>"; +} + +void hashHandler::reader (const std::string &s) { +char line [256]; +hashElement he; +FILE *f = fopen (s. c_str (), "r"); + if (f == nullptr) + return; + + while (!feof (f)) { + char s [255]; + char * t =fgets (line, 256, f); + if (t == nullptr) + break; + sscanf (line, "<%X:%s>", &he. key, s); + he. value = QString (s); + hashTable. push_back (he); + } + fclose (f); +} + +void hashHandler::writer (const std::string &s) { +FILE *f = fopen (s. c_str (), "w"); + if (f == nullptr) + return; + + for (int i = 0; i < hashTable. size (); i ++) + fprintf (f, "<%X:%s>\n", hashTable. at (i). key, + hashTable. at (i). value. toLatin1 (). data ()); + fclose (f); +} + diff --git a/decoders/ft8-decoder-win/hashHandler.h b/decoders/ft8-decoder-win/hashHandler.h new file mode 100644 index 0000000..d14a4ea --- /dev/null +++ b/decoders/ft8-decoder-win/hashHandler.h @@ -0,0 +1,49 @@ +# +/* + * Copyright (C) 2010, 2011, 2012, 2013 + * Jan van Katwijk (J.vanKatwijk@gmail.com) + * Lazy Chair Computing + * + * This file is part of swradio + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __HASH_HANDLER__ +#define __HASH_HANDLER__ + +#include +#include +#include + +typedef struct hashElement { + uint32_t key; + QString value; +}; + +class hashHandler { +public: + hashHandler (const std::string &s); + ~hashHandler (); + void add_hash (uint32_t key, const QString &value); + QString lookup (uint32_t key); +private: + + std::string saveFile; + void reader (const std::string &s); + void writer (const std::string &s); + std::vector hashTable; +}; + +#endif diff --git a/decoders/ft8-decoder-win/hashcodes.f90 b/decoders/ft8-decoder-win/hashcodes.f90 new file mode 100644 index 0000000..0c0628e --- /dev/null +++ b/decoders/ft8-decoder-win/hashcodes.f90 @@ -0,0 +1,34 @@ +program hashcodes + + parameter (NTOKENS=2063592) + integer*8 nprime,n8(3) + integer nbits(3),ihash(3) + character*11 callsign + character*38 c + data c/' 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/'/ + data nprime/47055833459_8/,nbits/10,12,22/ + + nargs=iargc() + if(nargs.ne.1) then + print*,'Usage: hashcodes ' + print*,'Examples: hashcodes PJ4/K1ABC' + print*,' hashcodes YW18FIFA' + go to 999 + endif + call getarg(1,callsign) + callsign=adjustl(callsign) + + do k=1,3 + n8(k)=0 + do i=1,11 + j=index(c,callsign(i:i)) - 1 + n8(k)=38*n8(k) + j + enddo + ihash(k)=ishft(nprime*n8(k),nbits(k)-64) + enddo + ih22_biased=ihash(3) + NTOKENS + write(*,1000) callsign,ihash,ih22_biased +1000 format('Callsign',9x,'h10',7x,'h12',7x,'h22'/41('-')/ & + a11,i9,2i10,/'Biased for storage in c28:',i14) + +999 end program hashcodes diff --git a/decoders/ft8-decoder-win/identity-dialog.cpp b/decoders/ft8-decoder-win/identity-dialog.cpp new file mode 100644 index 0000000..d3f26f3 --- /dev/null +++ b/decoders/ft8-decoder-win/identity-dialog.cpp @@ -0,0 +1,68 @@ + +#include "identity-dialog.h" + + +identityDialog::identityDialog (QSettings *ft8Settings, QWidget *parent): + QDialog (parent) { + this -> ft8Settings = ft8Settings; + callsignLabel = new QLabel (tr("callsign")); + gridLabel = new QLabel (tr("grid")); + antennaLabel = new QLabel (tr("antenna")); + + callsign = new QLineEdit; + homeGrid = new QLineEdit; + antenna = new QLineEdit; + + buttonBox = new QDialogButtonBox (QDialogButtonBox::Ok | + QDialogButtonBox::Cancel); + connect (buttonBox, &QDialogButtonBox::accepted, + this, &identityDialog::verify); + connect (buttonBox, &QDialogButtonBox::rejected, + this, &identityDialog::reject); + QGridLayout *mainLayout = new QGridLayout; + mainLayout -> addWidget (callsignLabel, 0, 0); + mainLayout -> addWidget (callsign, 0, 1); + mainLayout -> addWidget (gridLabel, 1, 0); + mainLayout -> addWidget (homeGrid, 1, 1); + mainLayout -> addWidget (antennaLabel, 2, 0); + mainLayout -> addWidget (antenna, 2, 1); + mainLayout -> addWidget (buttonBox, 3, 0); + setLayout (mainLayout); + setWindowTitle ("your data"); + show ();; +} + +identityDialog::~identityDialog () {} + +void identityDialog::verify () { + if (!callsign -> text (). isEmpty () && + !homeGrid -> text (). isEmpty () && + !antenna -> text (). isEmpty ()) { + ft8Settings -> beginGroup ("ft8Settings"); + ft8Settings -> setValue ("homeCall", callsign -> text ()); + ft8Settings -> setValue ("homeGrid", homeGrid -> text ()); + ft8Settings -> setValue ("antenna", antenna -> text ()); + ft8Settings -> endGroup (); + accept (); + return; + } + + QMessageBox::StandardButton answer; + answer = QMessageBox::warning (this, tr ("incomplete form"), + tr ("The form is not filled in completely\n" + "Do you want to discard it?"), + QMessageBox::Yes |QMessageBox::No); + if (answer == QMessageBox::Yes) + reject (); +} + +void identityDialog::reject () { + accept (); +} + + + + + + + diff --git a/decoders/ft8-decoder-win/identity-dialog.h b/decoders/ft8-decoder-win/identity-dialog.h new file mode 100644 index 0000000..b1d3828 --- /dev/null +++ b/decoders/ft8-decoder-win/identity-dialog.h @@ -0,0 +1,32 @@ + +#ifndef __IDENTITY_SELECTOR__ +#define __IDENTITY_SELECTOR__ + +#include +#include +#include +#include +#include +#include +#include + +class identityDialog: public QDialog { +public: + identityDialog (QSettings *, QWidget *parent = nullptr); + ~identityDialog (); +private slots: + QSettings *ft8Settings; + void verify (); + void reject (); + + QLabel *callsignLabel; + QLineEdit *callsign; + QLabel *gridLabel; + QLineEdit *homeGrid; + QLabel *antennaLabel; + QLineEdit *antenna; + QDialogButtonBox *buttonBox; + +}; +#endif + diff --git a/decoders/ft8-decoder-win/ldpc.cpp b/decoders/ft8-decoder-win/ldpc.cpp new file mode 100644 index 0000000..52aa246 --- /dev/null +++ b/decoders/ft8-decoder-win/ldpc.cpp @@ -0,0 +1,498 @@ +# +/* + * Copyright (C) 2022 + * Karlis Goba + * YL33G + * + * This file is imported from the ft8 library of Karlis Goba + * and the rights obviously remain to him + * The file is included in the ft8 decoder of swradio-8 + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// +// LDPC decoder for FT8. +// +// given a 174-bit codeword as an array of log-likelihood of zero, +// return a 174-bit corrected codeword, or zero-length array. +// last 87 bits are the (systematic) plain-text. +// this is an implementation of the sum-product algorithm +// from Sarah Johnson's Iterative Error Correction book. +// codeword [i] = log (P (x=0) / P(x=1) ) +// + +#include "ldpc.h" + +#include +#include +#include +#include + +// Each row describes one LDPC parity check. +// Each number is an index into the codeword (1-origin). +// The codeword bits mentioned in each row must XOR to zero. +const uint8_t kFTX_LDPC_Nm[FTX_LDPC_M][7] = { + { 4, 31, 59, 91, 92, 96, 153 }, + { 5, 32, 60, 93, 115, 146, 0 }, + { 6, 24, 61, 94, 122, 151, 0 }, + { 7, 33, 62, 95, 96, 143, 0 }, + { 8, 25, 63, 83, 93, 96, 148 }, + { 6, 32, 64, 97, 126, 138, 0 }, + { 5, 34, 65, 78, 98, 107, 154 }, + { 9, 35, 66, 99, 139, 146, 0 }, + { 10, 36, 67, 100, 107, 126, 0 }, + { 11, 37, 67, 87, 101, 139, 158 }, + { 12, 38, 68, 102, 105, 155, 0 }, + { 13, 39, 69, 103, 149, 162, 0 }, + { 8, 40, 70, 82, 104, 114, 145 }, + { 14, 41, 71, 88, 102, 123, 156 }, + { 15, 42, 59, 106, 123, 159, 0 }, + { 1, 33, 72, 106, 107, 157, 0 }, + { 16, 43, 73, 108, 141, 160, 0 }, + { 17, 37, 74, 81, 109, 131, 154 }, + { 11, 44, 75, 110, 121, 166, 0 }, + { 45, 55, 64, 111, 130, 161, 173 }, + { 8, 46, 71, 112, 119, 166, 0 }, + { 18, 36, 76, 89, 113, 114, 143 }, + { 19, 38, 77, 104, 116, 163, 0 }, + { 20, 47, 70, 92, 138, 165, 0 }, + { 2, 48, 74, 113, 128, 160, 0 }, + { 21, 45, 78, 83, 117, 121, 151 }, + { 22, 47, 58, 118, 127, 164, 0 }, + { 16, 39, 62, 112, 134, 158, 0 }, + { 23, 43, 79, 120, 131, 145, 0 }, + { 19, 35, 59, 73, 110, 125, 161 }, + { 20, 36, 63, 94, 136, 161, 0 }, + { 14, 31, 79, 98, 132, 164, 0 }, + { 3, 44, 80, 124, 127, 169, 0 }, + { 19, 46, 81, 117, 135, 167, 0 }, + { 7, 49, 58, 90, 100, 105, 168 }, + { 12, 50, 61, 118, 119, 144, 0 }, + { 13, 51, 64, 114, 118, 157, 0 }, + { 24, 52, 76, 129, 148, 149, 0 }, + { 25, 53, 69, 90, 101, 130, 156 }, + { 20, 46, 65, 80, 120, 140, 170 }, + { 21, 54, 77, 100, 140, 171, 0 }, + { 35, 82, 133, 142, 171, 174, 0 }, + { 14, 30, 83, 113, 125, 170, 0 }, + { 4, 29, 68, 120, 134, 173, 0 }, + { 1, 4, 52, 57, 86, 136, 152 }, + { 26, 51, 56, 91, 122, 137, 168 }, + { 52, 84, 110, 115, 145, 168, 0 }, + { 7, 50, 81, 99, 132, 173, 0 }, + { 23, 55, 67, 95, 172, 174, 0 }, + { 26, 41, 77, 109, 141, 148, 0 }, + { 2, 27, 41, 61, 62, 115, 133 }, + { 27, 40, 56, 124, 125, 126, 0 }, + { 18, 49, 55, 124, 141, 167, 0 }, + { 6, 33, 85, 108, 116, 156, 0 }, + { 28, 48, 70, 85, 105, 129, 158 }, + { 9, 54, 63, 131, 147, 155, 0 }, + { 22, 53, 68, 109, 121, 174, 0 }, + { 3, 13, 48, 78, 95, 123, 0 }, + { 31, 69, 133, 150, 155, 169, 0 }, + { 12, 43, 66, 89, 97, 135, 159 }, + { 5, 39, 75, 102, 136, 167, 0 }, + { 2, 54, 86, 101, 135, 164, 0 }, + { 15, 56, 87, 108, 119, 171, 0 }, + { 10, 44, 82, 91, 111, 144, 149 }, + { 23, 34, 71, 94, 127, 153, 0 }, + { 11, 49, 88, 92, 142, 157, 0 }, + { 29, 34, 87, 97, 147, 162, 0 }, + { 30, 50, 60, 86, 137, 142, 162 }, + { 10, 53, 66, 84, 112, 128, 165 }, + { 22, 57, 85, 93, 140, 159, 0 }, + { 28, 32, 72, 103, 132, 166, 0 }, + { 28, 29, 84, 88, 117, 143, 150 }, + { 1, 26, 45, 80, 128, 147, 0 }, + { 17, 27, 89, 103, 116, 153, 0 }, + { 51, 57, 98, 163, 165, 172, 0 }, + { 21, 37, 73, 138, 152, 169, 0 }, + { 16, 47, 76, 130, 137, 154, 0 }, + { 3, 24, 30, 72, 104, 139, 0 }, + { 9, 40, 90, 106, 134, 151, 0 }, + { 15, 58, 60, 74, 111, 150, 163 }, + { 18, 42, 79, 144, 146, 152, 0 }, + { 25, 38, 65, 99, 122, 160, 0 }, + { 17, 42, 75, 129, 170, 172, 0 } +}; + +// Each row corresponds to a codeword bit. +// The numbers indicate which three LDPC parity checks (rows in Nm) refer to the codeword bit. +// 1-origin. +const uint8_t kFTX_LDPC_Mn[FT8_LDPC_BITS][3] = { + { 16, 45, 73 }, + { 25, 51, 62 }, + { 33, 58, 78 }, + { 1, 44, 45 }, + { 2, 7, 61 }, + { 3, 6, 54 }, + { 4, 35, 48 }, + { 5, 13, 21 }, + { 8, 56, 79 }, + { 9, 64, 69 }, + { 10, 19, 66 }, + { 11, 36, 60 }, + { 12, 37, 58 }, + { 14, 32, 43 }, + { 15, 63, 80 }, + { 17, 28, 77 }, + { 18, 74, 83 }, + { 22, 53, 81 }, + { 23, 30, 34 }, + { 24, 31, 40 }, + { 26, 41, 76 }, + { 27, 57, 70 }, + { 29, 49, 65 }, + { 3, 38, 78 }, + { 5, 39, 82 }, + { 46, 50, 73 }, + { 51, 52, 74 }, + { 55, 71, 72 }, + { 44, 67, 72 }, + { 43, 68, 78 }, + { 1, 32, 59 }, + { 2, 6, 71 }, + { 4, 16, 54 }, + { 7, 65, 67 }, + { 8, 30, 42 }, + { 9, 22, 31 }, + { 10, 18, 76 }, + { 11, 23, 82 }, + { 12, 28, 61 }, + { 13, 52, 79 }, + { 14, 50, 51 }, + { 15, 81, 83 }, + { 17, 29, 60 }, + { 19, 33, 64 }, + { 20, 26, 73 }, + { 21, 34, 40 }, + { 24, 27, 77 }, + { 25, 55, 58 }, + { 35, 53, 66 }, + { 36, 48, 68 }, + { 37, 46, 75 }, + { 38, 45, 47 }, + { 39, 57, 69 }, + { 41, 56, 62 }, + { 20, 49, 53 }, + { 46, 52, 63 }, + { 45, 70, 75 }, + { 27, 35, 80 }, + { 1, 15, 30 }, + { 2, 68, 80 }, + { 3, 36, 51 }, + { 4, 28, 51 }, + { 5, 31, 56 }, + { 6, 20, 37 }, + { 7, 40, 82 }, + { 8, 60, 69 }, + { 9, 10, 49 }, + { 11, 44, 57 }, + { 12, 39, 59 }, + { 13, 24, 55 }, + { 14, 21, 65 }, + { 16, 71, 78 }, + { 17, 30, 76 }, + { 18, 25, 80 }, + { 19, 61, 83 }, + { 22, 38, 77 }, + { 23, 41, 50 }, + { 7, 26, 58 }, + { 29, 32, 81 }, + { 33, 40, 73 }, + { 18, 34, 48 }, + { 13, 42, 64 }, + { 5, 26, 43 }, + { 47, 69, 72 }, + { 54, 55, 70 }, + { 45, 62, 68 }, + { 10, 63, 67 }, + { 14, 66, 72 }, + { 22, 60, 74 }, + { 35, 39, 79 }, + { 1, 46, 64 }, + { 1, 24, 66 }, + { 2, 5, 70 }, + { 3, 31, 65 }, + { 4, 49, 58 }, + { 1, 4, 5 }, + { 6, 60, 67 }, + { 7, 32, 75 }, + { 8, 48, 82 }, + { 9, 35, 41 }, + { 10, 39, 62 }, + { 11, 14, 61 }, + { 12, 71, 74 }, + { 13, 23, 78 }, + { 11, 35, 55 }, + { 15, 16, 79 }, + { 7, 9, 16 }, + { 17, 54, 63 }, + { 18, 50, 57 }, + { 19, 30, 47 }, + { 20, 64, 80 }, + { 21, 28, 69 }, + { 22, 25, 43 }, + { 13, 22, 37 }, + { 2, 47, 51 }, + { 23, 54, 74 }, + { 26, 34, 72 }, + { 27, 36, 37 }, + { 21, 36, 63 }, + { 29, 40, 44 }, + { 19, 26, 57 }, + { 3, 46, 82 }, + { 14, 15, 58 }, + { 33, 52, 53 }, + { 30, 43, 52 }, + { 6, 9, 52 }, + { 27, 33, 65 }, + { 25, 69, 73 }, + { 38, 55, 83 }, + { 20, 39, 77 }, + { 18, 29, 56 }, + { 32, 48, 71 }, + { 42, 51, 59 }, + { 28, 44, 79 }, + { 34, 60, 62 }, + { 31, 45, 61 }, + { 46, 68, 77 }, + { 6, 24, 76 }, + { 8, 10, 78 }, + { 40, 41, 70 }, + { 17, 50, 53 }, + { 42, 66, 68 }, + { 4, 22, 72 }, + { 36, 64, 81 }, + { 13, 29, 47 }, + { 2, 8, 81 }, + { 56, 67, 73 }, + { 5, 38, 50 }, + { 12, 38, 64 }, + { 59, 72, 80 }, + { 3, 26, 79 }, + { 45, 76, 81 }, + { 1, 65, 74 }, + { 7, 18, 77 }, + { 11, 56, 59 }, + { 14, 39, 54 }, + { 16, 37, 66 }, + { 10, 28, 55 }, + { 15, 60, 70 }, + { 17, 25, 82 }, + { 20, 30, 31 }, + { 12, 67, 68 }, + { 23, 75, 80 }, + { 27, 32, 62 }, + { 24, 69, 75 }, + { 19, 21, 71 }, + { 34, 53, 61 }, + { 35, 46, 47 }, + { 33, 59, 76 }, + { 40, 43, 83 }, + { 41, 42, 63 }, + { 49, 75, 83 }, + { 20, 44, 48 }, + { 42, 49, 57 } +}; + +const uint8_t kFTX_LDPC_Num_rows[FTX_LDPC_M] = { + 7, 6, 6, 6, 7, 6, 7, 6, 6, 7, 6, 6, 7, 7, 6, 6, + 6, 7, 6, 7, 6, 7, 6, 6, 6, 7, 6, 6, 6, 7, 6, 6, + 6, 6, 7, 6, 6, 6, 7, 7, 6, 6, 6, 6, 7, 7, 6, 6, + 6, 6, 7, 6, 6, 6, 7, 6, 6, 6, 6, 7, 6, 6, 6, 7, + 6, 6, 6, 7, 7, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 7, + 6, 6, 6 +}; + + ldpc::ldpc () {} + ldpc::~ldpc () {} +// codeword is 174 log-likelihoods. +// plain is a return value, 174 ints, to be 0 or 1. +// max_iters is how hard to try. +// ok == 87 means success. +void ldpc::ldpc_decode (float codeword [], int max_iters, + uint8_t plain [], int* ok) { +//float m [FTX_LDPC_M][FTX_LDPC_N]; // ~60 kB +//float e [FTX_LDPC_M][FTX_LDPC_N]; // ~60 kB + +float **m = new float *[FTX_LDPC_M]; +float **e = new float *[FTX_LDPC_M]; + for (int i = 0; i < FTX_LDPC_M; i ++) { + m [i] = new float [FTX_LDPC_N]; + e [i] = new float [FTX_LDPC_N]; + } + +int min_errors = FTX_LDPC_M; +// +// we maken M kopien van het codewoord + for (int j = 0; j < FTX_LDPC_M; j++) { + for (int i = 0; i < FTX_LDPC_N; i++) { + m [j][i] = codeword [i]; + e [j][i] = 0.0f; + } + } + + for (int iter = 0; iter < max_iters; iter++) { +// prepare for decoding, compute the errorss + for (int j = 0; j < FTX_LDPC_M; j++) { + for (int ii1 = 0; ii1 < kFTX_LDPC_Num_rows [j]; ii1++) { + int i1 = kFTX_LDPC_Nm [j] [ii1] - 1; + float a = 1.0f; + for (int ii2 = 0; ii2 < kFTX_LDPC_Num_rows [j]; ii2++) { + int i2 = kFTX_LDPC_Nm [j][ii2] - 1; + if (i2 != i1) { + a *= tanh (-m [j][i2] / 2.0f); + } + } + e [j][i1] = -2.0f * atanh (a); + } + } +// +// new guess for plain values + for (int i = 0; i < FTX_LDPC_N; i++) { + float l = codeword [i]; + for (int j = 0; j < 3; j++) + l += e [kFTX_LDPC_Mn [i][j] - 1][i]; + plain [i] = (l > 0) ? 1 : 0; + } + + int errors = ldpc_check (plain); + + if (errors < min_errors) { +// Update the current best result + min_errors = errors; + if (errors == 0) { + break; // Found a perfect answer + } + } + +// and update the nodes + for (int i = 0; i < FTX_LDPC_N; i++) { + for (int ji1 = 0; ji1 < 3; ji1++) { + int j1 = kFTX_LDPC_Mn [i][ji1] - 1; + float l = codeword [i]; + for (int ji2 = 0; ji2 < 3; ji2++) { + if (ji1 != ji2) { + int j2 = kFTX_LDPC_Mn [i][ji2] - 1; + l += e [j2] [i]; + } + } + m [j1] [i] = l; + } + } + } + + for (int i = 0; i < FTX_LDPC_M; i ++) { + delete [] m [i]; + delete [] e [i]; + } + delete [] m; + delete [] e; + *ok = min_errors; +} + +void ldpc::bp_decode (float codeword [], int max_iters, + uint8_t plain [], int* ok) { +float tov [FTX_LDPC_N][3]; +float toc [FTX_LDPC_N][7]; +//float toc [FTX_LDPC_M][7]; + +int min_errors = FTX_LDPC_M; + +// initialize message data + for (int n = 0; n < FTX_LDPC_N; ++n) { + tov [n][0] = tov [n][1] = tov [n][2] = 0; + } + + for (int iter = 0; iter < max_iters; iter ++) { +// Do a hard decision guess (tov = 0 in iter 0) + int plain_sum = 0; + for (int n = 0; n < FTX_LDPC_N; ++n) { + plain [n] = ((codeword[n] + tov[n][0] + tov[n][1] + tov[n][2]) > 0) ? 1 : 0; + plain_sum += plain[n]; + } + + if (plain_sum == 0) { +// message converged to all-zeros, which is prohibited + break; + } + +// Check to see if we have a codeword (check before we do any iter) + int errors = ldpc_check (plain); + + if (errors < min_errors) { +// we have a better guess - update the result + min_errors = errors; + if (errors == 0) { + break; // Found a perfect answer + } + } + +// Send messages from bits to check nodes + for (int m = 0; m < FTX_LDPC_M; ++m) { + for (int n_idx = 0; n_idx < kFTX_LDPC_Num_rows [m]; n_idx ++) { + int n = kFTX_LDPC_Nm [m][n_idx] - 1; +// for each (n, m) + float Tnm = codeword [n]; + for (int m_idx = 0; m_idx < 3; m_idx ++) { + if ((kFTX_LDPC_Mn [n][m_idx] - 1) != m) { + Tnm += tov [n][m_idx]; + } + } + toc [m][n_idx] = tanh (-Tnm / 2); + } + } + +// send messages from check nodes to variable nodes + for (int n = 0; n < FTX_LDPC_N; ++n) { + for (int m_idx = 0; m_idx < 3; m_idx ++) { + int m = kFTX_LDPC_Mn [n][m_idx] - 1; +// for each (n, m) + float Tmn = 1.0f; + for (int n_idx = 0; n_idx < kFTX_LDPC_Num_rows[m]; ++n_idx) { + if ((kFTX_LDPC_Nm[m][n_idx] - 1) != n) { + Tmn *= toc [m][n_idx]; + } + } + tov [n][m_idx] = -2 * atanh (Tmn); + } + } + } + + *ok = min_errors; +} +// +// does a 174-bit codeword pass the FT8's LDPC parity checks? +// returns the number of parity errors. +// 0 means total success. +// +int ldpc::ldpc_check (uint8_t codeword[]) { +int errors = 0; + + for (int m = 0; m < FTX_LDPC_M; ++m) { + uint8_t x = 0; + for (int i = 0; i < kFTX_LDPC_Num_rows[m]; ++i) { + x ^= codeword [kFTX_LDPC_Nm[m][i] - 1]; + } + if (x != 0) { + errors ++; + } + } + return errors; +} + diff --git a/decoders/ft8-decoder-win/ldpc.h b/decoders/ft8-decoder-win/ldpc.h new file mode 100644 index 0000000..1b6d75b --- /dev/null +++ b/decoders/ft8-decoder-win/ldpc.h @@ -0,0 +1,47 @@ +# +/* + * Copyright (C) 2022 + * Karlis Goba + * YL33G + * + * This file is the :.h" file for the class made for the ldpc + * functions imported from the ft8 linrary from Karlis Goba + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _INCLUDE_LDPC_H_ +#define _INCLUDE_LDPC_H_ + +#include +#include "ft8-constants.h" + +class ldpc { +public: + ldpc (); + ~ldpc (); + + void ldpc_decode (float codeword[], int max_iters, + uint8_t plain[], int* ok); + void bp_decode (float codeword[], int max_iters, + uint8_t plain[], int* ok); +private: + + int ldpc_check (uint8_t codeword []); + float fast_tanh (float x); + float fast_atanh (float x); +}; + +#endif diff --git a/decoders/ft8-decoder-win/pack-handler.cpp b/decoders/ft8-decoder-win/pack-handler.cpp new file mode 100644 index 0000000..ee41adb --- /dev/null +++ b/decoders/ft8-decoder-win/pack-handler.cpp @@ -0,0 +1,683 @@ +# +/* + * Copyright (C) 2022 + * J van Katwijk + * Lazy Chair Computing + * + * Part of the algorithms are copied or derived from + * Karlis Goba + * YL3JG + * + * This file is part of the swradio + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "pack-handler.h" +#include +// Table 7 from "The FT4 and FT8 Communication Protocols" +// Authors Sreve Franke, Bill Somerville and Joe Taylor" +// reads +// DE 0 +// QRZ 1 +// CQ 2 +// CQ 000 - CQ 999 3 .. 1002 +// CQ A - CQ Z 1004 .. 1029 +// CQ AA - CQ ZZ 1031 .. 1731 +// CQ AAA - CQ ZZZ 1760 .. 20685 +// CQ AAAA - CQ ZZZZ 21443 .. 532443 +// +// 22-bit hash codes 2063592 + (0 .. 41943030 +// Standard call signs 6257896 + (0 .. 2684354550 + +#define CQ_3DIGITS 3 +#define CQ_3DIGITS_E 1002 +#define CQ_1LETTER 1004 +#define CQ_1LETTER_E 1029 +#define CQ_2LETTER 1031 +#define CQ_2LETTER_E 1731 +#define CQ_3LETTER 1760 +#define CQ_3LETTER_E 20685 +#define CQ_4LETTER 21443 +#define CQ_4LETTER_E 532443 +#define HASH_START (2063592L) +#define HASH_END (HASH_START + 4194304L) + +static inline +uint16_t getBits (const uint8_t *d, + int32_t offset, int16_t amount) { +int16_t res = 0; + + for (int i = 0; i < amount; i ++) { + res <<= 1; + res |= (d [offset + i] & 01); + } + return res; +} + +static inline +uint32_t getLBits (const uint8_t *d, + int32_t offset, int16_t amount) { +uint32_t res = 0; + + for (int i = 0; i < amount; i ++) { + res <<= 1; + res |= (d [offset + i] & 01); + } + return res; +} + +static inline +uint64_t getLLBits (const uint8_t *d, + int32_t offset, int16_t amount) { +uint64_t res = 0; + + for (int i = 0; i < amount; i ++) { + res <<= 1; + res |= (d [offset + i] & 01); + } + return res; +} + +// convert integer index to ASCII character according to one of 6 tables: +const char *table_0 = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"; +const char *table_1 = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const char *table_2 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const char *table_3 = "0123456789"; +const char *table_4 = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const char *table_5 = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/"; +static +char charn (int c, int table_idx) { +const char *theTable = nullptr; + switch (table_idx) { + case 0: + theTable = table_0; + break; + case 1: + theTable = table_1; + break; + case 2: + theTable = table_2; + break; + case 3: + theTable = table_3; + break; + case 4: + theTable = table_4; + break; + case 5: + theTable = table_5; + break; + default: + return '?'; + } + if (theTable == nullptr) + return '_'; // cannot happen + else + if ((0 <= c) && (c <= strlen (theTable))) + return theTable [c]; + else + return '_'; // should not happen +} + + packHandler::packHandler (): + the_hashHandler ("/tmp/xxx") { +} + + packHandler::~packHandler () {} + +void packHandler::pack_bits (const uint8_t *input, + int nrBits, uint8_t *out){ +static +uint8_t bits [] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; +int index = 0; + + out [0] = 0; + for (int i = 0; i < nrBits; i ++) { + if ((i > 0) && ((i % 8) == 0)) { + index += 1; + out [index] = 0; + } + if (input [i] != 0) + out [index] |= bits [i % 8]; + } +} + +QString packHandler::unpackMessage (const uint8_t* m_in) { +QString result; + + uint8_t i3 = getBits (m_in, 74, 3); + switch (i3) { + case 0: // dispatch further + return handle_type0 (m_in, getBits (m_in, 71, 3)); + + case 1: // c28 r1 c28 r1 R1 g15 + case 2: // c28 p1 c28 p1 R1 g15 + return handle_type1 (m_in, i3); + + case 3: // t1 c28 c28 R1 r3 s13 + return handle_type3 (m_in); + + case 4: // h12 c58 h1 r2 c1 + return handle_type4 (m_in); + + case 5: // h12 h22 R1 r3 s11 g25 + return handle_type5 (m_in); + + default: + return ""; // should not happen + } + + return result; // cannot happen +} + +QString packHandler::handle_type0 (const uint8_t *m_in, int n3) { + switch (n3) { + case 0: // t71 + return handle_type00 (m_in); + + case 1: // c28 c28 h10 r5 + return handle_type01 (m_in); + + case 2: // nothing + return ""; + + case 3: // c28 c28 R1 n4 k3 S7 + return handle_type03 (m_in); + + case 4: // c28 c28 R1 n4 k3 S7 + return handle_type04 (m_in); + + case 5: // t71 + return handle_type05 (m_in); + + default: // should not happen + return ""; + } + return ""; // cannot happen +} + +// handle type 1, i.e. "standard" messages +// handles bot type1 and type2 messages +// c28 r1 c28 r1 R1 g15: K1ABC/R PA0JAN/R EN35 +QString packHandler::handle_type1 (const uint8_t *m_in, uint8_t i3) { +uint32_t c28a, c28b; +int16_t g15; +uint8_t R1; +uint8_t r1, r2; +QString result = "type 1/2: "; + + c28a = getLBits (m_in, 0, 28); // callsign + r1 = getBits (m_in, 28, 1); // /R or /P + c28b = getLBits (m_in, 29, 28); // callsign + r2 = getBits (m_in, 57, 1); // /R or /P + R1 = getBits (m_in, 58, 1); // R1 + g15 = getBits (m_in, 59, 15); // g15 + +// Unpack both callsigns + QString c1 = getCallsign (c28a); + if (c1 == "") + return ""; +// Check if we should append /R or /P suffix + if (r1) { + if (i3 == 1) { + c1 += "/R"; + } + else + if (i3 == 2) { + c1 += "/P"; + } + } + + QString c2 = getCallsign (c28b); + if (c2 == "") + return ""; +// Check if we should append /R or /P suffix + if (r2) { + if (i3 == 1) { + c2 += "/R"; + } + else + if (i3 == 2) { + c2 += "/P"; + } + } + + result += c1 + " \t" + c2; + + if (g15 > 100) { + uint16_t n = g15; + char data [5]; + data [4] = '\0'; + data [3] = '0' + (n % 10); + n /= 10; + data [2] = '0' + (n % 10); + n /= 10; + data [1] = 'A' + (n % 18); + n /= 18; + data [0] = 'A' + (n % 18); + if (R1 > 0) + result += "\t R " + QString (data); + else + result += "\t" + QString (data); + } + else { +// Extract report + int irpt = g15; + QString res2; + switch (irpt) { + case 1: + res2 = ""; + break; + case 2: + res2 = "RRR"; + break; + case 3: + res2 = "RR73"; + break; + case 4: + res2 = "73"; + break; + default: + if (R1 > 0) { + res2 = "R"; // Add "R" before report + } + res2 += number_2 (irpt - 35); + } + result += "\t" + res2; + } + return result; +} + +// d28 is a 28-bit integer containing the +// call sign bits from a packed message. +QString packHandler::getCallsign (uint32_t d28) { +QString res; +static +const char * t1 [] = {"DE", "QRZ", "CQ"}; + +// Check for special tokens DE, QRZ, CQ, CQ_nnn, CQ_xxxx + if (d28 < HASH_START) { + if (d28 < 3) + return t1 [d28]; + + if (d28 <= CQ_3DIGITS_E) { +// CQ_ddd with 3 digits + return "CQ " + number_3 (d28 - 3); + } +// CQ_A + if (CQ_1LETTER <= d28 && d28 <= CQ_1LETTER_E) + return QString ("CQ") + QChar (charn (d28 - CQ_1LETTER, 4)); +// CQ_AA + if (CQ_2LETTER <= d28 && d28 <= CQ_2LETTER_E) + return get_CQcode (d28 - CQ_2LETTER, 2); +// CQ_AAA + if (CQ_3LETTER <= d28 && d28 <= CQ_3LETTER_E) + return get_CQcode (d28 - CQ_3LETTER, 3); +// CQ_AAAA + if (CQ_4LETTER <= d28 && d28 <= CQ_4LETTER_E) { + return get_CQcode (d28 - CQ_4LETTER, 4); + } + return ""; + } + + if (d28 < HASH_END) { + res = the_hashHandler. lookup ((d28 - HASH_START) & 0xFFF); + return QString (res); + } + +// Standard callsign + uint32_t n = d28 - HASH_END; + + static int divTable [] = {27, 27, 27, 10, 36, 37}; + static int tabIndex [] = { 4, 4, 4, 3, 2, 1}; + + char callsign [7]; + callsign [6] = 0; + for (int i = 0; i < 6; i ++) { + callsign [5 - i] = charn (n % divTable [i], tabIndex [i]); + n /= divTable [i]; + } + + res = QString (callsign). trimmed (); + if (res. size () == 0) + return ""; + + return res; +} +// Type 3 messages,RTTY RU t1 c28 c28 R1 r3 s12 +// K1ABC W9XYZ 579 EI +QString packHandler::handle_type3 (const uint8_t *m_in) { +uint8_t t1 = getBits (m_in, 0, 1); // TU +uint32_t c28a = getBits (m_in, 1, 28); // standard callsign +uint32_t c28b = getBits (m_in, 29, 28); // standard callsign +uint8_t R1 = getBits (m_in, 58, 1); // R +uint8_t r3 = getBits (m_in, 59, 3); // report 2 .. 9, 53 .. 59 +uint16_t s13 = getBits (m_in, 62, 13); // serial number +QString result = "type 3: "; + +// Unpack both callsigns + QString c1 = getCallsign (c28a); + if (c1 == "") + return ""; + + QString c2 = getCallsign (c28b); + if (c2 == "") + return ""; + result += c1 + " \t" + c2; + + QString S_extra = "R" + QString::number (509 + 10 * r3); + result += "\t" + QString::number (s13); + return result; +} + +// Type 4: Nonstandard calls, h12 c58 h1 r2 c1 +// e.g. PJ4/KA1ABC RR73 +QString packHandler::handle_type4 (const uint8_t* m_in) { +uint32_t h12; +uint8_t r2, c1; +uint8_t h1; +uint64_t c58; +QString result = "type 4: "; + + h12 = getBits (m_in, 0, 12); // hashed callsign, 12 b + c58 = getLLBits (m_in, 12, 58); // non standard callsign + h1 = getBits (m_in, 70, 1); // hashed callsign is second + r2 = getBits (m_in, 71, 2); // RRR, RR73, 73 or blank + c1 = getBits (m_in, 73, 1); // first callsign is cq + + char c11 [12]; + c11 [11] = '\0'; + for (int i = 0; i < 11; i ++) { + c11 [10 - i] = charn (c58 % 38, 5); + c58 /= 38; + } + + the_hashHandler. add_hash (h12, c11); + char call_3 [15]; + QString ss = "<" + QString::number (h12, 16) + ">"; + strcpy (call_3, ss. toLatin1 (). data ()); + char* call_1 = (h1) ? c11 : call_3; + char* call_2 = (h1) ? call_3 : c11; + + QString s2 = QString (call_2); + s2 = s2. trimmed (); + if (c1 == 0) { + result += QString (call_1); + result = result. trimmed (); + result += " " + s2; + if (r2 == 1) + result += " " + QString ("RRR"); + else + if (r2 == 2) + result += " " + QString ("RR73"); + else + if (r2 == 3) + result += " " + QString ("73"); + } + else { + result += QString ("CQ"); + result += " " + s2; + } + return result; +} + +// type 5 EU VHF R 570007 JO22DB +// h12 h22 R1 r3 s11 g25 +QString packHandler::handle_type5 (const uint8_t* m_in) { +uint16_t h12 = getBits (m_in, 0, 12); +uint32_t h22 = getBits (m_in, 12, 22); + + return QString ("type 5: ") + QString::number (h12, 16) + " " + + QString::number (h22, 16); + return ""; +} +// +// type 0.0 is a free text, packed as 71 bits +QString packHandler::handle_type00 (const uint8_t *m_in) { +uint8_t b9 [9]; +uint8_t b72 [72]; + +// Shift 71 bits right by 1 bit, so that +// it's right-aligned in the byte array + b72 [0] = 0; + for (int i = 0; i < 71; i ++) + b72 [1 + i] = m_in [i]; + pack_bits (b72, 72, b9); + + char c14 [14]; + c14 [13] = 0; + for (int idx = 12; idx >= 0; idx--) { +// Divide the long integer in b9 by 42 + uint16_t rem = 0; + for (int i = 0; i < 9; i ++) { + rem = (rem << 8) | b9 [i]; + b9 [i] = rem / 42; + rem = rem % 42; + } + c14 [idx] = charn (rem, 0); + } + QString result = QString (c14); + return "type 0.0: " + result. trimmed (); +} + +// Type 0.1 DXpedition c28 c28 h10 r5 +// K1ABC RR73; W9CYZ -08 +QString packHandler::handle_type01 (const uint8_t *m_in) { +uint32_t c28a = getBits (m_in, 0, 28); +uint32_t c28b = getBits (m_in, 28, 28); +uint16_t h10 = getBits (m_in, 58, 10); +int16_t r5 = getBits (m_in, 68, 5); + + QString res1 = getCallsign (c28a); + QString res2 = getCallsign (c28b); + QString s_h10 = the_hashHandler. lookup (h10); + r5 = (r5 - 8) * 2; + QString s_r5 = QString::number (r5); + return "type 0.1: " + res1 + " \t" + res2 + "\t" + s_h10 + "\t" + s_r5; +} + +// Type 0.3 Field day c28 c28 R1 n4 k3 S7 +// KA1ABC W9XYZ 6A EI +QString packHandler::handle_type03 (const uint8_t *m_in) { +uint32_t c28a = getBits (m_in, 0, 28); // callsign +uint32_t c28b = getBits (m_in, 28, 28); // callsign +uint8_t R1 = getBits (m_in, 56, 1); // R +uint8_t n4 = getBits (m_in, 57, 4); // Number of transmitter +uint8_t k3 = getBits (m_in, 61, 3); // Field day class A .. F +uint8_t S7 = getBits (m_in, 64, 7); // ARRL/RAC section + + QString res1 = getCallsign (c28a); + QString res2 = getCallsign (c28b); + QString R = R1 ? "R" : ""; + QString s_n4 = QString::number (n4); + QString s_k3 = QString (QChar ('A' + k3)); + QString s_S7 = QString::number (S7); + return "type 0.3: " + res1 + " \t" + res2 + " \t" + R + s_n4 + "\t" + s_k3 + " " + s_S7; +} +// Type 0.4 Field day c28 c28 R1 n4 k3 S7 +// W9XYZ KqABC R1 18B EMA +QString packHandler::handle_type04 (const uint8_t *m_in) { + return handle_type3 (m_in); +} +// Type 0.5 t71 +// 0123456789ABCDEF012 +QString packHandler::handle_type05 (const uint8_t *m_in) { +uint8_t sym = getBits (m_in, 0, 3); +int index; +int teller = 0; +QString result; + + result. push_back (table_2 [sym]); + index = 3; + while (index < 72) { + uint8_t sym = getBits (m_in, index, 4); + index += 4; + result. push_back (sym > 0 ? table_2 [sym] : 0); + } + return "type 0.5: " + result; +} + +QString packHandler::number_3 (int number) { +QString res; +int divider = 100; + + while (divider > 0) { + if (number / divider >= 10) + fprintf (stderr, "problem\n"); + res += char (number / divider + '0'); + number %= divider; + divider /= 10; + } + return res; +} + +QString packHandler::number_2 (int number) { +QString res; + if (number >= 0) + res = "+"; + else { + res = "-"; + number = -number; + } + if (number > 100) + return "-00"; + res += char ('0' + number / 10); + res += char ('0' + number % 10); + return res; +} + +QString packHandler::get_CQcode (uint32_t data, int size) { +char cq [size + 1]; + + cq [size] = 0; + for (int i = 0; i < size; i ++) { + cq [size - 1 - i] = charn (data % 27, 4); + data /= 27; + } + + char *r = cq; + for (int i = 0; i < size; i ++) + if (cq [i] != ' ') + break; + else + r = &cq [i]; + + return "CQ_" + QString (r); +} + +QStringList packHandler::extractCall (const uint8_t * m_in) { +QStringList result; + + uint8_t i3 = getBits (m_in, 74, 3); + switch (i3) { + case 0: // dispatch further + return result; + + case 1: // c28 r1 c28 r1 R1 g15 + case 2: // c28 p1 c28 p1 R1 g15 + return extract_call_type_1 (m_in, i3); + + case 3: // t1 c28 c28 R1 r3 s13 + return result; + + case 4: // h12 c58 h1 r2 c1 + return result; + + case 5: // h12 h22 R1 r3 s11 g25 + return result; + + default: + return result; // should not happen + } + + return result; // cannot happen +} +// +// for now, we assume that interest is in the caller +QStringList packHandler::extract_call_type_1 (const uint8_t *m_in, int type) { +uint32_t c28a; +uint32_t c28b; +uint8_t R1; +uint16_t g15 = 0; +QString call; +QString locator; +QStringList result; + + c28a = getLBits (m_in, 0, 28); // callsign + c28b = getLBits (m_in, 29, 28); // callsign + R1 = getBits (m_in, 58, 1); // R1 + g15 = getBits (m_in, 59, 15); // g15, potential grid + +// Check for special tokens DE, QRZ, CQ, CQ_nnn, CQ_xxxx + if (c28a == 2) { // It is a cq + if (R1 > 0) + return result; + for (int i = 0; i < gehad. size (); i ++) + if (gehad. at (i) == c28b) + return result; + call = getCallsign (c28b); + gehad. push_back (c28b); + result << call; + if (g15 > 0) { + uint16_t n = g15; + char data [5]; + data [4] = '\0'; + data [3] = '0' + (n % 10); + n /= 10; + data [2] = '0' + (n % 10); + n /= 10; + data [1] = 'A' + (n % 18); + n /= 18; + data [0] = 'A' + (n % 18); + locator = QString (data); + if ((data [0] == 'A') || (data [0] == 'R')) + return result; + result << locator; + return result; + } + } + else + if (c28a > HASH_END) { // normal call + if (R1 > 0) + return result; + for (int i = 0; i < gehad. size (); i ++) + if (gehad. at (i) == c28a) + return result; + call = getCallsign (c28a); + gehad. push_back (c28a); + result << call; + if (g15 > 0) { + uint16_t n = g15; + char data [5]; + data [4] = '\0'; + data [3] = '0' + (n % 10); + n /= 10; + data [2] = '0' + (n % 10); + n /= 10; + data [1] = 'A' + (n % 18); + n /= 18; + data [0] = 'A' + (n % 18); + if ((data [0] == 'A') || (data [0] == 'R')) + return result; + locator = QString (data); + result << locator; + return result; + } + } + return result; + +} + diff --git a/decoders/ft8-decoder-win/pack-handler.h b/decoders/ft8-decoder-win/pack-handler.h new file mode 100644 index 0000000..3355c28 --- /dev/null +++ b/decoders/ft8-decoder-win/pack-handler.h @@ -0,0 +1,71 @@ +# +/* + * Copyright (C) 2022 + * J van Katwijk + * :azy Chair Computing + * + * Parts of the algorithms are copied or derived from + * Karlis Goba + * YL33G + * + * This file is part of the swradio + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PACK_HANDLER__ +#define __PACK_HANDLER__ +#include +#include +#include +#include +#include +#include "hashHandler.h" + +class packHandler: public QObject { +Q_OBJECT +public: + packHandler (); + ~packHandler (); + + QString unpackMessage (const uint8_t* m_in); + QStringList extractCall (const uint8_t* m_in); +private: + hashHandler the_hashHandler; + bool pskReporterReady; + QString handle_type0 (const uint8_t *m_in, int n3); + QString handle_type1 (const uint8_t *m_in, uint8_t i3); + QString handle_type3 (const uint8_t *m_in); + QString handle_type4 (const uint8_t* m_in); + QString handle_type5 (const uint8_t* m_in); + QString handle_type00 (const uint8_t *m_in); + QString handle_type01 (const uint8_t *m_in); + QString handle_type03 (const uint8_t *m_in); + QString handle_type04 (const uint8_t *m_in); + QString handle_type05 (const uint8_t *m_in); + + QString get_CQcode (uint32_t data, int size); + QString number_2 (int number); + QString number_3 (int number); + void pack_bits (const uint8_t bit_array [], + int num_bits, uint8_t packed []); + QString getCallsign (const uint32_t n28); + QStringList extract_call_type_1 (const uint8_t *m_in, int type); + + std::vector gehad; +signals: + void show_pskStatus (bool); +}; +#endif diff --git a/decoders/ft8-decoder-win/semaphore.h b/decoders/ft8-decoder-win/semaphore.h new file mode 100644 index 0000000..8926c20 --- /dev/null +++ b/decoders/ft8-decoder-win/semaphore.h @@ -0,0 +1,64 @@ +# +/* + * Copyright (C) 2010, 2011, 2012, 2013 + * Jan van Katwijk (J.vanKatwijk@gmail.com) + * Lazy Chair Programming + * + * This file is part of swradio + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __SEMAPHORE +#define __SEMAPHORE + +#include +#include +#include + +class Semaphore { +private: + std::mutex mtx; + std::condition_variable cv; + int count; +public: + Semaphore (int count_ = 0) : count {count_} {} + +void Release () { + std::unique_locklck (mtx); + ++count; + cv. notify_one (); +} + +void acquire () { + std::unique_lock lck (mtx); + while (count == 0) { + cv. wait (lck); + } + -- count; +} + +bool tryAcquire (int delay) { + std::unique_lock lck (mtx); + if (count == 0) { + auto now = std::chrono::system_clock::now (); + cv. wait_until (lck, now + std::chrono::milliseconds (delay)); + } + if (count == 0) + return false; + -- count; + return true; +} +}; +#endif diff --git a/decoders/ft8-decoder/ft8-decoder.cpp b/decoders/ft8-decoder/ft8-decoder.cpp index 850beb9..97d8706 100644 --- a/decoders/ft8-decoder/ft8-decoder.cpp +++ b/decoders/ft8-decoder/ft8-decoder.cpp @@ -27,6 +27,7 @@ #include "pack-handler.h" #include #include "radio.h" +#include "identity-dialog.h" #define LOG_BASE 240 @@ -36,8 +37,7 @@ QSettings *settings) : virtualDecoder (inRate, buffer), myFrame (nullptr), - theWriter (settings), - theProcessor (this, 20, &theWriter) { + theProcessor (this, 20) { ft8Settings = settings; this -> mr = mr; setupUi (&myFrame); @@ -70,6 +70,7 @@ readIndex = 0; inPointer = 0; lineCounter = 0; + theWriter = nullptr; filePointer. store (nullptr); ft8Settings -> beginGroup ("ft8Settings"); @@ -90,8 +91,10 @@ this, SLOT (handle_filesaveButton ())); connect (identityButton, SIGNAL (clicked ()), this, SLOT (handle_identityButton ())); + connect (pskReporterButton, SIGNAL (clicked ()), + this, SLOT (handle_pskReporterButton ())); - show_pskStatus (theWriter. reporterReady ()); + show_pskStatus (false); teller = 0; } @@ -124,10 +127,12 @@ void ft8_Decoder::process (std::complex z) { if (inPointer < toneLength / FRAMES_PER_TONE) return; inPointer = 0; -static int counter = 0; - if (++counter >= 100) { - counter = 0; - theWriter. sendMessages (); + if (theWriter != nullptr) { + static int counter = 0; + if (++counter >= 100) { + counter = 0; + theWriter -> sendMessages (); + } } int content = (FRAMES_PER_TONE - 1) * toneLength / FRAMES_PER_TONE; int newAmount = toneLength / FRAMES_PER_TONE; @@ -379,8 +384,7 @@ float workVector [2 * toneLength]; for (int index = begin + 5; index < end - 5; index ++) { if ((1.1 * workVector [index - 5] < workVector [index]) && - (1.1 * workVector [index + 5] < workVector [index]) ) - { + (1.1 * workVector [index + 5] < workVector [index]) ) { E. index = index; E. value = theBuffer [readIndex] [index]; cache. push_back (E); @@ -422,22 +426,37 @@ bool ft8_Decoder::pskReporterReady () { return pskReady; } -void ft8_Decoder::handle_identityButton () { -bool ok; -QString text = QInputDialog::getText (nullptr, tr ("your callsign"), - "enter your callsign", - QLineEdit::Normal, tr ("NL99999"), &ok); - if (ok) { - ft8Settings -> beginGroup ("ft8Settings"); - ft8Settings -> setValue ("homeCall", text); +void ft8_Decoder::handle_pskReporterButton () { + if (theWriter != nullptr) { + delete theWriter; + pskReady = false; + theWriter = nullptr; + show_pskStatus (false); + return; } - text = QInputDialog::getText (nullptr, tr ("your grid"), - "enter your grid", - QLineEdit::Normal, tr ("JO00aa"), &ok); - if (ok) { - ft8Settings -> setValue ("homeGrid", text); + + try { + theWriter = new reporterWriter (ft8Settings); + pskReady = true; + } catch (int e) { + pskReady = false; } - ft8Settings -> endGroup (); + show_pskStatus (pskReady); +} + +void ft8_Decoder::addMessage (const QString &call, + const QString &grid, + int frequency, + int snr) { + if (theWriter != nullptr) + theWriter -> addMessage (call. toStdString (), + grid. toStdString (), + frequency, snr); +} + +void ft8_Decoder::handle_identityButton () { +identityDialog Identity (ft8Settings); + Identity. QDialog::exec (); } void ft8_Decoder::show_pskStatus (bool b) { diff --git a/decoders/ft8-decoder/ft8-decoder.h b/decoders/ft8-decoder/ft8-decoder.h index a81d671..aebdf34 100644 --- a/decoders/ft8-decoder/ft8-decoder.h +++ b/decoders/ft8-decoder/ft8-decoder.h @@ -50,7 +50,7 @@ Q_OBJECT int32_t, RingBuffer> *, QSettings *); - ~ft8_Decoder (void); + ~ft8_Decoder (); void processBuffer (std::complex *, int32_t); void process (std::complex z); int tunedFrequency (); @@ -59,8 +59,8 @@ Q_OBJECT private: QFrame myFrame; ldpc ldpcHandler; - reporterWriter theWriter; ft8_processor theProcessor; + reporterWriter *theWriter; QSettings *ft8Settings; RadioInterface *mr; int32_t inputRate; @@ -108,11 +108,15 @@ Q_OBJECT public slots: void printLine (const QString &); void show_pskStatus (bool); + void addMessage (const QString &, + const QString &, + int, int); private slots: void set_maxIterations (int); void set_spectrumWidth (int); void handle_filesaveButton (); void handle_identityButton (); + void handle_pskReporterButton (); }; #endif diff --git a/decoders/ft8-decoder/ft8-decoder.ui b/decoders/ft8-decoder/ft8-decoder.ui index dec2313..3b090d2 100644 --- a/decoders/ft8-decoder/ft8-decoder.ui +++ b/decoders/ft8-decoder/ft8-decoder.ui @@ -7,7 +7,7 @@ 0 0 445 - 302 + 305 @@ -66,6 +66,13 @@ + + + + start/stop pskReport + + + diff --git a/decoders/ft8-decoder/ft8-processor.cpp b/decoders/ft8-decoder/ft8-processor.cpp index ebd540a..7b79b5c 100644 --- a/decoders/ft8-decoder/ft8-processor.cpp +++ b/decoders/ft8-decoder/ft8-processor.cpp @@ -30,14 +30,12 @@ #include ft8_processor::ft8_processor (ft8_Decoder *theDecoder, - int maxIterations, - reporterWriter *theWriter): + int maxIterations): theCache (30), freeSlots (nrBlocks) { this -> theDecoder = theDecoder; this -> maxIterations = maxIterations; - this -> theWriter = theWriter; this -> blockToRead = 0; this -> blockToWrite = 0; running. store (false); @@ -47,6 +45,10 @@ SLOT (printLine (const QString &))); connect (this, SIGNAL (show_pskStatus (bool)), theDecoder, SLOT (show_pskStatus (bool))); + connect (this, SIGNAL (addMessage (const QString &, const QString &, + int, int)), + theDecoder, + SLOT (addMessage (const QString &, const QString &, int, int))); threadHandle = std::thread (&ft8_processor::run, this); } @@ -166,7 +168,7 @@ int errors; theBuffer [blockToRead]. value, theBuffer [blockToRead]. frequency, res); - if (theWriter -> reporterReady ()) { + if (theDecoder -> pskReporterReady ()) { QStringList call = unpackHandler. extractCall (ldpcBits); if (call. length () > 0) { QString callIdent = call. at (0); @@ -175,17 +177,13 @@ int errors; locator = call. at (1); int snr = theBuffer [blockToRead]. value; int freq = theBuffer [blockToRead]. frequency + theDecoder -> tunedFrequency (); - theWriter -> addMessage (callIdent. toStdString (), - locator. toStdString (), - freq, snr); + addMessage (callIdent, locator, freq, snr); } } } } // prepare for the next round - freeSlots. Release (); - blockToRead = (blockToRead + 1) % (nrBlocks); } } diff --git a/decoders/ft8-decoder/ft8-processor.h b/decoders/ft8-decoder/ft8-processor.h index cbe7361..03fafd9 100644 --- a/decoders/ft8-decoder/ft8-processor.h +++ b/decoders/ft8-decoder/ft8-processor.h @@ -40,7 +40,7 @@ class reporterWriter; class ft8_processor: public QObject { Q_OBJECT public: - ft8_processor (ft8_Decoder *, int, reporterWriter *); + ft8_processor (ft8_Decoder *, int); ~ft8_processor (); void PassOn (int, float, int, float *); @@ -48,7 +48,6 @@ Q_OBJECT private: packHandler unpackHandler; - reporterWriter *theWriter; void run (); bool check_crc_bits (uint8_t *message, int nrBits); void showLine (int, int, int, const QString &); @@ -77,6 +76,8 @@ Q_OBJECT signals: void printLine (const QString &); void show_pskStatus (bool); + void addMessage (const QString &, const QString &, + int, int); }; #endif diff --git a/decoders/ft8-decoder/identity-dialog.cpp b/decoders/ft8-decoder/identity-dialog.cpp new file mode 100644 index 0000000..d3f26f3 --- /dev/null +++ b/decoders/ft8-decoder/identity-dialog.cpp @@ -0,0 +1,68 @@ + +#include "identity-dialog.h" + + +identityDialog::identityDialog (QSettings *ft8Settings, QWidget *parent): + QDialog (parent) { + this -> ft8Settings = ft8Settings; + callsignLabel = new QLabel (tr("callsign")); + gridLabel = new QLabel (tr("grid")); + antennaLabel = new QLabel (tr("antenna")); + + callsign = new QLineEdit; + homeGrid = new QLineEdit; + antenna = new QLineEdit; + + buttonBox = new QDialogButtonBox (QDialogButtonBox::Ok | + QDialogButtonBox::Cancel); + connect (buttonBox, &QDialogButtonBox::accepted, + this, &identityDialog::verify); + connect (buttonBox, &QDialogButtonBox::rejected, + this, &identityDialog::reject); + QGridLayout *mainLayout = new QGridLayout; + mainLayout -> addWidget (callsignLabel, 0, 0); + mainLayout -> addWidget (callsign, 0, 1); + mainLayout -> addWidget (gridLabel, 1, 0); + mainLayout -> addWidget (homeGrid, 1, 1); + mainLayout -> addWidget (antennaLabel, 2, 0); + mainLayout -> addWidget (antenna, 2, 1); + mainLayout -> addWidget (buttonBox, 3, 0); + setLayout (mainLayout); + setWindowTitle ("your data"); + show ();; +} + +identityDialog::~identityDialog () {} + +void identityDialog::verify () { + if (!callsign -> text (). isEmpty () && + !homeGrid -> text (). isEmpty () && + !antenna -> text (). isEmpty ()) { + ft8Settings -> beginGroup ("ft8Settings"); + ft8Settings -> setValue ("homeCall", callsign -> text ()); + ft8Settings -> setValue ("homeGrid", homeGrid -> text ()); + ft8Settings -> setValue ("antenna", antenna -> text ()); + ft8Settings -> endGroup (); + accept (); + return; + } + + QMessageBox::StandardButton answer; + answer = QMessageBox::warning (this, tr ("incomplete form"), + tr ("The form is not filled in completely\n" + "Do you want to discard it?"), + QMessageBox::Yes |QMessageBox::No); + if (answer == QMessageBox::Yes) + reject (); +} + +void identityDialog::reject () { + accept (); +} + + + + + + + diff --git a/decoders/ft8-decoder/identity-dialog.h b/decoders/ft8-decoder/identity-dialog.h new file mode 100644 index 0000000..b1d3828 --- /dev/null +++ b/decoders/ft8-decoder/identity-dialog.h @@ -0,0 +1,32 @@ + +#ifndef __IDENTITY_SELECTOR__ +#define __IDENTITY_SELECTOR__ + +#include +#include +#include +#include +#include +#include +#include + +class identityDialog: public QDialog { +public: + identityDialog (QSettings *, QWidget *parent = nullptr); + ~identityDialog (); +private slots: + QSettings *ft8Settings; + void verify (); + void reject (); + + QLabel *callsignLabel; + QLineEdit *callsign; + QLabel *gridLabel; + QLineEdit *homeGrid; + QLabel *antennaLabel; + QLineEdit *antenna; + QDialogButtonBox *buttonBox; + +}; +#endif + diff --git a/decoders/ft8-decoder/psk-writer.cpp b/decoders/ft8-decoder/psk-writer.cpp index 1ce6a49..3a8935c 100644 --- a/decoders/ft8-decoder/psk-writer.cpp +++ b/decoders/ft8-decoder/psk-writer.cpp @@ -1,4 +1,25 @@ - +# +/* + * Copyright (C) 2022 + * Jan van Katwijk (J.vanKatwijk@gmail.com) + * Lazy Chair Computing + * + * This file is part of the swradio + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "psk-writer.h" #include @@ -39,12 +60,12 @@ struct hostent *host; settings -> beginGroup ("ft8Settings"); this -> homeCall = - settings -> value ("homeCall", ""). toString (). toStdString (); + settings -> value ("homeCall", "NL14157"). toString (). toStdString (); this -> homeGrid = - settings -> value ("homeGrid", ""). toString (). toStdString (); - this -> programName = "swradio-9"; + settings -> value ("homeGrid", "JO22fa"). toString (). toStdString (); + this -> programName = "swRadio-9"; this -> antenna = - settings -> value ("antenna", ""). toString (). toStdString (); + settings -> value ("antenna", "home built loop"). toString (). toStdString (); settings -> endGroup (); sequence = 1; reporterOK = false; @@ -56,14 +77,18 @@ struct hostent *host; homeCall. c_str (), homeGrid. c_str (), antenna. c_str ()); +#ifndef __MINGW32__ if ((sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { +#else + if ((sock = socket (PF_INET, SOCK_DGRAM, 0)) < 0) { +#endif fprintf (stderr, "Cannot open socket %d.\n", errno); - return; + throw (22); } if ((host = gethostbyname (name)) == NULL) { fprintf (stderr, "Cannot find remote host address.\n"); - return; + throw (23); } memset (&addr, 0, sizeof (addr)); diff --git a/decoders/ft8-decoder/psk-writer.h b/decoders/ft8-decoder/psk-writer.h index 4ce631c..e136d80 100644 --- a/decoders/ft8-decoder/psk-writer.h +++ b/decoders/ft8-decoder/psk-writer.h @@ -1,4 +1,26 @@ +# +/* + * Copyright (C) 2022 + * Jan van Katwijk (J.vanKatwijk@gmail.com) + * Lazy Chair Computing + * + * This file is part of the swradio + * + * swradio 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 2 of the License, or + * (at your option) any later version. + * + * swradio 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 swradio; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #ifndef __REPORTER_WRITER__ #define __REPORTER_WRITER__ diff --git a/mocinclude.opt b/mocinclude.opt new file mode 100644 index 0000000..8bc7d5d --- /dev/null +++ b/mocinclude.opt @@ -0,0 +1,53 @@ +-I/usr/i686-w64-mingw32/sys-root/mingw/share/qt5/mkspecs/mingw-w64-g++ +-I/usr/shared/systems/swradio-8 +-I/usr/shared/systems/swradio-8 +-I/usr/shared/systems/swradio-8/decimators +-I/usr/shared/systems/swradio-8/filters +-I/usr/shared/systems/swradio-8/various +-I/usr/shared/systems/swradio-8/output +-I/usr/shared/systems/swradio-8/scopes-qwt6 +-I/usr/shared/systems/swradio-8/devices +-I/usr/shared/systems/swradio-8/devices/filereader +-I/usr/shared/systems/swradio-8/decoders +-I/usr/i686-w64-mingw32/sys-root/mingw/include +-I/usr/i686-w64-mingw32/sys-root/mingw/include/eigen3 +-I/usr/i686-w64-mingw32/sys-root/mingw/include/qt5/qwt +-I/usr/local/include +-I/usr/shared/systems/swradio-8/C:\msys32\mingw32\include\qwt +-I/usr/shared/systems/swradio-8/devices/sdrplay-handler +-I/usr/shared/systems/swradio-8/devices/sdrplay-handler-v3 +-I/usr/shared/systems/swradio-8/devices/sdrplay-handler-v3/include-v3 +-I/usr/shared/systems/swradio-8/devices/hackrf-handler +-I/usr/shared/systems/swradio-8/devices/rtlsdr-handler +-I/usr/shared/systems/swradio-8/decoders/am-decoder +-I/usr/shared/systems/swradio-8/decoders/ssb-decoder +-I/usr/shared/systems/swradio-8/decoders/cw-decoder +-I/usr/shared/systems/swradio-8/decoders/amtor-decoder +-I/usr/shared/systems/swradio-8/decoders/psk-decoder +-I/usr/shared/systems/swradio-8/decoders/ft8-decoder-win +-I/usr/shared/systems/swradio-8/decoders/ft8-decoder-win/fft +-I/usr/shared/systems/swradio-8/decoders/rtty-decoder +-I/usr/shared/systems/swradio-8/decoders/fax-decoder +-I/usr/shared/systems/swradio-8/fdk-aac +-I/usr/shared/systems/swradio-8/decoders/drm-decoder +-I/usr/shared/systems/swradio-8/decoders/drm-decoder +-I/usr/shared/systems/swradio-8/decoders/drm-decoder/fac +-I/usr/shared/systems/swradio-8/decoders/drm-decoder/equalizer +-I/usr/shared/systems/swradio-8/decoders/drm-decoder/msc +-I/usr/shared/systems/swradio-8/decoders/drm-decoder/ofdm +-I/usr/shared/systems/swradio-8/decoders/drm-decoder/parameters +-I/usr/shared/systems/swradio-8/decoders/drm-decoder/sdc +-I/usr/shared/systems/swradio-8/decoders/drm-decoder/data +-I/usr/shared/systems/swradio-8/decoders/drm-decoder/support +-I/usr/local/include/fdk-aac +-I/usr/i686-w64-mingw32/sys-root/mingw/include/qt5 +-I/usr/i686-w64-mingw32/sys-root/mingw/include/qt5/QtWidgets +-I/usr/i686-w64-mingw32/sys-root/mingw/include/qt5/QtGui +-I/usr/i686-w64-mingw32/sys-root/mingw/include/qt5/QtXml +-I/usr/i686-w64-mingw32/sys-root/mingw/include/qt5/QtCore +-I/usr/include/c++/11 +-I/usr/include/c++/11/x86_64-redhat-linux +-I/usr/include/c++/11/backward +-I/usr/lib/gcc/x86_64-redhat-linux/11/include +-I/usr/local/include +-I/usr/include diff --git a/swradio-8.pro b/swradio-8.pro index f69f38b..7f0a20d 100644 --- a/swradio-8.pro +++ b/swradio-8.pro @@ -169,7 +169,7 @@ CONFIG += cw-decoder CONFIG += amtor-decoder CONFIG += psk-decoder CONFIG += rtty-decoder -CONFIG += ft8-decoder +CONFIG += ft8-decoder-win CONFIG += fax-decoder CONFIG += drm-decoder-fdk # @@ -353,7 +353,10 @@ ft8-decoder { HEADERS += ./decoders/ft8-decoder/fft/_kiss_fft_guts.h SOURCES += ./decoders/ft8-decoder/fft/kiss_fft.c SOURCES += ./decoders/ft8-decoder/fft/kiss_fftr.c + HEADERS += ./decoders/ft8-decoder/identity-dialog.h + SOURCES += ./decoders/ft8-decoder/identity-dialog.cpp } + ft8-decoder-win { DEFINES += HAVE_FT8_DECODER DEFINES += KISSFFT_DATATYPE=double @@ -380,6 +383,8 @@ ft8-decoder-win { HEADERS += ./decoders/ft8-decoder-win/fft/_kiss_fft_guts.h SOURCES += ./decoders/ft8-decoder-win/fft/kiss_fft.c SOURCES += ./decoders/ft8-decoder-win/fft/kiss_fftr.c + HEADERS += ./decoders/ft8-decoder/identity-dialog.h + SOURCES += ./decoders/ft8-decoder/identity-dialog.cpp LIBS += -lPSKReporter } diff --git a/swradio-9_resource.rc b/swradio-9_resource.rc deleted file mode 100644 index 61cd153..0000000 --- a/swradio-9_resource.rc +++ /dev/null @@ -1,37 +0,0 @@ -#include - -IDI_ICON1 ICON DISCARDABLE "/usr/shared/systems/swradio-8/swradio.ico" - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,0,0,0 - PRODUCTVERSION 0,0,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_DLL - FILESUBTYPE 0x0L - BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "\0" - VALUE "FileDescription", "\0" - VALUE "FileVersion", "0.0.0.0\0" - VALUE "LegalCopyright", "\0" - VALUE "OriginalFilename", "swradio-9.exe\0" - VALUE "ProductName", "swradio-9\0" - VALUE "ProductVersion", "0.0.0.0\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0409, 1200 - END - END -/* End of Version info */ - diff --git a/swradio-ft8-widget.png b/swradio-ft8-widget.png index 046ffcd..75e9374 100644 Binary files a/swradio-ft8-widget.png and b/swradio-ft8-widget.png differ