=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/ft8CN/app/src/main/cpp/fft/kiss_fft.h b/ft8CN/app/src/main/cpp/fft/kiss_fft.h
new file mode 100644
index 0000000..45c3a6a
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/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/ft8CN/app/src/main/cpp/fft/kiss_fftr.c b/ft8CN/app/src/main/cpp/fft/kiss_fftr.c
new file mode 100644
index 0000000..8102132
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/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/ft8CN/app/src/main/cpp/fft/kiss_fftr.h b/ft8CN/app/src/main/cpp/fft/kiss_fftr.h
new file mode 100644
index 0000000..588948d
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/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/ft8CN/app/src/main/cpp/ft8/constants.c b/ft8CN/app/src/main/cpp/ft8/constants.c
new file mode 100644
index 0000000..f98fc50
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/constants.c
@@ -0,0 +1,394 @@
+#include "constants.h"
+
+
+//科斯塔阵列
+const uint8_t kFT8CostasPattern[7] = { 3, 1, 4, 0, 6, 5, 2 };
+const uint8_t kFT4CostasPattern[4][4] = {
+ { 0, 1, 3, 2 },
+ { 1, 0, 2, 3 },
+ { 2, 3, 1, 0 },
+ { 3, 2, 0, 1 }
+};
+
+// Gray code map (FTx bits -> channel symbols)
+//格雷码
+const uint8_t kFT8GrayMap[8] = { 0, 1, 3, 2, 5, 6, 4, 7 };
+const uint8_t kFT4GrayMap[4] = { 0, 1, 3, 2 };
+
+const uint8_t kFT4XORSequence[10] = {
+ 0x4Au, // 01001010
+ 0x5Eu, // 01011110
+ 0x89u, // 10001001
+ 0xB4u, // 10110100
+ 0xB0u, // 10110000
+ 0x8Au, // 10001010
+ 0x79u, // 01111001
+ 0x55u, // 01010101
+ 0xBEu, // 10111110
+ 0x28u, // 00101 [000]
+};
+
+// Parity generator matrix for (174,91) LDPC code, stored in bitpacked format (MSB first)
+const uint8_t kFTXLDPCGenerator[FTX_LDPC_M][FTX_LDPC_K_BYTES] = {
+ { 0x83, 0x29, 0xce, 0x11, 0xbf, 0x31, 0xea, 0xf5, 0x09, 0xf2, 0x7f, 0xc0 },
+ { 0x76, 0x1c, 0x26, 0x4e, 0x25, 0xc2, 0x59, 0x33, 0x54, 0x93, 0x13, 0x20 },
+ { 0xdc, 0x26, 0x59, 0x02, 0xfb, 0x27, 0x7c, 0x64, 0x10, 0xa1, 0xbd, 0xc0 },
+ { 0x1b, 0x3f, 0x41, 0x78, 0x58, 0xcd, 0x2d, 0xd3, 0x3e, 0xc7, 0xf6, 0x20 },
+ { 0x09, 0xfd, 0xa4, 0xfe, 0xe0, 0x41, 0x95, 0xfd, 0x03, 0x47, 0x83, 0xa0 },
+ { 0x07, 0x7c, 0xcc, 0xc1, 0x1b, 0x88, 0x73, 0xed, 0x5c, 0x3d, 0x48, 0xa0 },
+ { 0x29, 0xb6, 0x2a, 0xfe, 0x3c, 0xa0, 0x36, 0xf4, 0xfe, 0x1a, 0x9d, 0xa0 },
+ { 0x60, 0x54, 0xfa, 0xf5, 0xf3, 0x5d, 0x96, 0xd3, 0xb0, 0xc8, 0xc3, 0xe0 },
+ { 0xe2, 0x07, 0x98, 0xe4, 0x31, 0x0e, 0xed, 0x27, 0x88, 0x4a, 0xe9, 0x00 },
+ { 0x77, 0x5c, 0x9c, 0x08, 0xe8, 0x0e, 0x26, 0xdd, 0xae, 0x56, 0x31, 0x80 },
+ { 0xb0, 0xb8, 0x11, 0x02, 0x8c, 0x2b, 0xf9, 0x97, 0x21, 0x34, 0x87, 0xc0 },
+ { 0x18, 0xa0, 0xc9, 0x23, 0x1f, 0xc6, 0x0a, 0xdf, 0x5c, 0x5e, 0xa3, 0x20 },
+ { 0x76, 0x47, 0x1e, 0x83, 0x02, 0xa0, 0x72, 0x1e, 0x01, 0xb1, 0x2b, 0x80 },
+ { 0xff, 0xbc, 0xcb, 0x80, 0xca, 0x83, 0x41, 0xfa, 0xfb, 0x47, 0xb2, 0xe0 },
+ { 0x66, 0xa7, 0x2a, 0x15, 0x8f, 0x93, 0x25, 0xa2, 0xbf, 0x67, 0x17, 0x00 },
+ { 0xc4, 0x24, 0x36, 0x89, 0xfe, 0x85, 0xb1, 0xc5, 0x13, 0x63, 0xa1, 0x80 },
+ { 0x0d, 0xff, 0x73, 0x94, 0x14, 0xd1, 0xa1, 0xb3, 0x4b, 0x1c, 0x27, 0x00 },
+ { 0x15, 0xb4, 0x88, 0x30, 0x63, 0x6c, 0x8b, 0x99, 0x89, 0x49, 0x72, 0xe0 },
+ { 0x29, 0xa8, 0x9c, 0x0d, 0x3d, 0xe8, 0x1d, 0x66, 0x54, 0x89, 0xb0, 0xe0 },
+ { 0x4f, 0x12, 0x6f, 0x37, 0xfa, 0x51, 0xcb, 0xe6, 0x1b, 0xd6, 0xb9, 0x40 },
+ { 0x99, 0xc4, 0x72, 0x39, 0xd0, 0xd9, 0x7d, 0x3c, 0x84, 0xe0, 0x94, 0x00 },
+ { 0x19, 0x19, 0xb7, 0x51, 0x19, 0x76, 0x56, 0x21, 0xbb, 0x4f, 0x1e, 0x80 },
+ { 0x09, 0xdb, 0x12, 0xd7, 0x31, 0xfa, 0xee, 0x0b, 0x86, 0xdf, 0x6b, 0x80 },
+ { 0x48, 0x8f, 0xc3, 0x3d, 0xf4, 0x3f, 0xbd, 0xee, 0xa4, 0xea, 0xfb, 0x40 },
+ { 0x82, 0x74, 0x23, 0xee, 0x40, 0xb6, 0x75, 0xf7, 0x56, 0xeb, 0x5f, 0xe0 },
+ { 0xab, 0xe1, 0x97, 0xc4, 0x84, 0xcb, 0x74, 0x75, 0x71, 0x44, 0xa9, 0xa0 },
+ { 0x2b, 0x50, 0x0e, 0x4b, 0xc0, 0xec, 0x5a, 0x6d, 0x2b, 0xdb, 0xdd, 0x00 },
+ { 0xc4, 0x74, 0xaa, 0x53, 0xd7, 0x02, 0x18, 0x76, 0x16, 0x69, 0x36, 0x00 },
+ { 0x8e, 0xba, 0x1a, 0x13, 0xdb, 0x33, 0x90, 0xbd, 0x67, 0x18, 0xce, 0xc0 },
+ { 0x75, 0x38, 0x44, 0x67, 0x3a, 0x27, 0x78, 0x2c, 0xc4, 0x20, 0x12, 0xe0 },
+ { 0x06, 0xff, 0x83, 0xa1, 0x45, 0xc3, 0x70, 0x35, 0xa5, 0xc1, 0x26, 0x80 },
+ { 0x3b, 0x37, 0x41, 0x78, 0x58, 0xcc, 0x2d, 0xd3, 0x3e, 0xc3, 0xf6, 0x20 },
+ { 0x9a, 0x4a, 0x5a, 0x28, 0xee, 0x17, 0xca, 0x9c, 0x32, 0x48, 0x42, 0xc0 },
+ { 0xbc, 0x29, 0xf4, 0x65, 0x30, 0x9c, 0x97, 0x7e, 0x89, 0x61, 0x0a, 0x40 },
+ { 0x26, 0x63, 0xae, 0x6d, 0xdf, 0x8b, 0x5c, 0xe2, 0xbb, 0x29, 0x48, 0x80 },
+ { 0x46, 0xf2, 0x31, 0xef, 0xe4, 0x57, 0x03, 0x4c, 0x18, 0x14, 0x41, 0x80 },
+ { 0x3f, 0xb2, 0xce, 0x85, 0xab, 0xe9, 0xb0, 0xc7, 0x2e, 0x06, 0xfb, 0xe0 },
+ { 0xde, 0x87, 0x48, 0x1f, 0x28, 0x2c, 0x15, 0x39, 0x71, 0xa0, 0xa2, 0xe0 },
+ { 0xfc, 0xd7, 0xcc, 0xf2, 0x3c, 0x69, 0xfa, 0x99, 0xbb, 0xa1, 0x41, 0x20 },
+ { 0xf0, 0x26, 0x14, 0x47, 0xe9, 0x49, 0x0c, 0xa8, 0xe4, 0x74, 0xce, 0xc0 },
+ { 0x44, 0x10, 0x11, 0x58, 0x18, 0x19, 0x6f, 0x95, 0xcd, 0xd7, 0x01, 0x20 },
+ { 0x08, 0x8f, 0xc3, 0x1d, 0xf4, 0xbf, 0xbd, 0xe2, 0xa4, 0xea, 0xfb, 0x40 },
+ { 0xb8, 0xfe, 0xf1, 0xb6, 0x30, 0x77, 0x29, 0xfb, 0x0a, 0x07, 0x8c, 0x00 },
+ { 0x5a, 0xfe, 0xa7, 0xac, 0xcc, 0xb7, 0x7b, 0xbc, 0x9d, 0x99, 0xa9, 0x00 },
+ { 0x49, 0xa7, 0x01, 0x6a, 0xc6, 0x53, 0xf6, 0x5e, 0xcd, 0xc9, 0x07, 0x60 },
+ { 0x19, 0x44, 0xd0, 0x85, 0xbe, 0x4e, 0x7d, 0xa8, 0xd6, 0xcc, 0x7d, 0x00 },
+ { 0x25, 0x1f, 0x62, 0xad, 0xc4, 0x03, 0x2f, 0x0e, 0xe7, 0x14, 0x00, 0x20 },
+ { 0x56, 0x47, 0x1f, 0x87, 0x02, 0xa0, 0x72, 0x1e, 0x00, 0xb1, 0x2b, 0x80 },
+ { 0x2b, 0x8e, 0x49, 0x23, 0xf2, 0xdd, 0x51, 0xe2, 0xd5, 0x37, 0xfa, 0x00 },
+ { 0x6b, 0x55, 0x0a, 0x40, 0xa6, 0x6f, 0x47, 0x55, 0xde, 0x95, 0xc2, 0x60 },
+ { 0xa1, 0x8a, 0xd2, 0x8d, 0x4e, 0x27, 0xfe, 0x92, 0xa4, 0xf6, 0xc8, 0x40 },
+ { 0x10, 0xc2, 0xe5, 0x86, 0x38, 0x8c, 0xb8, 0x2a, 0x3d, 0x80, 0x75, 0x80 },
+ { 0xef, 0x34, 0xa4, 0x18, 0x17, 0xee, 0x02, 0x13, 0x3d, 0xb2, 0xeb, 0x00 },
+ { 0x7e, 0x9c, 0x0c, 0x54, 0x32, 0x5a, 0x9c, 0x15, 0x83, 0x6e, 0x00, 0x00 },
+ { 0x36, 0x93, 0xe5, 0x72, 0xd1, 0xfd, 0xe4, 0xcd, 0xf0, 0x79, 0xe8, 0x60 },
+ { 0xbf, 0xb2, 0xce, 0xc5, 0xab, 0xe1, 0xb0, 0xc7, 0x2e, 0x07, 0xfb, 0xe0 },
+ { 0x7e, 0xe1, 0x82, 0x30, 0xc5, 0x83, 0xcc, 0xcc, 0x57, 0xd4, 0xb0, 0x80 },
+ { 0xa0, 0x66, 0xcb, 0x2f, 0xed, 0xaf, 0xc9, 0xf5, 0x26, 0x64, 0x12, 0x60 },
+ { 0xbb, 0x23, 0x72, 0x5a, 0xbc, 0x47, 0xcc, 0x5f, 0x4c, 0xc4, 0xcd, 0x20 },
+ { 0xde, 0xd9, 0xdb, 0xa3, 0xbe, 0xe4, 0x0c, 0x59, 0xb5, 0x60, 0x9b, 0x40 },
+ { 0xd9, 0xa7, 0x01, 0x6a, 0xc6, 0x53, 0xe6, 0xde, 0xcd, 0xc9, 0x03, 0x60 },
+ { 0x9a, 0xd4, 0x6a, 0xed, 0x5f, 0x70, 0x7f, 0x28, 0x0a, 0xb5, 0xfc, 0x40 },
+ { 0xe5, 0x92, 0x1c, 0x77, 0x82, 0x25, 0x87, 0x31, 0x6d, 0x7d, 0x3c, 0x20 },
+ { 0x4f, 0x14, 0xda, 0x82, 0x42, 0xa8, 0xb8, 0x6d, 0xca, 0x73, 0x35, 0x20 },
+ { 0x8b, 0x8b, 0x50, 0x7a, 0xd4, 0x67, 0xd4, 0x44, 0x1d, 0xf7, 0x70, 0xe0 },
+ { 0x22, 0x83, 0x1c, 0x9c, 0xf1, 0x16, 0x94, 0x67, 0xad, 0x04, 0xb6, 0x80 },
+ { 0x21, 0x3b, 0x83, 0x8f, 0xe2, 0xae, 0x54, 0xc3, 0x8e, 0xe7, 0x18, 0x00 },
+ { 0x5d, 0x92, 0x6b, 0x6d, 0xd7, 0x1f, 0x08, 0x51, 0x81, 0xa4, 0xe1, 0x20 },
+ { 0x66, 0xab, 0x79, 0xd4, 0xb2, 0x9e, 0xe6, 0xe6, 0x95, 0x09, 0xe5, 0x60 },
+ { 0x95, 0x81, 0x48, 0x68, 0x2d, 0x74, 0x8a, 0x38, 0xdd, 0x68, 0xba, 0xa0 },
+ { 0xb8, 0xce, 0x02, 0x0c, 0xf0, 0x69, 0xc3, 0x2a, 0x72, 0x3a, 0xb1, 0x40 },
+ { 0xf4, 0x33, 0x1d, 0x6d, 0x46, 0x16, 0x07, 0xe9, 0x57, 0x52, 0x74, 0x60 },
+ { 0x6d, 0xa2, 0x3b, 0xa4, 0x24, 0xb9, 0x59, 0x61, 0x33, 0xcf, 0x9c, 0x80 },
+ { 0xa6, 0x36, 0xbc, 0xbc, 0x7b, 0x30, 0xc5, 0xfb, 0xea, 0xe6, 0x7f, 0xe0 },
+ { 0x5c, 0xb0, 0xd8, 0x6a, 0x07, 0xdf, 0x65, 0x4a, 0x90, 0x89, 0xa2, 0x00 },
+ { 0xf1, 0x1f, 0x10, 0x68, 0x48, 0x78, 0x0f, 0xc9, 0xec, 0xdd, 0x80, 0xa0 },
+ { 0x1f, 0xbb, 0x53, 0x64, 0xfb, 0x8d, 0x2c, 0x9d, 0x73, 0x0d, 0x5b, 0xa0 },
+ { 0xfc, 0xb8, 0x6b, 0xc7, 0x0a, 0x50, 0xc9, 0xd0, 0x2a, 0x5d, 0x03, 0x40 },
+ { 0xa5, 0x34, 0x43, 0x30, 0x29, 0xea, 0xc1, 0x5f, 0x32, 0x2e, 0x34, 0xc0 },
+ { 0xc9, 0x89, 0xd9, 0xc7, 0xc3, 0xd3, 0xb8, 0xc5, 0x5d, 0x75, 0x13, 0x00 },
+ { 0x7b, 0xb3, 0x8b, 0x2f, 0x01, 0x86, 0xd4, 0x66, 0x43, 0xae, 0x96, 0x20 },
+ { 0x26, 0x44, 0xeb, 0xad, 0xeb, 0x44, 0xb9, 0x46, 0x7d, 0x1f, 0x42, 0xc0 },
+ { 0x60, 0x8c, 0xc8, 0x57, 0x59, 0x4b, 0xfb, 0xb5, 0x5d, 0x69, 0x60, 0x00 }
+};
+
+// 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[FTX_LDPC_N][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_LDPCNumRows[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
+};
\ No newline at end of file
diff --git a/ft8CN/app/src/main/cpp/ft8/constants.h b/ft8CN/app/src/main/cpp/ft8/constants.h
new file mode 100644
index 0000000..a97dd50
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/constants.h
@@ -0,0 +1,89 @@
+#ifndef _INCLUDE_CONSTANTS_H_
+#define _INCLUDE_CONSTANTS_H_
+
+#include
+#include
+
+typedef enum
+{
+ PROTO_FT4,
+ PROTO_FT8
+} ftx_protocol_t;
+#define kMin_score (10) /// 候选人的最低同步分数阈值。Minimum sync score threshold for candidates
+#define FT8_SAMPLE_RATE (12000)//采样率
+#define FT8_SYMBOL_PERIOD (0.160f) ///< FT8 symbol duration, defines tone deviation in Hz and symbol rate
+#define FT8_SLOT_TIME (15.0f) ///< FT8 slot period
+
+#define FT4_SYMBOL_PERIOD (0.048f) ///< FT4 symbol duration, defines tone deviation in Hz and symbol rate
+#define FT4_SLOT_TIME (7.5f) ///< FT4 slot period
+
+// Define FT8 symbol counts
+// FT8 message structure:
+// S D1 S D2 S
+// S - sync block (7 symbols of Costas pattern)
+// D1 - first data block (29 symbols each encoding 3 bits)
+#define FT8_ND (58) ///< Data symbols
+#define FT8_NN (79) ///< Total channel symbols (FT8_NS + FT8_ND)
+#define FT8_LENGTH_SYNC (7) ///< Length of each sync group
+#define FT8_NUM_SYNC (3) ///< Number of sync groups
+#define FT8_SYNC_OFFSET (36) ///< Offset between sync groups
+#define FT8_SYMBOL_BT (2.0f)
+
+// Define FT4 symbol counts
+// FT4 message structure:
+// R Sa D1 Sb D2 Sc D3 Sd R
+// R - ramping symbol (no payload information conveyed)
+// Sx - one of four _different_ sync blocks (4 symbols of Costas pattern)
+// Dy - data block (29 symbols each encoding 2 bits)
+#define FT4_ND (87) ///< Data symbols
+#define FT4_NR (2) ///< Ramp symbols (beginning + end)
+#define FT4_NN (105) ///< Total channel symbols (FT4_NS + FT4_ND + FT4_NR)
+#define FT4_LENGTH_SYNC (4) ///< Length of each sync group
+#define FT4_NUM_SYNC (4) ///< Number of sync groups
+#define FT4_SYNC_OFFSET (33) ///< Offset between sync groups
+
+// Define LDPC parameters
+#define FTX_LDPC_N (174) ///编码消息中的位数(LDPC校验和位的有效负载)< Number of bits in the encoded message (payload with LDPC checksum bits)
+#define FTX_LDPC_K (91) ///< 有效负载位数(包括CRC)Number of payload bits (including CRC)
+#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) ///< 存储91位所需的整字节数(仅限有效负载+CRC)Number of whole bytes needed to store 91 bits (payload + CRC only)
+
+// Define CRC parameters
+#define FT8_CRC_POLYNOMIAL ((uint16_t)0x2757u) ///< CRC-14 polynomial without the leading (MSB) 1
+#define FT8_CRC_WIDTH (14)
+
+#define DECODE_TIME_OUT (1) ///解码迭代时间超时(秒)
+
+/// Costas 7x7 tone pattern for synchronization
+extern const uint8_t kFT8CostasPattern[7];
+extern const uint8_t kFT4CostasPattern[4][4];
+
+/// Gray code map to encode 8 symbols (tones)
+extern const uint8_t kFT8GrayMap[8];
+extern const uint8_t kFT4GrayMap[4];
+
+extern const uint8_t kFT4XORSequence[10];
+
+/// Parity generator matrix for (174,91) LDPC code, stored in bitpacked format (MSB first)
+extern const uint8_t kFTXLDPCGenerator[FTX_LDPC_M][FTX_LDPC_K_BYTES];
+
+/// LDPC(174,91) parity check matrix, containing 83 rows,
+/// each row describes one parity check,
+/// each number is an index into the codeword (1-origin).
+/// The codeword bits mentioned in each row must xor to zero.
+/// From WSJT-X's ldpc_174_91_c_reordered_parity.f90.
+extern const uint8_t kFTX_LDPC_Nm[FTX_LDPC_M][7];
+
+/// Mn from WSJT-X's bpdecode174.f90. Each row corresponds to a codeword bit.
+/// The numbers indicate which three parity checks (rows in Nm) refer to the codeword bit.
+/// The numbers use 1 as the origin (first entry).
+extern const uint8_t kFTX_LDPC_Mn[FTX_LDPC_N][3];
+
+/// Number of rows (columns in C/C++) in the array Nm.
+extern const uint8_t kFTX_LDPCNumRows[FTX_LDPC_M];
+
+#endif // _INCLUDE_CONSTANTS_H_
+
+
+
diff --git a/ft8CN/app/src/main/cpp/ft8/crc.c b/ft8CN/app/src/main/cpp/ft8/crc.c
new file mode 100644
index 0000000..72a4c89
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/crc.c
@@ -0,0 +1,63 @@
+#include "crc.h"
+#include "constants.h"
+
+#define TOPBIT (1u << (FT8_CRC_WIDTH - 1))
+
+// Compute 14-bit CRC for a sequence of given number of bits
+// Adapted from https://barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code
+// [IN] message - byte sequence (MSB first)
+// [IN] num_bits - number of bits in the sequence
+uint16_t ftx_compute_crc(const uint8_t message[], int num_bits)
+{
+ uint16_t remainder = 0;
+ int idx_byte = 0;
+
+ // Perform modulo-2 division, a bit at a time.
+ for (int idx_bit = 0; idx_bit < num_bits; ++idx_bit)
+ {
+ if (idx_bit % 8 == 0)
+ {
+ // Bring the next byte into the remainder.
+ remainder ^= (message[idx_byte] << (FT8_CRC_WIDTH - 8));
+ ++idx_byte;
+ }
+
+ // Try to divide the current data bit.
+ if (remainder & TOPBIT)
+ {
+ remainder = (remainder << 1) ^ FT8_CRC_POLYNOMIAL;
+ }
+ else
+ {
+ remainder = (remainder << 1);
+ }
+ }
+
+ return remainder & ((TOPBIT << 1) - 1u);
+}
+
+uint16_t ftx_extract_crc(const uint8_t a91[])
+{
+ uint16_t chksum = ((a91[9] & 0x07) << 11) | (a91[10] << 3) | (a91[11] >> 5);
+ return chksum;
+}
+
+void ftx_add_crc(const uint8_t payload[], uint8_t a91[])
+{
+ // Copy 77 bits of payload data
+ for (int i = 0; i < 10; i++)
+ a91[i] = payload[i];
+
+ // Clear 3 bits after the payload to make 82 bits
+ a91[9] &= 0xF8u;
+ a91[10] = 0;
+
+ // Calculate CRC of 82 bits (77 + 5 zeros)
+ // 'The CRC is calculated on the source-encoded message, zero-extended from 77 to 82 bits'
+ uint16_t checksum = ftx_compute_crc(a91, 96 - 14);
+
+ // Store the CRC at the end of 77 bit message
+ a91[9] |= (uint8_t)(checksum >> 11);
+ a91[10] = (uint8_t)(checksum >> 3);
+ a91[11] = (uint8_t)(checksum << 5);
+}
diff --git a/ft8CN/app/src/main/cpp/ft8/crc.h b/ft8CN/app/src/main/cpp/ft8/crc.h
new file mode 100644
index 0000000..99db708
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/crc.h
@@ -0,0 +1,23 @@
+#ifndef _INCLUDE_CRC_H_
+#define _INCLUDE_CRC_H_
+
+#include
+#include
+
+// Compute 14-bit CRC for a sequence of given number of bits using FT8/FT4 CRC polynomial
+// [IN] message - byte sequence (MSB first)
+// [IN] num_bits - number of bits in the sequence
+uint16_t ftx_compute_crc(const uint8_t message[], int num_bits);
+
+/// Extract the FT8/FT4 CRC of a packed message (during decoding)
+/// 提取压缩消息的FT8/FT4 CRC(解码期间)
+/// @param[in] a91 77 bits of payload data + CRC
+/// @return Extracted CRC
+uint16_t ftx_extract_crc(const uint8_t a91[]);
+
+/// Add FT8/FT4 CRC to a packed message (during encoding)
+/// @param[in] payload 77 bits of payload data
+/// @param[out] a91 91 bits of payload data + CRC
+void ftx_add_crc(const uint8_t payload[], uint8_t a91[]);
+
+#endif // _INCLUDE_CRC_H_
\ No newline at end of file
diff --git a/ft8CN/app/src/main/cpp/ft8/decode.c b/ft8CN/app/src/main/cpp/ft8/decode.c
new file mode 100644
index 0000000..a5e69bb
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/decode.c
@@ -0,0 +1,628 @@
+#include "decode.h"
+#include "constants.h"
+#include "crc.h"
+#include "ldpc.h"
+#include "unpack.h"
+
+#include
+#include
+#include "../common/debug.h"
+#include "hash22.h"
+
+/// Compute log likelihood log(p(1) / p(0)) of 174 message bits for later use in soft-decision LDPC decoding
+/// @param[in] wf Waterfall data collected during message slot
+/// @param[in] cand Candidate to extract the message from
+/// @param[in] code_map Symbol encoding map
+/// @param[out] log174 Output of decoded log likelihoods for each of the 174 message bits
+static void ft4_extract_likelihood(const waterfall_t *wf, const candidate_t *cand, float *log174);
+
+static void ft8_extract_likelihood(const waterfall_t *wf, candidate_t *cand, float *log174);
+
+/// Packs a string of bits each represented as a zero/non-zero byte in bit_array[],
+/// as a string of packed bits starting from the MSB of the first byte of packed[]
+/// @param[in] plain Array of bits (0 and nonzero values) with num_bits entires
+/// @param[in] num_bits Number of bits (entries) passed in bit_array
+/// @param[out] packed Byte-packed bits representing the data in bit_array
+static void pack_bits(const uint8_t bit_array[], int num_bits, uint8_t packed[]);
+
+static float max2(float a, float b);
+
+static float max4(float a, float b, float c, float d);
+
+static void heapify_down(candidate_t heap[], int heap_size);
+
+static void heapify_up(candidate_t heap[], int heap_size);
+
+static void ftx_normalize_logl(float *log174);
+
+static void ft4_extract_symbol(const uint8_t *wf, float *logl);
+
+static void ft8_extract_symbol(const uint8_t *wf, float *logl);
+
+static void
+ft8_decode_multi_symbols(const uint8_t *wf, int num_bins, int n_syms, int bit_idx, float *log174);
+
+static int get_index(const waterfall_t *wf, const candidate_t *candidate) {
+ int offset = candidate->time_offset;//time_offset:-12 ~ 23,(costas阵列7个符号+29个数据符号=36)
+ offset = (offset * wf->time_osr) + candidate->time_sub;//time_sub:0~1
+ offset = (offset * wf->freq_osr) + candidate->freq_sub;//freq_sub:0~1
+ offset = (offset * wf->num_bins) +
+ candidate->freq_offset;//num_bins:960,freq_offset:0~ 960(-1) -7
+ return offset;
+}
+
+
+static int ft8_sync_score(const waterfall_t *wf, candidate_t *candidate) {
+ /*
+ * ft8本应有58个符号,但在开始(0-7)、中间(36-43)、结尾(72-79)加了科斯塔阵列,所以共有79个符号,
+ *此函数在4层循环中执行。时间采样率2*频率采样率2*时间偏移(-12~24=36)*频率偏移(num_bins:960-7)
+ */
+ int score = 0;
+ int num_average = 0;
+
+ float signal = 0;
+ float noise = 0;
+
+ // Get the pointer to symbol 0 of the candidate
+ //获取指向候选符号0的指针,在mag数组中取candidate对应的mag数据。
+ const uint8_t *mag_cand = wf->mag + get_index(wf, candidate);
+ //用于信号量和噪音的计算,暂时注释掉
+ // const float *mag_signal = wf->mag2 + get_index(wf, candidate);
+
+
+ // Compute average score over sync symbols (m+k = 0-7, 36-43, 72-79)
+ //计算同步符号的平均分数(m+k=0-7、36-43、72-79)
+ //m=0~2(3组),k=0~6(7个符号)
+ for (int m = 0; m < FT8_NUM_SYNC; ++m) {
+ for (int k = 0; k < FT8_LENGTH_SYNC; ++k) {
+ //FT8_SYNC_OFFSET=36,block=0..6,36~43,72~79,这是costas阵列在符号序列中的索引
+ int block = (FT8_SYNC_OFFSET * m) + k; // 相对于消息relative to the message
+ int block_abs =
+ //time_offset=-12.。23(36个)
+ candidate->time_offset + block; // 相对于捕获的信号relative to the captured signal
+ // Check for time boundaries
+ //检查时间界限
+ if (block_abs < 0)
+ continue;
+ if (block_abs >= wf->num_blocks)
+ break;
+
+ // Get the pointer to symbol 'block' of the candidate
+ //获取指向候选人符号“block”的指针
+ const uint8_t *p8 = mag_cand + (block * wf->block_stride);
+
+ // Weighted difference between the expected and all other symbols
+ //预期符号和所有其他符号之间的加权差
+ // Does not work as well as the alternative score below
+ //效果不如下面的备选分数
+ // score += 8 * p8[kFT8CostasPattern[k]] -
+ // p8[0] - p8[1] - p8[2] - p8[3] -
+ // p8[4] - p8[5] - p8[6] - p8[7];
+ // ++num_average;
+
+ // Check only the neighbors of the expected symbol frequency- and time-wise
+ //仅检查预期符号频率和时间的相邻项,k=0..6
+ int sm = kFT8CostasPattern[k]; //预期数据的索引 Index of the expected bin
+
+
+ //此处计算信号量和噪音,可能不正确,暂时注释掉
+ // const float *p8Signal = mag_signal + (block * wf->block_stride);
+
+
+
+ //通过sm判断相邻频率的信号量是否小于本位置的信号量,小于就加分
+ if (sm > 0) {
+ // look at one frequency bin lower
+ //信号量的差值。
+ score += p8[sm] - p8[sm - 1];
+ ++num_average;
+ }
+ if (sm < 7) {
+ // look at one frequency bin higher
+ score += p8[sm] - p8[sm + 1];
+ ++num_average;
+ }
+ //判断前后符号时间频率信号量是否小于本位置的信号量,小于就加分
+ if ((k > 0) && (block_abs > 0)) {
+ // look one symbol back in time
+ score += p8[sm] - p8[sm - wf->block_stride];
+
+ ++num_average;
+ }
+ if (((k + 1) < FT8_LENGTH_SYNC) && ((block_abs + 1) < wf->num_blocks)) {
+ // look one symbol forward in time
+ score += p8[sm] - p8[sm + wf->block_stride];
+ ++num_average;
+ }
+ }
+ }
+
+ if (num_average > 0) {
+ score /= num_average;
+ }
+
+ return score;
+}
+
+static int ft4_sync_score(const waterfall_t *wf, const candidate_t *candidate) {
+ int score = 0;
+ int num_average = 0;
+
+ // Get the pointer to symbol 0 of the candidate
+ const uint8_t *mag_cand = wf->mag + get_index(wf, candidate);
+
+ // Compute average score over sync symbols (block = 1-4, 34-37, 67-70, 100-103)
+ for (int m = 0; m < FT4_NUM_SYNC; ++m) {
+ for (int k = 0; k < FT4_LENGTH_SYNC; ++k) {
+ int block = 1 + (FT4_SYNC_OFFSET * m) + k;
+ int block_abs = candidate->time_offset + block;
+ // Check for time boundaries
+ if (block_abs < 0)
+ continue;
+ if (block_abs >= wf->num_blocks)
+ break;
+
+ // Get the pointer to symbol 'block' of the candidate
+ const uint8_t *p4 = mag_cand + (block * wf->block_stride);
+
+ int sm = kFT4CostasPattern[m][k]; // Index of the expected bin
+
+ // score += (4 * p4[sm]) - p4[0] - p4[1] - p4[2] - p4[3];
+ // num_average += 4;
+
+ // Check only the neighbors of the expected symbol frequency- and time-wise
+ if (sm > 0) {
+ // look at one frequency bin lower
+ score += p4[sm] - p4[sm - 1];
+ ++num_average;
+ }
+ if (sm < 3) {
+ // look at one frequency bin higher
+ score += p4[sm] - p4[sm + 1];
+ ++num_average;
+ }
+ if ((k > 0) && (block_abs > 0)) {
+ // look one symbol back in time
+ score += p4[sm] - p4[sm - wf->block_stride];
+ ++num_average;
+ }
+ if (((k + 1) < FT4_LENGTH_SYNC) && ((block_abs + 1) < wf->num_blocks)) {
+ // look one symbol forward in time
+ score += p4[sm] - p4[sm + wf->block_stride];
+ ++num_average;
+ }
+ }
+ }
+
+ if (num_average > 0)
+ score /= num_average;
+
+ return score;
+}
+
+//检测ft8信号,num_candidates最大候选人数量=120,heap[]候选人列表(size=120),kMin_score候选人的最低同步分数阈值=10
+int ft8_find_sync(const waterfall_t *wf, int num_candidates, candidate_t heap[], int min_score) {
+ int heap_size = 0;
+ candidate_t candidate;//候选人
+ // Here we allow time offsets that exceed signal boundaries, as long as we still have all data bits.
+ // I.e. we can afford to skip the first 7 or the last 7 Costas symbols, as long as we track how many
+ // sync symbols we included in the score, so the score is averaged.
+ //在这里,我们允许超过信号边界的时间偏移,只要我们仍然拥有所有数据位。
+ //也就是说,我们可以跳过前7个或最后7个Costas符号,只要我们跟踪有多少个
+ //我们在分数中包含了同步符号,所以分数是平均值。
+ //循环:时间过采样*频率过采样*前36个符号(7同步+29信息)*fft频率偏移=2*2*36*960=3840*36=138240
+ for (candidate.time_sub = 0; candidate.time_sub < wf->time_osr; ++candidate.time_sub) {
+ for (candidate.freq_sub = 0; candidate.freq_sub < wf->freq_osr; ++candidate.freq_sub) {
+ for (candidate.time_offset = -12; candidate.time_offset < 24; ++candidate.time_offset) {
+ for (candidate.freq_offset = 0;
+ (candidate.freq_offset + 7) < wf->num_bins; ++candidate.freq_offset) {
+ if (wf->protocol == PROTO_FT4) {
+ candidate.score = ft4_sync_score(wf, &candidate);
+ } else {
+ candidate.score = ft8_sync_score(wf, &candidate);
+ }
+
+ if (candidate.score < min_score)
+ continue;
+
+ // If the heap is full AND the current candidate is better than
+ // the worst in the heap, we remove the worst and make space
+ //如果堆已满且当前候选堆优于在堆中最坏的,我们移除最坏的,并创造空间
+ if (heap_size == num_candidates && candidate.score > heap[0].score) {
+ heap[0] = heap[heap_size - 1];
+ --heap_size;
+ //降序?
+ heapify_down(heap, heap_size);
+ }
+
+ // If there's free space in the heap, we add the current candidate
+ //如果堆中有可用空间,我们将添加当前候选堆
+ if (heap_size < num_candidates) {
+ heap[heap_size] = candidate;
+ ++heap_size;
+ //升序?
+ heapify_up(heap, heap_size);
+ }
+ }
+ }
+ }
+ }
+
+
+
+ // Sort the candidates by sync strength - here we benefit from the heap structure
+ int len_unsorted = heap_size;
+ while (len_unsorted > 1) {
+ candidate_t tmp = heap[len_unsorted - 1];
+ heap[len_unsorted - 1] = heap[0];
+ heap[0] = tmp;
+ len_unsorted--;
+ heapify_down(heap, len_unsorted);
+ }
+
+ return heap_size;
+}
+
+static void ft4_extract_likelihood(const waterfall_t *wf, const candidate_t *cand, float *log174) {
+ const uint8_t *mag_cand = wf->mag + get_index(wf, cand);
+
+ // Go over FSK tones and skip Costas sync symbols
+ for (int k = 0; k < FT4_ND; ++k) {
+ // Skip either 5, 9 or 13 sync symbols
+ // TODO: replace magic numbers with constants
+ int sym_idx = k + ((k < 29) ? 5 : ((k < 58) ? 9 : 13));
+ int bit_idx = 2 * k;
+
+ // Check for time boundaries
+ int block = cand->time_offset + sym_idx;
+ if ((block < 0) || (block >= wf->num_blocks)) {
+ log174[bit_idx + 0] = 0;
+ log174[bit_idx + 1] = 0;
+ } else {
+ // Pointer to 4 bins of the current symbol
+ const uint8_t *ps = mag_cand + (sym_idx * wf->block_stride);
+
+ ft4_extract_symbol(ps, log174 + bit_idx);
+ }
+ }
+}
+
+//解开可能的FT8信号
+static void ft8_extract_likelihood(const waterfall_t *wf, candidate_t *cand, float *log174) {
+ const uint8_t *mag_cand = wf->mag + get_index(wf, cand);
+
+ ////FT8总消息的为174位,符号是174/3=58个,加上同步costas阵列的7*3=21个符号,共计58+21=79个符号。
+ //log174数组的大小是174
+ // Go over FSK tones and skip Costas sync symbols
+ //浏览FSK音调并跳过Costas同步符号,所以log174
+ //FT8_ND=58,k=0..57
+ for (int k = 0; k < FT8_ND; ++k) {
+ // Skip either 7 or 14 sync symbols
+ // TODO: replace magic numbers with constants
+ //sym_idx=7..35,43..71
+ int sym_idx = k + ((k < 29) ? 7 : 14);
+ //bit_idx符号位的索引
+ int bit_idx = 3 * k;
+
+ // Check for time boundaries
+ //检测时间边界
+ int block = cand->time_offset + sym_idx;
+ if ((block < 0) || (block >= wf->num_blocks)) {
+ log174[bit_idx + 0] = 0;
+ log174[bit_idx + 1] = 0;
+ log174[bit_idx + 2] = 0;
+ } else {
+ // Pointer to 8 bins of the current symbol
+ //指向当前符号信号量的8个箱子的指针
+ //block_stride=960*2*2=3840
+ const uint8_t *ps = mag_cand + (sym_idx * wf->block_stride);
+
+ //每个符号,bit_idx是符号的3倍
+ ft8_extract_symbol(ps, log174 + bit_idx);
+ }
+ }
+}
+
+static void ftx_normalize_logl(float *log174) {
+ // Compute the variance of log174
+ //计算log174的方差
+ float sum = 0;
+ float sum2 = 0;
+ //FTX_LDPC_N=174
+ for (int i = 0; i < FTX_LDPC_N; ++i) {
+ sum += log174[i];//取和
+ sum2 += log174[i] * log174[i];//取平方和
+ }
+ float inv_n = 1.0f / FTX_LDPC_N;
+ //variance方差
+ float variance = (sum2 - (sum * sum * inv_n)) * inv_n;
+
+ // Normalize log174 distribution and scale it with experimentally found coefficient
+ ////规范化log174分布,并用实验发现的系数对其进行缩放
+ float norm_factor = sqrtf(24.0f / variance);
+ for (int i = 0; i < FTX_LDPC_N; ++i) {
+ log174[i] *= norm_factor;
+ }
+}
+
+//推算snr
+static void ft8_guess_snr(const waterfall_t *wf, candidate_t *cand) {
+ const float *mag_signal = wf->mag2 + get_index(wf, cand);
+
+
+ float signal = 0, noise = 0;
+ for (int i = 0; i < 7; ++i) {
+ if ((cand->time_offset + i >= 0) && (cand->time_offset + i < wf->num_blocks + 8)) {
+ //LOG_PRINTF("End guess SNR 0...");
+ signal += mag_signal[(i) * wf->block_stride + kFT8CostasPattern[i]];
+ noise += mag_signal[(i) * wf->block_stride + ((kFT8CostasPattern[i] + 4) % 8)];
+ //LOG_PRINTF("End guess SNR 0... done");
+ }
+ if ((cand->time_offset + i + 36 >= 0) && (cand->time_offset + i < wf->num_blocks + 8)) {
+ //LOG_PRINTF("End guess SNR 36...");
+ signal += mag_signal[(i + 36) * wf->block_stride + kFT8CostasPattern[i]];
+ noise += mag_signal[(i + 36) * wf->block_stride + ((kFT8CostasPattern[i] + 4) % 8)];
+ //LOG_PRINTF("End guess SNR 36... done");
+ }
+ //此处容易产生数组下标越界的问题
+// if ((cand->time_offset+i+72>=0)&&(cand->time_offset+inum_blocks+8)) {
+// LOG_PRINTF("End guess SNR 72...");
+// signal += mag_signal[(i + 72) * wf->block_stride + kFT8CostasPattern[i]];
+// noise += mag_signal[(i + 72) * wf->block_stride + ((kFT8CostasPattern[i] + 4) % 8)];
+// LOG_PRINTF("End guess SNR 72... done");
+// }
+ }
+ //LOG(LOG_INFO, "Max magnitude:ft8_guess_snr 002\n");
+ if (noise != 0) {
+ float raw = signal / noise;
+ cand->snr = floor(10 * log10f(1E-12f + raw) - 24 + 0.5);
+ if (cand->snr < -30) {//-30是最小值了。
+ cand->snr = -30;
+ }
+ } else {
+ cand->snr = -100;
+ }
+}
+
+//max_iterations=20 LDPC的迭代次数。
+bool
+ft8_decode(waterfall_t *wf, candidate_t *cand, message_t *message, int max_iterations,
+ decode_status_t *status) {
+ //FT8总消息的为174位,符号是174/3=58个,加上同步costas阵列的7*3=21个符号,共计58+21=79个符号。
+ //FTX_LDPC_N=174,是把7*3个符号的位去掉后的数组,
+ float log174[FTX_LDPC_N]; //编码为似然的消息位 message bits encoded as likelihood
+ if (wf->protocol == PROTO_FT4) {
+ ft4_extract_likelihood(wf, cand, log174);
+ } else {
+ //检测可能的FT8信号,结果在log174中,每3个为一组,与8个格雷码为索引的信号量的平方差的值
+ ft8_extract_likelihood(wf, cand, log174);
+ }
+
+ //规范化
+ ftx_normalize_logl(log174);
+
+ //FTX_LDPC_N=174
+ uint8_t plain174[FTX_LDPC_N]; // message bits (0/1)
+
+ //bp_decode是原作者采用的,ldpc_decode经测试也是可以用的。
+ //结果在plain174中,以0和1为值。包括77位信息+14位冗余校验+83位前向纠错=174位。
+ //max_iterations是最大迭代次数,越大速度越慢,精度越高
+ bp_decode(log174, max_iterations, plain174, &status->ldpc_errors);
+ //ldpc_decode(log174, max_iterations, plain174, &status->ldpc_errors);
+
+ if (status->ldpc_errors > 0) {
+ return false;
+ }
+
+ // Extract payload + CRC (first FTX_LDPC_K bits) packed into a byte array
+ ////提取压缩到字节数组中的有效负载+CRC(第一个FTX\U LDPC\U K位)
+ ////FTX_LDPC_K_BYTES:存储91位所需的整字节数(仅限有效负载+CRC)
+ ////FTX_LDPC_K有效负载位数(包括CRC)
+ uint8_t a91[FTX_LDPC_K_BYTES];
+ //提取出91个位,77位信息+14位冗余校验
+ pack_bits(plain174, FTX_LDPC_K, a91);
+
+ // Extract CRC and check it
+ ////提取CRC并进行检查,后面crc_extracted又作为hash值保存下来
+ status->crc_extracted = ftx_extract_crc(a91);
+
+ // [1]: 'The CRC is calculated on the source-encoded message, zero-extended from 77 to 82 bits.'
+ a91[9] &= 0xF8;
+ a91[10] &= 0x00;
+ status->crc_calculated = ftx_compute_crc(a91, 96 - 14);
+
+ if (status->crc_extracted != status->crc_calculated) {
+ return false;
+ }
+
+ if (wf->protocol == PROTO_FT4) {
+ // '[..] for FT4 only, in order to avoid transmitting a long string of zeros when sending CQ messages,
+ // the assembled 77-bit message is bitwise exclusive-OR’ed with [a] pseudorandom sequence before computing the CRC and FEC parity bits'
+ for (int i = 0; i < 10; ++i) {
+ a91[i] ^= kFT4XORSequence[i];
+ }
+ }
+
+ //从91位解包77位信息,然后返回消息的文本内容。
+ //status->unpack_status = unpack77(a91, message->text);
+ message->call_to[0] = message->call_de[0] = message->maidenGrid[0] = message->extra[0] = '\0';
+ message->call_de_hash.hash10 = message->call_de_hash.hash12 = message->call_de_hash.hash22 = 0;
+ message->call_to_hash.hash10 = message->call_to_hash.hash12 = message->call_to_hash.hash22 = 0;
+ memcpy(message->a91, a91, FTX_LDPC_K_BYTES);//把数据包保存下来,用于音频相减
+
+ //LOG_PRINTF("hex:%0x %0x %0x %0x %0x %0x %0x %0x %0x %0x"
+ // ,a91[0],a91[1],a91[2],a91[3],a91[4],a91[5],a91[6],a91[7],a91[8],a91[9]);
+
+
+ status->unpack_status = unpackToMessage_t(a91, message);
+ //message->call_de_hash.hash12=hashcall(message->call_de,HASH_12) ;
+
+ if (status->unpack_status < 0) {
+ return false;
+ }
+
+ // Reuse binary message CRC as hash value for the message
+ //重用二进制消息CRC作为消息的哈希值
+ message->hash = status->crc_extracted;
+
+
+ //2022-05-13增加解析i3,n3
+ //解出i3和n3
+ // Extract i3 (bits 74..76)
+ //message->i3 = (a91[9] >> 3) & 0x07;
+ // Extract n3 (bits 71..73)
+ //message->n3 = ((a91[8] << 2) & 0x04) | ((a91[9] >> 6) & 0x03);
+ //推算信噪比
+ ft8_guess_snr(wf, cand);
+
+ return true;
+}
+
+static float max2(float a, float b) {
+ return (a >= b) ? a : b;
+}
+
+static float max4(float a, float b, float c, float d) {
+ return max2(max2(a, b), max2(c, d));
+}
+
+static void heapify_down(candidate_t heap[], int heap_size) {
+ // heapify from the root down
+ int current = 0;
+ while (true) {
+ int largest = current;
+ int left = 2 * current + 1;
+ int right = left + 1;
+
+ if (left < heap_size && heap[left].score < heap[largest].score) {
+ largest = left;
+ }
+ if (right < heap_size && heap[right].score < heap[largest].score) {
+ largest = right;
+ }
+ if (largest == current) {
+ break;
+ }
+
+ candidate_t tmp = heap[largest];
+ heap[largest] = heap[current];
+ heap[current] = tmp;
+ current = largest;
+ }
+}
+
+static void heapify_up(candidate_t heap[], int heap_size) {
+ // heapify from the last node up
+ int current = heap_size - 1;
+ while (current > 0) {
+ int parent = (current - 1) / 2;
+ if (heap[current].score >= heap[parent].score) {
+ break;
+ }
+
+ candidate_t tmp = heap[parent];
+ heap[parent] = heap[current];
+ heap[current] = tmp;
+ current = parent;
+ }
+}
+
+// Compute unnormalized log likelihood log(p(1) / p(0)) of 2 message bits (1 FSK symbol)
+static void ft4_extract_symbol(const uint8_t *wf, float *logl) {
+ // Cleaned up code for the simple case of n_syms==1
+ float s2[4];
+
+ for (int j = 0; j < 4; ++j) {
+ s2[j] = (float) wf[kFT4GrayMap[j]];
+ }
+
+ logl[0] = max2(s2[2], s2[3]) - max2(s2[0], s2[1]);
+ logl[1] = max2(s2[1], s2[3]) - max2(s2[0], s2[2]);
+}
+
+// Compute unnormalized log likelihood log(p(1) / p(0)) of 3 message bits (1 FSK symbol)
+//计算3个消息位(1个FSK符号)的非规范化对数似然对数log((p(1)/p(0))
+//wf当前符号的信号量的地址,logl当前符号的位数组的地址。
+static void ft8_extract_symbol(const uint8_t *wf, float *logl) {
+ // Cleaned up code for the simple case of n_syms==1
+ //清理了n_syms==1简单案例的代码
+ float s2[8];//信号强度数组,格雷码数组内容做偏移索引:{ 0, 1, 3, 2, 5, 6, 4, 7 }
+
+ for (int j = 0; j < 8; ++j) {
+ s2[j] = (float) wf[kFT8GrayMap[j]];//格雷码值作索引,对应信号的强度保存到
+ }
+ //信号量的值在之前已经是平方过的了,相减实际上是log(p(1)/p(0))。
+ 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]);
+}
+
+// Compute unnormalized log likelihood log(p(1) / p(0)) of bits corresponding to several FSK symbols at once
+static void
+ft8_decode_multi_symbols(const uint8_t *wf, int num_bins, int n_syms, int bit_idx, float *log174) {
+ const int n_bits = 3 * n_syms;
+ const int n_tones = (1 << n_bits);
+
+ float s2[n_tones];
+
+ for (int j = 0; j < n_tones; ++j) {
+ int j1 = j & 0x07;
+ if (n_syms == 1) {
+ s2[j] = (float) wf[kFT8GrayMap[j1]];
+ continue;
+ }
+ int j2 = (j >> 3) & 0x07;
+ if (n_syms == 2) {
+ s2[j] = (float) wf[kFT8GrayMap[j2]];
+ s2[j] += (float) wf[kFT8GrayMap[j1] + 4 * num_bins];
+ continue;
+ }
+ int j3 = (j >> 6) & 0x07;
+ s2[j] = (float) wf[kFT8GrayMap[j3]];
+ s2[j] += (float) wf[kFT8GrayMap[j2] + 4 * num_bins];
+ s2[j] += (float) wf[kFT8GrayMap[j1] + 8 * num_bins];
+ }
+
+ // Extract bit significance (and convert them to float)
+ // 8 FSK tones = 3 bits
+ for (int i = 0; i < n_bits; ++i) {
+ if (bit_idx + i >= FTX_LDPC_N) {
+ // Respect array size
+ break;
+ }
+
+ uint16_t mask = (n_tones >> (i + 1));
+ float max_zero = -1000, max_one = -1000;
+ for (int n = 0; n < n_tones; ++n) {
+ if (n & mask) {
+ max_one = max2(max_one, s2[n]);
+ } else {
+ max_zero = max2(max_zero, s2[n]);
+ }
+ }
+
+ log174[bit_idx + i] = max_one - max_zero;
+ }
+}
+
+// Packs a string of bits each represented as a zero/non-zero byte in plain[],
+// as a string of packed bits starting from the MSB of the first byte of packed[]
+static void pack_bits(const uint8_t bit_array[], int num_bits, uint8_t packed[]) {
+ int num_bytes = (num_bits + 7) / 8;
+ for (int i = 0; i < num_bytes; ++i) {
+ packed[i] = 0;
+ }
+
+ uint8_t mask = 0x80;
+ int byte_idx = 0;
+ for (int i = 0; i < num_bits; ++i) {
+ if (bit_array[i]) {
+ packed[byte_idx] |= mask;
+ }
+ mask >>= 1;
+ if (!mask) {
+ mask = 0x80;
+ ++byte_idx;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ft8CN/app/src/main/cpp/ft8/decode.h b/ft8CN/app/src/main/cpp/ft8/decode.h
new file mode 100644
index 0000000..eea7b7e
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/decode.h
@@ -0,0 +1,121 @@
+#ifndef _INCLUDE_DECODE_H_
+#define _INCLUDE_DECODE_H_
+
+#include
+#include
+
+#include "constants.h"
+#include "../fft/kiss_fft.h"
+#include "../common/debug.h"
+/// Input structure to ft8_find_sync() function. This structure describes stored waterfall data over the whole message slot.
+/// Fields time_osr and freq_osr specify additional oversampling rate for time and frequency resolution.
+/// If time_osr=1, FFT magnitude data is collected once for every symbol transmitted, i.e. every 1/6.25 = 0.16 seconds.
+/// Values time_osr > 1 mean each symbol is further subdivided in time.
+/// If freq_osr=1, each bin in the FFT magnitude data corresponds to 6.25 Hz, which is the tone spacing.
+/// Values freq_osr > 1 mean the tone spacing is further subdivided by FFT analysis.
+
+/// ft8_find_sync()函数的输入结构。这种结构描述了整个消息槽中存储的瀑布数据。
+/// time_osr和freq_osr字段指定时间和频率分辨率的额外过采样率。
+/// 如果time_osr=1,则针对每个传输的符号收集一次FFT幅度数据,即每1/6.25=0.16秒收集一次。FSK符号的时间长度是0.16秒。
+/// 如果time_osr>1表示每个符号在时间上进一步过采样。
+/// 如果freq_osr=1,FFT幅度数据中的每个单元对应于6.25 Hz,这是音调间隔(FSK的符号时长)。
+/// 如果freq_osr>1意味着通过FFT分析进一步对音调间隔过采样。
+
+typedef struct
+{
+ int max_blocks; ///< mag阵列中分配的块(符号)数。number of blocks (symbols) allocated in the mag array
+ int num_blocks; ///< mag阵列中存储的块(符号)编号,时域序列号。number of blocks (symbols) stored in the mag array
+ //num_bins = 12000 * 0.16 / 2 = 960
+ int num_bins; ///< 以6.25 Hz为单位的FFT箱数量(960)。number of FFT bins in terms of 6.25 Hz
+ int time_osr; ///< 时间过采样率(时间细分数)。number of time subdivisions
+ int freq_osr; ///< 频率过采样率(频率细分数)。number of frequency subdivisions
+ uint8_t* mag; ///< FFT的magnitudes(量级)存储。FFT magnitudes stored as uint8_t[blocks][time_osr][freq_osr][num_bins]
+ int block_stride; ///< 块的步态?没搞懂。Helper value = time_osr * freq_osr * num_bins
+ ftx_protocol_t protocol; ///< 协议。Indicate if using FT4 or FT8
+ float* mag2;///用于存储准确信号量的数组,这是个平方值,真正的值需要开方,为了速度,就不开方了,等后续再计算。
+
+} waterfall_t;
+
+/// Output structure of ft8_find_sync() and input structure of ft8_decode().
+/// Holds the position of potential start of a message in time and frequency.
+// 此结构是ft8_find_sync()的输出结构,ft8_decode()的输入
+// 在时间和频率上,保持消息的潜在起始位置。
+typedef struct
+{
+ int16_t score; ///< score 候选分数(非负数;分数越高表示可能性越大)。Candidate score (non-negative number; higher score means higher likelihood)
+ int16_t time_offset; ///< 时间段索引。Index of the time block
+ int16_t freq_offset; ///< 频率段索引。Index of the frequency bin
+ uint8_t time_sub; ///< 所用时间细分的索引。Index of the time subdivision used
+ uint8_t freq_sub; ///< 所用频率细分的索引。Index of the frequency subdivision used
+ int snr;//信噪比
+} candidate_t;
+
+/// Structure that holds the decoded message
+typedef struct {
+ uint32_t hash22;
+ uint32_t hash12;
+ uint32_t hash10;
+} hashCode;
+// 保存已解码消息的结构
+typedef struct
+{
+ //2022-05-13增加i3和n3
+ uint8_t i3;
+ uint8_t n3;
+
+ // TODO: check again that this size is enough
+ //char text[25]; ///< 纯文本,Plain text,原文是25,
+ char text[48]; ///<但在在unpack.c中,unpack77函数的最大可能是14+14+19=
+ uint16_t hash; ///用于对消息hash,防止消息重复< Hash value to be used in hash table and quick checking for duplicates
+
+ //2022-05-26新增以下内容
+ char call_to[14];//被呼叫的呼号
+ char call_de[14];//发起的呼号
+ char extra[19];//扩展内容
+
+ //---TODO-------------
+ char maidenGrid[5];//梅登海德
+ int report;//信号报告
+
+ hashCode call_to_hash;//22位长度的哈希码
+ hashCode call_de_hash;//22位长度的哈希码
+ //TO HASH , FROM HASH
+
+ uint8_t a91[FTX_LDPC_K_BYTES];//用于生成减法代码的数据
+
+} message_t;
+
+/// Structure that contains the status of various steps during decoding of a message
+/// 包含消息解码过程中各个步骤的状态的结构
+typedef struct
+{
+ int ldpc_errors; ///< 解码期间的LDPC(稀疏校验矩阵)错误数。Number of LDPC errors during decoding
+ uint16_t crc_extracted; ///< 从消息中恢复的CRC值。CRC value recovered from the message
+ uint16_t crc_calculated; ///< 在有效负载上计算的CRC值。CRC value calculated over the payload
+ int unpack_status; ///< 解包例程的返回值。Return value of the unpack routine
+} decode_status_t;
+
+/// Localize top N candidates in frequency and time according to their sync strength (looking at Costas symbols)
+/// 根据同步强度(查看Costas符号),在频率和时间上对前N名候选人进行本地化。
+/// We treat and organize the candidate list as a min-heap (empty initially).
+/// 我们将候选列表视为一个最小堆(最初为空)。
+
+/// @param[in] power 在消息槽期间收集的瀑布数据。Waterfall data collected during message slot
+/// @param[in] sync_pattern 同步模式。Synchronization pattern
+/// @param[in] num_candidates 最大候选数量(堆数组大小)。Number of maximum candidates (size of heap array)
+/// @param[in,out] heap 候选项类型的数组(分配了num个候选项)。Array of candidate_t type entries (with num_candidates allocated entries)
+/// @param[in] min_score 删减不太可能的候选项所允许的最低分数(可以为零,没有效果)。Minimal score allowed for pruning unlikely candidates (can be zero for no effect)
+/// @return 堆中填写的候选人数。Number of candidates filled in the heap
+int ft8_find_sync(const waterfall_t* power, int num_candidates, candidate_t heap[], int min_score);
+
+/// Attempt to decode a message candidate. Extracts the bit probabilities, runs LDPC decoder, checks CRC and unpacks the message in plain text.
+/// 尝试解码候选消息。提取比特概率,运行LDPC解码器,检查CRC,并将消息解压为纯文本。
+/// @param[in] power 在消息槽期间收集的瀑布数据。Waterfall data collected during message slot
+/// @param[in] cand 要解码的候选人。Candidate to decode
+/// @param[out] message 将接收解码消息的message_t结构。message_t structure that will receive the decoded message
+/// @param[in] max_iterations 允许的最大LDPC迭代次数(数字越小,解码速度越快,但精度越低)。Maximum allowed LDPC iterations (lower number means faster decode, but less precise)
+/// @param[out] status decode_status_t结构,该结构将填充各种解码步骤的状态。decode_status_t structure that will be filled with the status of various decoding steps
+/// @return 如果解码成功,则为True,否则为false(查看状态了解详细信息)。True if the decoding was successful, false otherwise (check status for details)
+bool ft8_decode(waterfall_t* power, candidate_t* cand, message_t* message, int max_iterations, decode_status_t* status);
+
+#endif // _INCLUDE_DECODE_H_
diff --git a/ft8CN/app/src/main/cpp/ft8/encode.c b/ft8CN/app/src/main/cpp/ft8/encode.c
new file mode 100644
index 0000000..ad4b57d
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/encode.c
@@ -0,0 +1,197 @@
+#include "encode.h"
+#include "constants.h"
+#include "crc.h"
+
+#include
+#include "../common/debug.h"
+// Returns 1 if an odd number of bits are set in x, zero otherwise
+static uint8_t parity8(uint8_t x)
+{
+ x ^= x >> 4; // a b c d ae bf cg dh
+ x ^= x >> 2; // a b ac bd cae dbf aecg bfdh
+ x ^= x >> 1; // a ab bac acbd bdcae caedbf aecgbfdh
+ return x % 2; // modulo 2
+}
+
+// Encode via LDPC a 91-bit message and return a 174-bit codeword.
+// The generator matrix has dimensions (87,87).
+// The code is a (174,91) regular LDPC code with column weight 3.
+// Arguments:
+// [IN] message - array of 91 bits stored as 12 bytes (MSB first)
+// [OUT] codeword - array of 174 bits stored as 22 bytes (MSB first)
+static void encode174(const uint8_t* message, uint8_t* codeword)
+{
+ // This implementation accesses the generator bits straight from the packed binary representation in kFTXLDPCGenerator
+
+ // Fill the codeword with message and zeros, as we will only update binary ones later
+ for (int j = 0; j < FTX_LDPC_N_BYTES; ++j)
+ {
+ codeword[j] = (j < FTX_LDPC_K_BYTES) ? message[j] : 0;
+ }
+
+ // Compute the byte index and bit mask for the first checksum bit
+ uint8_t col_mask = (0x80u >> (FTX_LDPC_K % 8u)); // bitmask of current byte
+ uint8_t col_idx = FTX_LDPC_K_BYTES - 1; // index into byte array
+
+ // Compute the LDPC checksum bits and store them in codeword
+ for (int i = 0; i < FTX_LDPC_M; ++i)
+ {
+ // Fast implementation of bitwise multiplication and parity checking
+ // Normally nsum would contain the result of dot product between message and kFTXLDPCGenerator[i],
+ // but we only compute the sum modulo 2.
+ uint8_t nsum = 0;
+ for (int j = 0; j < FTX_LDPC_K_BYTES; ++j)
+ {
+ uint8_t bits = message[j] & kFTXLDPCGenerator[i][j]; // bitwise AND (bitwise multiplication)
+ nsum ^= parity8(bits); // bitwise XOR (addition modulo 2)
+ }
+
+ // Set the current checksum bit in codeword if nsum is odd
+ if (nsum % 2)
+ {
+ codeword[col_idx] |= col_mask;
+ }
+
+ // Update the byte index and bit mask for the next checksum bit
+ col_mask >>= 1;
+ if (col_mask == 0)
+ {
+ col_mask = 0x80u;
+ ++col_idx;
+ }
+ }
+}
+
+void ft8_encode(const uint8_t* payload, uint8_t* tones)
+{
+ uint8_t a91[FTX_LDPC_K_BYTES]; // Store 77 bits of payload + 14 bits CRC
+
+ // Compute and add CRC at the end of the message
+ // a91 contains 77 bits of payload + 14 bits of CRC
+ ftx_add_crc(payload, a91);
+
+ uint8_t codeword[FTX_LDPC_N_BYTES];
+ encode174(a91, codeword);
+
+
+
+ // Message structure: S7 D29 S7 D29 S7
+ // Total symbols: 79 (FT8_NN)
+
+ uint8_t mask = 0x80u; // Mask to extract 1 bit from codeword
+ int i_byte = 0; // Index of the current byte of the codeword
+ for (int i_tone = 0; i_tone < FT8_NN; ++i_tone)
+ {
+ if ((i_tone >= 0) && (i_tone < 7))
+ {
+ tones[i_tone] = kFT8CostasPattern[i_tone];
+ }
+ else if ((i_tone >= 36) && (i_tone < 43))
+ {
+ tones[i_tone] = kFT8CostasPattern[i_tone - 36];
+ }
+ else if ((i_tone >= 72) && (i_tone < 79))
+ {
+ tones[i_tone] = kFT8CostasPattern[i_tone - 72];
+ }
+ else
+ {
+ // Extract 3 bits from codeword at i-th position
+ uint8_t bits3 = 0;
+
+ if (codeword[i_byte] & mask)
+ bits3 |= 4;
+ if (0 == (mask >>= 1))
+ {
+ mask = 0x80u;
+ i_byte++;
+ }
+ if (codeword[i_byte] & mask)
+ bits3 |= 2;
+ if (0 == (mask >>= 1))
+ {
+ mask = 0x80u;
+ i_byte++;
+ }
+ if (codeword[i_byte] & mask)
+ bits3 |= 1;
+ if (0 == (mask >>= 1))
+ {
+ mask = 0x80u;
+ i_byte++;
+ }
+
+ tones[i_tone] = kFT8GrayMap[bits3];
+ }
+ }
+}
+
+void ft4_encode(const uint8_t* payload, uint8_t* tones)
+{
+ uint8_t a91[FTX_LDPC_K_BYTES]; // Store 77 bits of payload + 14 bits CRC
+ uint8_t payload_xor[10]; // Encoded payload data
+
+ // '[..] for FT4 only, in order to avoid transmitting a long string of zeros when sending CQ messages,
+ // the assembled 77-bit message is bitwise exclusive-OR’ed with [a] pseudorandom sequence before computing the CRC and FEC parity bits'
+ for (int i = 0; i < 10; ++i)
+ {
+ payload_xor[i] = payload[i] ^ kFT4XORSequence[i];
+ }
+
+ // Compute and add CRC at the end of the message
+ // a91 contains 77 bits of payload + 14 bits of CRC
+ ftx_add_crc(payload_xor, a91);
+
+ uint8_t codeword[FTX_LDPC_N_BYTES];
+ encode174(a91, codeword); // 91 bits -> 174 bits
+
+ // Message structure: R S4_1 D29 S4_2 D29 S4_3 D29 S4_4 R
+ // Total symbols: 105 (FT4_NN)
+
+ uint8_t mask = 0x80u; // Mask to extract 1 bit from codeword
+ int i_byte = 0; // Index of the current byte of the codeword
+ for (int i_tone = 0; i_tone < FT4_NN; ++i_tone)
+ {
+ if ((i_tone == 0) || (i_tone == 104))
+ {
+ tones[i_tone] = 0; // R (ramp) symbol
+ }
+ else if ((i_tone >= 1) && (i_tone < 5))
+ {
+ tones[i_tone] = kFT4CostasPattern[0][i_tone - 1];
+ }
+ else if ((i_tone >= 34) && (i_tone < 38))
+ {
+ tones[i_tone] = kFT4CostasPattern[1][i_tone - 34];
+ }
+ else if ((i_tone >= 67) && (i_tone < 71))
+ {
+ tones[i_tone] = kFT4CostasPattern[2][i_tone - 67];
+ }
+ else if ((i_tone >= 100) && (i_tone < 104))
+ {
+ tones[i_tone] = kFT4CostasPattern[3][i_tone - 100];
+ }
+ else
+ {
+ // Extract 2 bits from codeword at i-th position
+ uint8_t bits2 = 0;
+
+ if (codeword[i_byte] & mask)
+ bits2 |= 2;
+ if (0 == (mask >>= 1))
+ {
+ mask = 0x80u;
+ i_byte++;
+ }
+ if (codeword[i_byte] & mask)
+ bits2 |= 1;
+ if (0 == (mask >>= 1))
+ {
+ mask = 0x80u;
+ i_byte++;
+ }
+ tones[i_tone] = kFT4GrayMap[bits2];
+ }
+ }
+}
diff --git a/ft8CN/app/src/main/cpp/ft8/encode.h b/ft8CN/app/src/main/cpp/ft8/encode.h
new file mode 100644
index 0000000..d5e2759
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/encode.h
@@ -0,0 +1,32 @@
+#ifndef _INCLUDE_ENCODE_H_
+#define _INCLUDE_ENCODE_H_
+
+#include
+
+// typedef struct
+// {
+// uint8_t tones[FT8_NN];
+// // for waveform readout:
+// int n_spsym; // Number of waveform samples per symbol
+// float *pulse; // [3 * n_spsym]
+// int idx_symbol; // Index of the current symbol
+// float f0; // Base frequency, Hertz
+// float signal_rate; // Waveform sample rate, Hertz
+// } encoder_t;
+
+// void encoder_init(float signal_rate, float *pulse_buffer);
+// void encoder_set_f0(float f0);
+// void encoder_process(const message_t *message); // in: message
+// void encoder_generate(float *block); // out: block of waveforms
+
+/// Generate FT8 tone sequence from payload data
+/// @param[in] payload - 10 byte array consisting of 77 bit payload
+/// @param[out] tones - array of FT8_NN (79) bytes to store the generated tones (encoded as 0..7)
+void ft8_encode(const uint8_t* payload, uint8_t* tones);
+
+/// Generate FT4 tone sequence from payload data
+/// @param[in] payload - 10 byte array consisting of 77 bit payload
+/// @param[out] tones - array of FT4_NN (105) bytes to store the generated tones (encoded as 0..3)
+void ft4_encode(const uint8_t* payload, uint8_t* tones);
+
+#endif // _INCLUDE_ENCODE_H_
diff --git a/ft8CN/app/src/main/cpp/ft8/hash22.c b/ft8CN/app/src/main/cpp/ft8/hash22.c
new file mode 100644
index 0000000..bf9e97b
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/hash22.c
@@ -0,0 +1,65 @@
+
+
+#include "hash22.h"
+#include "stdlib.h"
+#include "string.h"
+//m为hash的长度,12,22
+//call的长度是12(包括'\0')
+uint32_t hashcall(char* call, int m)
+{
+ const char *chars = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/";
+ char callsign[11]=" ";
+
+ char *temp=call;
+
+ int j=0;
+ while(temp[0] == ' '){
+ ++temp;
+ if (temp[0]!=' ')
+ {
+ break;
+ }
+ j++;
+ }
+
+ for (int i = 0; i < 11-j; i++)
+ {
+ if (temp[i]=='\0')
+ {
+ break;
+ }else{
+ callsign[i]=temp[i];
+ }
+
+ }
+
+ uint64_t x = 0;
+ for(int i = 0; i < 11; i++){
+
+ int c = (int)callsign[i];
+ const char *p = strchr(chars, c);
+ if (p==NULL)
+ {
+ return 0;
+ }
+
+ int j = p - chars;
+ x = 38*x + j;
+ }
+
+ x = x * 47055833459LL;
+ x = x >> (64 - m);
+
+ return x;
+
+}
+
+uint32_t hashcall_10(char* call){
+ return hashcall(call,HASH_10);
+}
+uint32_t hashcall_12(char* call){
+ return hashcall(call,HASH_12);
+}
+uint32_t hashcall_22(char* call){
+ return hashcall(call,HASH_22);
+}
\ No newline at end of file
diff --git a/ft8CN/app/src/main/cpp/ft8/hash22.h b/ft8CN/app/src/main/cpp/ft8/hash22.h
new file mode 100644
index 0000000..96ce5ab
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/hash22.h
@@ -0,0 +1,11 @@
+#include
+
+
+#define HASH_10 (10) ///哈希码长度为10
+#define HASH_12 (12) ///哈希码长度为12
+#define HASH_22 (22) ///哈希码长度为12
+uint32_t hashcall(char* call, int m);
+
+uint32_t hashcall_10(char* call);//返回长度是10的哈希码
+uint32_t hashcall_12(char* call);//返回长度是12的哈希码
+uint32_t hashcall_22(char* call);//返回长度是22的哈希码
\ No newline at end of file
diff --git a/ft8CN/app/src/main/cpp/ft8/ldpc.c b/ft8CN/app/src/main/cpp/ft8/ldpc.c
new file mode 100644
index 0000000..1b3584c
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/ldpc.c
@@ -0,0 +1,268 @@
+//
+// 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) )
+//
+////将174位码字作为对数似然为零的数组,
+////返回一个174位已更正的码字或零长度数组。
+////最后87位是(系统)纯文本。
+////这是和积算法的一个实现
+////来自Sarah Johnson的迭代纠错手册。
+////码字[i]=对数(P(x=0)/P(x=1))
+
+#include "ldpc.h"
+#include "constants.h"
+
+#include
+#include
+#include
+#include
+
+static int ldpc_check(uint8_t codeword[]);
+static float fast_tanh(float x);
+static float fast_atanh(float x);
+
+// 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_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
+ int min_errors = FTX_LDPC_M;
+
+ 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++)
+ {
+ for (int j = 0; j < FTX_LDPC_M; j++)
+ {
+
+ for (int ii1 = 0; ii1 < kFTX_LDPCNumRows[j]; ii1++)
+ {
+
+ int i1 = kFTX_LDPC_Nm[j][ii1] - 1;
+ float a = 1.0f;
+ for (int ii2 = 0; ii2 < kFTX_LDPCNumRows[j]; ii2++)
+ {
+
+ int i2 = kFTX_LDPC_Nm[j][ii2] - 1;
+ if (i2 != i1)
+ {
+ a *= fast_tanh(-m[j][i2] / 2.0f);
+ }
+ }
+ e[j][i1] = -2.0f * fast_atanh(a);
+ }
+ }
+
+ 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
+ }
+ }
+
+ 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;
+ }
+ }
+ }
+
+ *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.
+//
+static int 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_LDPCNumRows[m]; ++i)
+ {
+ x ^= codeword[kFTX_LDPC_Nm[m][i] - 1];
+ }
+ if (x != 0)
+ {
+ ++errors;
+ }
+ }
+ return errors;
+}
+
+//// 码字是174个对数可能性。
+//// plain是一个返回值,174 整数,为0或1。
+//// max_iters是迭代次数。
+//// ok==87表示成功。好像不是哦,==0才是
+void bp_decode(float codeword[], int max_iters, uint8_t plain[], int* ok)
+{
+ float tov[FTX_LDPC_N][3];
+ float toc[FTX_LDPC_M][7];
+
+ //FTX_LDPC_M=83
+ int min_errors = FTX_LDPC_M;
+
+ // initialize message data
+ //FTX_LDPC_N=174
+ 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)
+ {//转换成0和1
+ 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)
+ //向LDPC(稀疏校验矩阵)检测LDPC矩阵是预定义的83行91列的矩阵
+ 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_LDPCNumRows[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] = fast_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_LDPCNumRows[m]; ++n_idx)
+ {
+ if ((kFTX_LDPC_Nm[m][n_idx] - 1) != n)
+ {
+ Tmn *= toc[m][n_idx];
+ }
+ }
+ tov[n][m_idx] = -2 * fast_atanh(Tmn);
+ }
+ }
+ }
+
+ *ok = min_errors;
+}
+
+// Ideas for approximating tanh/atanh:
+// * https://varietyofsound.wordpress.com/2011/02/14/efficient-tanh-computation-using-lamberts-continued-fraction/
+// * http://functions.wolfram.com/ElementaryFunctions/ArcTanh/10/0001/
+// * https://mathr.co.uk/blog/2017-09-06_approximating_hyperbolic_tangent.html
+// * https://math.stackexchange.com/a/446411
+
+static float fast_tanh(float x)
+{
+ if (x < -4.97f)
+ {
+ return -1.0f;
+ }
+ if (x > 4.97f)
+ {
+ return 1.0f;
+ }
+ float x2 = x * x;
+ // float a = x * (135135.0f + x2 * (17325.0f + x2 * (378.0f + x2)));
+ // float b = 135135.0f + x2 * (62370.0f + x2 * (3150.0f + x2 * 28.0f));
+ // float a = x * (10395.0f + x2 * (1260.0f + x2 * 21.0f));
+ // float b = 10395.0f + x2 * (4725.0f + x2 * (210.0f + x2));
+ float a = x * (945.0f + x2 * (105.0f + x2));
+ float b = 945.0f + x2 * (420.0f + x2 * 15.0f);
+ return a / b;
+}
+
+static float fast_atanh(float x)
+{
+ float x2 = x * x;
+ // float a = x * (-15015.0f + x2 * (19250.0f + x2 * (-5943.0f + x2 * 256.0f)));
+ // float b = (-15015.0f + x2 * (24255.0f + x2 * (-11025.0f + x2 * 1225.0f)));
+ // float a = x * (-1155.0f + x2 * (1190.0f + x2 * -231.0f));
+ // float b = (-1155.0f + x2 * (1575.0f + x2 * (-525.0f + x2 * 25.0f)));
+ float a = x * (945.0f + x2 * (-735.0f + x2 * 64.0f));
+ float b = (945.0f + x2 * (-1050.0f + x2 * 225.0f));
+ return a / b;
+}
diff --git a/ft8CN/app/src/main/cpp/ft8/ldpc.h b/ft8CN/app/src/main/cpp/ft8/ldpc.h
new file mode 100644
index 0000000..5215d4b
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/ldpc.h
@@ -0,0 +1,20 @@
+#ifndef _INCLUDE_LDPC_H_
+#define _INCLUDE_LDPC_H_
+
+#include
+#include "../common/debug.h"
+
+//// codeword is 174 log-likelihoods.
+//// plain is a return value, 174 ints, to be 0 or 1.
+//// iters is how hard to try.
+//// ok == 87 means success.
+//// 码字是174个对数可能性。
+//// plain是一个返回值,174 整数,为0或1。
+//// max_iters是迭代次数。
+//// ok==87表示成功。??好像不是哦,==0才是
+
+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);
+
+#endif // _INCLUDE_LDPC_H_
diff --git a/ft8CN/app/src/main/cpp/ft8/pack.c b/ft8CN/app/src/main/cpp/ft8/pack.c
new file mode 100644
index 0000000..9617aa9
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/pack.c
@@ -0,0 +1,373 @@
+#include "pack.h"
+#include "text.h"
+
+#include
+#include
+#include
+#include
+#include "../common/debug.h"
+
+#define NTOKENS ((uint32_t)2063592L)
+#define MAX22 ((uint32_t)4194304L)
+#define MAXGRID4 ((uint16_t)32400)
+
+const char A0[] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?";
+const char A1[] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+const char A2[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+const char A3[] = "0123456789";
+const char A4[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+// Pack a special token, a 22-bit hash code, or a valid base call
+// into a 28-bit integer.
+int32_t pack28(const char* callsign)
+{
+ // Check for special tokens first
+ if (starts_with(callsign, "DE "))
+ return 0;
+ if (starts_with(callsign, "QRZ "))
+ return 1;
+ if (starts_with(callsign, "CQ "))
+ return 2;
+
+ if (starts_with(callsign, "CQ_"))
+ {
+ int nnum = 0, nlet = 0;
+
+ // TODO:
+ }
+
+ // TODO: Check for <...> callsign
+
+ char c6[6] = { ' ', ' ', ' ', ' ', ' ', ' ' };
+
+ int length = 0; // strlen(callsign); // We will need it later
+ while (callsign[length] != ' ' && callsign[length] != 0)
+ {
+ length++;
+ }
+
+ // Copy callsign to 6 character buffer
+ if (starts_with(callsign, "3DA0") && length <= 7)
+ {
+ // Work-around for Swaziland prefix: 3DA0XYZ -> 3D0XYZ
+ memcpy(c6, "3D0", 3);
+ memcpy(c6 + 3, callsign + 4, length - 4);
+ }
+ else if (starts_with(callsign, "3X") && is_letter(callsign[2]) && length <= 7)
+ {
+ // Work-around for Guinea prefixes: 3XA0XYZ -> QA0XYZ
+ memcpy(c6, "Q", 1);
+ memcpy(c6 + 1, callsign + 2, length - 2);
+ }
+ else
+ {
+ if (is_digit(callsign[2]) && length <= 6)
+ {
+ // AB0XYZ
+ memcpy(c6, callsign, length);
+ }
+ else if (is_digit(callsign[1]) && length <= 5)
+ {
+ // A0XYZ -> " A0XYZ"
+ memcpy(c6 + 1, callsign, length);
+ }
+ }
+
+ // Check for standard callsign
+ int i0, i1, i2, i3, i4, i5;
+ if ((i0 = char_index(A1, c6[0])) >= 0 && (i1 = char_index(A2, c6[1])) >= 0 && (i2 = char_index(A3, c6[2])) >= 0 && (i3 = char_index(A4, c6[3])) >= 0 && (i4 = char_index(A4, c6[4])) >= 0 && (i5 = char_index(A4, c6[5])) >= 0)
+ {
+ // This is a standard callsign
+ int32_t n28 = i0;
+ n28 = n28 * 36 + i1;
+ n28 = n28 * 10 + i2;
+ n28 = n28 * 27 + i3;
+ n28 = n28 * 27 + i4;
+ n28 = n28 * 27 + i5;
+ return NTOKENS + MAX22 + n28;
+ }
+
+ //char text[13];
+ //if (length > 13) return -1;
+
+ // TODO:
+ // Treat this as a nonstandard callsign: compute its 22-bit hash
+ return -1;
+}
+
+// Check if a string could be a valid standard callsign or a valid
+// compound callsign.
+// Return base call "bc" and a logical "cok" indicator.
+bool chkcall(const char* call, char* bc)
+{
+ int length = strlen(call); // n1=len_trim(w)
+ if (length > 11)
+ return false;
+ if (0 != strchr(call, '.'))
+ return false;
+ if (0 != strchr(call, '+'))
+ return false;
+ if (0 != strchr(call, '-'))
+ return false;
+ if (0 != strchr(call, '?'))
+ return false;
+ if (length > 6 && 0 != strchr(call, '/'))
+ return false;
+
+ // TODO: implement suffix parsing (or rework?)
+
+ return true;
+}
+
+uint16_t packgrid(const char* grid4)
+{
+ if (grid4 == 0)
+ {
+ // Two callsigns only, no report/grid
+ return MAXGRID4 + 1;
+ }
+
+ // Take care of special cases
+ if (equals(grid4, "RRR"))
+ return MAXGRID4 + 2;
+ if (equals(grid4, "RR73"))
+ return MAXGRID4 + 3;
+ if (equals(grid4, "73"))
+ return MAXGRID4 + 4;
+
+ // Check for standard 4 letter grid
+ if (in_range(grid4[0], 'A', 'R') && in_range(grid4[1], 'A', 'R') && is_digit(grid4[2]) && is_digit(grid4[3]))
+ {
+ uint16_t igrid4 = (grid4[0] - 'A');
+ igrid4 = igrid4 * 18 + (grid4[1] - 'A');
+ igrid4 = igrid4 * 10 + (grid4[2] - '0');
+ igrid4 = igrid4 * 10 + (grid4[3] - '0');
+ return igrid4;
+ }
+
+ // Parse report: +dd / -dd / R+dd / R-dd
+ // TODO: check the range of dd
+ if (grid4[0] == 'R')
+ {
+ int dd = dd_to_int(grid4 + 1, 3);
+ uint16_t irpt = 35 + dd;
+ return (MAXGRID4 + irpt) | 0x8000; // ir = 1
+ }
+ else
+ {
+ int dd = dd_to_int(grid4, 3);
+ uint16_t irpt = 35 + dd;
+ return (MAXGRID4 + irpt); // ir = 0
+ }
+
+ return MAXGRID4 + 1;
+}
+
+// Pack Type 1 (Standard 77-bit message) and Type 2 (ditto, with a "/P" call)
+int pack77_1(const char* msg, uint8_t* b77)
+{
+ // Locate the first delimiter
+ const char* s1 = strchr(msg, ' ');
+ if (s1 == 0)
+ return -1;
+
+ const char* call1 = msg; // 1st call
+ const char* call2 = s1 + 1; // 2nd call
+
+ LOG(LOG_DEBUG,"call1 :%s", call1);
+ LOG(LOG_DEBUG,"call2 :%s", call2);
+
+ int32_t n28a = pack28(call1);
+ int32_t n28b = pack28(call2);
+ LOG(LOG_DEBUG,"n28a %2X",n28a);
+ LOG(LOG_DEBUG,"n28b %2X",n28b);
+
+ if (n28a < 0 || n28b < 0)
+ return -1;
+
+ uint16_t igrid4;
+
+ // Locate the second delimiter
+ const char* s2 = strchr(s1 + 1, ' ');
+ if (s2 != 0)
+ {
+ LOG(LOG_DEBUG,"GRID: %s",s2);
+ igrid4 = packgrid(s2 + 1);
+ }
+ else
+ {
+ // Two callsigns, no grid/report
+ igrid4 = packgrid(0);
+ }
+ LOG(LOG_DEBUG,"G15: %x",igrid4);
+
+ uint8_t i3 = 1; // No suffix or /R
+
+ // TODO: check for suffixes
+
+ // Shift in ipa and ipb bits into n28a and n28b
+ n28a <<= 1; // ipa = 0
+ n28b <<= 1; // ipb = 0
+
+ // Pack into (28 + 1) + (28 + 1) + (1 + 15) + 3 bits
+ b77[0] = (n28a >> 21);
+ b77[1] = (n28a >> 13);
+ b77[2] = (n28a >> 5);
+ b77[3] = (uint8_t)(n28a << 3) | (uint8_t)(n28b >> 26);
+ b77[4] = (n28b >> 18);
+ b77[5] = (n28b >> 10);
+ b77[6] = (n28b >> 2);
+ b77[7] = (uint8_t)(n28b << 6) | (uint8_t)(igrid4 >> 10);
+ b77[8] = (igrid4 >> 2);
+ b77[9] = (uint8_t)(igrid4 << 6) | (uint8_t)(i3 << 3);
+
+ return 0;
+}
+
+void packtext77(const char* text, uint8_t* b77)
+{
+ int length = strlen(text);
+
+ // Skip leading and trailing spaces
+ while (*text == ' ' && *text != 0)
+ {
+ ++text;
+ --length;
+ }
+ while (length > 0 && text[length - 1] == ' ')
+ {
+ --length;
+ }
+
+ // Clear the first 72 bits representing a long number
+ for (int i = 0; i < 9; ++i)
+ {
+ b77[i] = 0;
+ }
+
+ // Now express the text as base-42 number stored
+ // in the first 72 bits of b77
+ for (int j = 0; j < 13; ++j)
+ {
+ // Multiply the long integer in b77 by 42
+ uint16_t x = 0;
+ for (int i = 8; i >= 0; --i)
+ {
+ x += b77[i] * (uint16_t)42;
+ b77[i] = (x & 0xFF);
+ x >>= 8;
+ }
+
+ // Get the index of the current char
+ if (j < length)
+ {
+ int q = char_index(A0, text[j]);
+ x = (q > 0) ? q : 0;
+ }
+ else
+ {
+ x = 0;
+ }
+ // Here we double each added number in order to have the result multiplied
+ // by two as well, so that it's a 71 bit number left-aligned in 72 bits (9 bytes)
+ x <<= 1;
+
+ // Now add the number to our long number
+ for (int i = 8; i >= 0; --i)
+ {
+ if (x == 0)
+ break;
+ x += b77[i];
+ b77[i] = (x & 0xFF);
+ x >>= 8;
+ }
+ }
+
+ // Set n3=0 (bits 71..73) and i3=0 (bits 74..76)
+ b77[8] &= 0xFE;
+ b77[9] &= 0x00;
+}
+
+int pack77(const char* msg, uint8_t* c77)
+{
+ // Check Type 1 (Standard 77-bit message) or Type 2, with optional "/P"
+ if (0 == pack77_1(msg, c77))
+ {
+ return 0;
+ }
+
+ // TODO:
+ // Check 0.5 (telemetry)
+
+ // Check Type 4 (One nonstandard call and one hashed call)
+
+ // Default to free text
+ // i3=0 n3=0
+ packtext77(msg, c77);
+ return 0;
+}
+
+#ifdef UNIT_TEST
+
+#include
+
+bool test1()
+{
+ const char* inputs[] = {
+ "",
+ " ",
+ "ABC",
+ "A9",
+ "L9A",
+ "L7BC",
+ "L0ABC",
+ "LL3JG",
+ "LL3AJG",
+ "CQ ",
+ 0
+ };
+
+ for (int i = 0; inputs[i]; ++i)
+ {
+ int32_t result = ft8_v2::pack28(inputs[i]);
+ printf("pack28(\"%s\") = %d\n", inputs[i], result);
+ }
+
+ return true;
+}
+
+bool test2()
+{
+ const char* inputs[] = {
+ "CQ LL3JG",
+ "CQ LL3JG KO26",
+ "L0UAA LL3JG KO26",
+ "L0UAA LL3JG +02",
+ "L0UAA LL3JG RRR",
+ "L0UAA LL3JG 73",
+ 0
+ };
+
+ for (int i = 0; inputs[i]; ++i)
+ {
+ uint8_t result[10];
+ int rc = ft8_v2::pack77_1(inputs[i], result);
+ printf("pack77_1(\"%s\") = %d\t[", inputs[i], rc);
+ for (int j = 0; j < 10; ++j)
+ {
+ printf("%02x ", result[j]);
+ }
+ printf("]\n");
+ }
+
+ return true;
+}
+
+int main()
+{
+ test1();
+ test2();
+ return 0;
+}
+
+#endif
\ No newline at end of file
diff --git a/ft8CN/app/src/main/cpp/ft8/pack.h b/ft8CN/app/src/main/cpp/ft8/pack.h
new file mode 100644
index 0000000..fbe1286
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/pack.h
@@ -0,0 +1,12 @@
+#ifndef _INCLUDE_PACK_H_
+#define _INCLUDE_PACK_H_
+
+#include
+
+// Pack FT8 text message into 72 bits
+// [IN] msg - FT8 message (e.g. "CQ TE5T KN01")
+// [OUT] c77 - 10 byte array to store the 77 bit payload (MSB first)
+int pack77(const char* msg, uint8_t* c77);
+void packtext77(const char* text, uint8_t* b77);
+
+#endif // _INCLUDE_PACK_H_
diff --git a/ft8CN/app/src/main/cpp/ft8/text.c b/ft8CN/app/src/main/cpp/ft8/text.c
new file mode 100644
index 0000000..68393a9
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/text.c
@@ -0,0 +1,253 @@
+#include "text.h"
+
+#include
+
+const char* trim_front(const char* str)
+{
+ // Skip leading whitespace
+ while (*str == ' ')
+ {
+ str++;
+ }
+ return str;
+}
+
+void trim_back(char* str)
+{
+ // Skip trailing whitespace by replacing it with '\0' characters
+ int idx = strlen(str) - 1;
+ while (idx >= 0 && str[idx] == ' ')
+ {
+ str[idx--] = '\0';
+ }
+}
+
+// 1) trims a string from the back by changing whitespaces to '\0'
+// 2) trims a string from the front by skipping whitespaces
+char* trim(char* str)
+{
+ str = (char*)trim_front(str);
+ trim_back(str);
+ // return a pointer to the first non-whitespace character
+ return str;
+}
+
+char to_upper(char c)
+{
+ return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c;
+}
+
+bool is_digit(char c)
+{
+ return (c >= '0') && (c <= '9');
+}
+
+bool is_letter(char c)
+{
+ return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'));
+}
+
+bool is_space(char c)
+{
+ return (c == ' ');
+}
+
+bool in_range(char c, char min, char max)
+{
+ return (c >= min) && (c <= max);
+}
+
+bool starts_with(const char* string, const char* prefix)
+{
+ return 0 == memcmp(string, prefix, strlen(prefix));
+}
+
+bool equals(const char* string1, const char* string2)
+{
+ return 0 == strcmp(string1, string2);
+}
+
+int char_index(const char* string, char c)
+{
+ for (int i = 0; *string; ++i, ++string)
+ {
+ if (c == *string)
+ {
+ return i;
+ }
+ }
+ return -1; // Not found
+}
+
+// Text message formatting:
+// - replaces lowercase letters with uppercase
+// - merges consecutive spaces into single space
+void fmtmsg(char* msg_out, const char* msg_in)
+{
+ char c;
+ char last_out = 0;
+ while ((c = *msg_in))
+ {
+ if (c != ' ' || last_out != ' ')
+ {
+ last_out = to_upper(c);
+ *msg_out = last_out;
+ ++msg_out;
+ }
+ ++msg_in;
+ }
+ *msg_out = 0; // Add zero termination
+}
+
+// Parse a 2 digit integer from string
+int dd_to_int(const char* str, int length)
+{
+ int result = 0;
+ bool negative;
+ int i;
+ if (str[0] == '-')
+ {
+ negative = true;
+ i = 1; // Consume the - sign
+ }
+ else
+ {
+ negative = false;
+ i = (str[0] == '+') ? 1 : 0; // Consume a + sign if found
+ }
+
+ while (i < length)
+ {
+ if (str[i] == 0)
+ break;
+ if (!is_digit(str[i]))
+ break;
+ result *= 10;
+ result += (str[i] - '0');
+ ++i;
+ }
+
+ return negative ? -result : result;
+}
+
+// Convert a 2 digit integer to string
+void int_to_dd(char* str, int value, int width, bool full_sign)
+{
+ if (value < 0)
+ {
+ *str = '-';
+ ++str;
+ value = -value;
+ }
+ else if (full_sign)
+ {
+ *str = '+';
+ ++str;
+ }
+
+ int divisor = 1;
+ for (int i = 0; i < width - 1; ++i)
+ {
+ divisor *= 10;
+ }
+
+ while (divisor >= 1)
+ {
+ int digit = value / divisor;
+
+ *str = '0' + digit;
+ ++str;
+
+ value -= digit * divisor;
+ divisor /= 10;
+ }
+ *str = 0; // Add zero terminator
+}
+
+// convert integer index to ASCII character according to one of 6 tables:
+// table 0: " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"
+// table 1: " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+// table 2: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+// table 3: "0123456789"
+// table 4: " ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+// table 5: " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/"
+char charn(int c, int table_idx)
+{
+ if (table_idx != 2 && table_idx != 3)
+ {
+ if (c == 0)
+ return ' ';
+ c -= 1;
+ }
+ if (table_idx != 4)
+ {
+ if (c < 10)
+ return '0' + c;
+ c -= 10;
+ }
+ if (table_idx != 3)
+ {
+ if (c < 26)
+ return 'A' + c;
+ c -= 26;
+ }
+
+ if (table_idx == 0)
+ {
+ if (c < 5)
+ return "+-./?"[c];
+ }
+ else if (table_idx == 5)
+ {
+ if (c == 0)
+ return '/';
+ }
+
+ return '_'; // unknown character, should never get here
+}
+
+// Convert character to its index (charn in reverse) according to a table
+int nchar(char c, int table_idx)
+{
+ int n = 0;
+ if (table_idx != 2 && table_idx != 3)
+ {
+ if (c == ' ')
+ return n + 0;
+ n += 1;
+ }
+ if (table_idx != 4)
+ {
+ if (c >= '0' && c <= '9')
+ return n + (c - '0');
+ n += 10;
+ }
+ if (table_idx != 3)
+ {
+ if (c >= 'A' && c <= 'Z')
+ return n + (c - 'A');
+ n += 26;
+ }
+
+ if (table_idx == 0)
+ {
+ if (c == '+')
+ return n + 0;
+ if (c == '-')
+ return n + 1;
+ if (c == '.')
+ return n + 2;
+ if (c == '/')
+ return n + 3;
+ if (c == '?')
+ return n + 4;
+ }
+ else if (table_idx == 5)
+ {
+ if (c == '/')
+ return n + 0;
+ }
+
+ // Character not found
+ return -1;
+}
diff --git a/ft8CN/app/src/main/cpp/ft8/text.h b/ft8CN/app/src/main/cpp/ft8/text.h
new file mode 100644
index 0000000..aac9921
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/text.h
@@ -0,0 +1,37 @@
+#ifndef _INCLUDE_TEXT_H_
+#define _INCLUDE_TEXT_H_
+
+#include
+#include
+
+// Utility functions for characters and strings
+
+const char* trim_front(const char* str);
+void trim_back(char* str);
+char* trim(char* str);
+
+char to_upper(char c);
+bool is_digit(char c);
+bool is_letter(char c);
+bool is_space(char c);
+bool in_range(char c, char min, char max);
+bool starts_with(const char* string, const char* prefix);
+bool equals(const char* string1, const char* string2);
+
+int char_index(const char* string, char c);
+
+// Text message formatting:
+// - replaces lowercase letters with uppercase
+// - merges consecutive spaces into single space
+void fmtmsg(char* msg_out, const char* msg_in);
+
+// Parse a 2 digit integer from string
+int dd_to_int(const char* str, int length);
+
+// Convert a 2 digit integer to string
+void int_to_dd(char* str, int value, int width, bool full_sign);
+
+char charn(int c, int table_idx);
+int nchar(char c, int table_idx);
+
+#endif // _INCLUDE_TEXT_H_
diff --git a/ft8CN/app/src/main/cpp/ft8/unpack.c b/ft8CN/app/src/main/cpp/ft8/unpack.c
new file mode 100644
index 0000000..7c12a90
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/unpack.c
@@ -0,0 +1,626 @@
+#ifdef __linux__
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#endif
+
+#include "unpack.h"
+
+#include "text.h"
+// #include //为防止警告
+#include
+#include "hash22.h"
+
+#define MAX22 ((uint32_t)4194304L)
+#define NTOKENS ((uint32_t)2063592L)
+#define MAXGRID4 ((uint16_t)32400L)
+
+
+// n28 is a 28-bit integer, e.g. n28a or n28b, containing all the
+// call sign bits from a packed message.
+int unpack_callsign(uint32_t n28, uint8_t ip, uint8_t i3, char *result,hashCode * hash) {
+ // Check for special tokens DE, QRZ, CQ, CQ_nnn, CQ_aaaa
+ hash->hash10=0;
+ hash->hash12=0;
+ hash->hash22=0;
+ if (n28 < NTOKENS) {
+ if (n28 <= 2) {
+ if (n28 == 0)
+ strcpy(result, "DE");
+ if (n28 == 1)
+ strcpy(result, "QRZ");
+ if (n28 == 2)
+ strcpy(result, "CQ");
+ return 0; // Success
+ }
+ if (n28 <= 1002) {
+ // CQ_nnn with 3 digits
+ strcpy(result, "CQ ");
+ int_to_dd(result + 3, n28 - 3, 3, false);
+ return 0; // Success
+ }
+ if (n28 <= 532443L) {
+ // CQ_aaaa with 4 alphanumeric symbols
+ uint32_t n = n28 - 1003;
+ char aaaa[5];
+
+ aaaa[4] = '\0';
+ for (int i = 3; /* */; --i) {
+ aaaa[i] = charn(n % 27, 4);
+ if (i == 0)
+ break;
+ n /= 27;
+ }
+
+ strcpy(result, "CQ ");
+ strcat(result, trim_front(aaaa));
+ return 0; // Success
+ }
+ // ? TODO: unspecified in the WSJT-X code
+ return -1;
+ }
+
+ n28 = n28 - NTOKENS;
+ if (n28 < MAX22) {
+ hash->hash10=n28;//把哈希值保存下来
+ hash->hash12=n28;//把哈希值保存下来
+ hash->hash22=n28;//把哈希值保存下来
+
+ LOG_PRINTF("N28 HASH: %0x",n28);
+
+ // This is a 22-bit hash of a result
+ // TODO: implement
+ strcpy(result, "<...>");
+ // result[0] = '<';
+ // int_to_dd(result + 1, n28, 7, false);
+ // result[8] = '>';
+ // result[9] = '\0';
+ return 0;
+ }
+
+ // Standard callsign
+ uint32_t n = n28 - MAX22;
+
+ char callsign[7];
+ callsign[6] = '\0';
+ callsign[5] = charn(n % 27, 4);
+ n /= 27;
+ callsign[4] = charn(n % 27, 4);
+ n /= 27;
+ callsign[3] = charn(n % 27, 4);
+ n /= 27;
+ callsign[2] = charn(n % 10, 3);
+ n /= 10;
+ callsign[1] = charn(n % 36, 2);
+ n /= 36;
+ callsign[0] = charn(n % 37, 1);
+
+ // Skip trailing and leading whitespace in case of a short callsign
+ //短呼号时跳过尾随和前导空格
+ strcpy(result, trim(callsign));
+ if (strlen(result) == 0)
+ return -1;
+
+ hash->hash10=hashcall_10(result);//对呼号进行22位的哈希
+ hash->hash12=hashcall_12(result);//对呼号进行22位的哈希
+ hash->hash22=hashcall_22(result);//对呼号进行22位的哈希
+ // Check if we should append /R or /P suffix
+ //检查是否应附加/R或/P后缀
+ if (ip) {
+ if (i3 == 1) {
+ strcat(result, "/R");
+ } else if (i3 == 2) {
+ strcat(result, "/P");
+ }
+ }
+
+ return 0; // Success
+}
+
+//int unpack_type1(const uint8_t *a77, uint8_t i3, char *call_to, char *call_de, char *extra) {
+// uint32_t n28a, n28b;
+// uint16_t igrid4;
+// uint8_t ir;
+//
+// // Extract packed fields
+// n28a = (a77[0] << 21);
+// n28a |= (a77[1] << 13);
+// n28a |= (a77[2] << 5);
+// n28a |= (a77[3] >> 3);
+// n28b = ((a77[3] & 0x07) << 26);
+// n28b |= (a77[4] << 18);
+// n28b |= (a77[5] << 10);
+// n28b |= (a77[6] << 2);
+// n28b |= (a77[7] >> 6);
+// ir = ((a77[7] & 0x20) >> 5);
+// igrid4 = ((a77[7] & 0x1F) << 10);
+// igrid4 |= (a77[8] << 2);
+// igrid4 |= (a77[9] >> 6);
+//
+// // Unpack both callsigns
+// if (unpack_callsign(n28a >> 1, n28a & 0x01, i3, call_to) < 0) {
+// return -1;
+// }
+// if (unpack_callsign(n28b >> 1, n28b & 0x01, i3, call_de) < 0) {
+// return -2;
+// }
+// // Fix "CQ_" to "CQ " -> already done in unpack_callsign()
+//
+// // TODO: add to recent calls
+// // if (call_to[0] != '<' && strlen(call_to) >= 4) {
+// // save_hash_call(call_to)
+// // }
+// // if (call_de[0] != '<' && strlen(call_de) >= 4) {
+// // save_hash_call(call_de)
+// // }
+//
+// char *dst = extra;
+//
+// if (igrid4 <= MAXGRID4) {
+// // Extract 4 symbol grid locator
+// if (ir > 0) {
+// // In case of ir=1 add an "R" before grid
+// //dst = stpcpy(dst, "R ");//除错
+// dst = strcpy(dst, "R ");
+// dst += 3;
+// }
+//
+// uint16_t n = igrid4;
+// dst[4] = '\0';
+// dst[3] = '0' + (n % 10);
+// n /= 10;
+// dst[2] = '0' + (n % 10);
+// n /= 10;
+// dst[1] = 'A' + (n % 18);
+// n /= 18;
+// dst[0] = 'A' + (n % 18);
+// // if (ir > 0 && strncmp(call_to, "CQ", 2) == 0) return -1;
+// } else {
+// // Extract report
+// int irpt = igrid4 - MAXGRID4;
+//
+// // Check special cases first (irpt > 0 always)
+// switch (irpt) {
+// case 1:
+// extra[0] = '\0';
+// break;
+// case 2:
+// strcpy(dst, "RRR");
+// break;
+// case 3:
+// strcpy(dst, "RR73");
+// break;
+// case 4:
+// strcpy(dst, "73");
+// break;
+// default:
+// // Extract signal report as a two digit number with a + or - sign
+// if (ir > 0) {
+// *dst++ = 'R'; // Add "R" before report
+// }
+// int_to_dd(dst, irpt - 35, 2, true);
+// break;
+// }
+// // if (irpt >= 2 && strncmp(call_to, "CQ", 2) == 0) return -1;
+// }
+//
+// return 0; // Success
+//}
+
+
+int unpack_type1_(const uint8_t *a77, message_t *message) {
+ uint32_t n28a, n28b;
+ uint16_t igrid4;
+ uint8_t ir;
+
+ // Extract packed fields
+ n28a = (a77[0] << 21);
+ n28a |= (a77[1] << 13);
+ n28a |= (a77[2] << 5);
+ n28a |= (a77[3] >> 3);
+ n28b = ((a77[3] & 0x07) << 26);
+ n28b |= (a77[4] << 18);
+ n28b |= (a77[5] << 10);
+ n28b |= (a77[6] << 2);
+ n28b |= (a77[7] >> 6);
+ ir = ((a77[7] & 0x20) >> 5);
+ igrid4 = ((a77[7] & 0x1F) << 10);
+ igrid4 |= (a77[8] << 2);
+ igrid4 |= (a77[9] >> 6);
+
+
+
+ // Unpack both callsigns
+ if (unpack_callsign(n28a >> 1, n28a & 0x01, message->i3, message->call_to,&message->call_to_hash) < 0) {
+ return -1;
+ }
+ if (unpack_callsign(n28b >> 1, n28b & 0x01, message->i3, message->call_de,&message->call_de_hash) < 0) {
+ return -2;
+ }
+ // Fix "CQ_" to "CQ " -> already done in unpack_callsign()
+
+ // TODO: add to recent calls
+ // if (call_to[0] != '<' && strlen(call_to) >= 4) {
+ // save_hash_call(call_to)
+ // }
+ // if (call_de[0] != '<' && strlen(call_de) >= 4) {
+ // save_hash_call(call_de)
+ // }
+
+ char *dst = message->extra;
+ message->report=-100;//-100说明没有信号报告
+ message->maidenGrid[0]='\0';
+ if (igrid4 <= MAXGRID4) {
+ // // 解码4字符的梅登海德网格,!!!网格数据优先于信号报告
+ if (ir > 0) {
+ // In case of ir=1 add an "R" before grid
+ dst = strcpy(dst, "R ");
+ dst += 3;
+ }
+
+ uint16_t n = igrid4;
+ dst[4] = '\0';
+ dst[3] = '0' + (n % 10);
+ n /= 10;
+ dst[2] = '0' + (n % 10);
+ n /= 10;
+ dst[1] = 'A' + (n % 18);
+ n /= 18;
+ dst[0] = 'A' + (n % 18);
+ strcpy(message->maidenGrid, dst);//把网格内容复制出来
+
+
+ // if (ir > 0 && strncmp(call_to, "CQ", 2) == 0) return -1;
+ } else {
+ // 解码信号报告,数值-35是报告。
+ // 网格数据优先于信号报告
+ message->report = igrid4 - MAXGRID4 - 35;
+
+ // Check special cases first (irpt > 0 always)
+ switch (message->report) {
+ case 1 - 35:
+ message->extra[0] = '\0';
+ break;
+ case 2 - 35:
+ strcpy(dst, "RRR");
+ break;
+ case 3 - 35:
+ strcpy(dst, "RR73");
+ break;
+ case 4 - 35:
+ strcpy(dst, "73");
+ break;
+ default:
+ // Extract signal report as a two digit number with a + or - sign
+ if (ir > 0) {
+ *dst++ = 'R'; // Add "R" before report
+ }
+ int_to_dd(dst, message->report, 2, true);
+ break;
+ }
+ // if (irpt >= 2 && strncmp(call_to, "CQ", 2) == 0) return -1;
+ }
+
+
+
+ return 0; // Success
+}
+
+
+int unpack_text(const uint8_t *a71, char *text) {
+ // TODO: test
+ uint8_t b71[9];
+
+ // Shift 71 bits right by 1 bit, so that it's right-aligned in the byte array
+ uint8_t carry = 0;
+ for (int i = 0; i < 9; ++i) {
+ b71[i] = carry | (a71[i] >> 1);
+ carry = (a71[i] & 1) ? 0x80 : 0;
+ }
+
+ char c14[14];
+ c14[13] = 0;
+ for (int idx = 12; idx >= 0; --idx) {
+ // Divide the long integer in b71 by 42
+ uint16_t rem = 0;
+ for (int i = 0; i < 9; ++i) {
+ rem = (rem << 8) | b71[i];
+ b71[i] = rem / 42;
+ rem = rem % 42;
+ }
+ c14[idx] = charn(rem, 0);
+ }
+
+ strcpy(text, trim(c14));
+ return 0; // Success
+}
+
+int unpack_telemetry(const uint8_t *a71, char *telemetry) {
+ uint8_t b71[9];
+
+ // Shift bits in a71 right by 1 bit
+ uint8_t carry = 0;
+ for (int i = 0; i < 9; ++i) {
+ b71[i] = (carry << 7) | (a71[i] >> 1);
+ carry = (a71[i] & 0x01);
+ }
+
+ // Convert b71 to hexadecimal string
+ for (int i = 0; i < 9; ++i) {
+ uint8_t nibble1 = (b71[i] >> 4);
+ uint8_t nibble2 = (b71[i] & 0x0F);
+ char c1 = (nibble1 > 9) ? (nibble1 - 10 + 'A') : nibble1 + '0';
+ char c2 = (nibble2 > 9) ? (nibble2 - 10 + 'A') : nibble2 + '0';
+ telemetry[i * 2] = c1;
+ telemetry[i * 2 + 1] = c2;
+ }
+
+ telemetry[18] = '\0';
+ return 0;
+}
+
+//none standard for wsjt-x 2.0
+//by KD8CEC
+int unpack_nonstandard(const uint8_t *a77, message_t *message) {
+ uint32_t n12, iflip, nrpt, icq;
+ uint64_t n58;
+ n12 = (a77[0] << 4); //11 ~4 : 8
+ n12 |= (a77[1] >> 4); //3~0 : 12
+ uint32_t h12=a77[0];
+
+
+
+
+ n58 = ((uint64_t) (a77[1] & 0x0F) << 54); //57 ~ 54 : 4
+ n58 |= ((uint64_t) a77[2] << 46); //53 ~ 46 : 12
+ n58 |= ((uint64_t) a77[3] << 38); //45 ~ 38 : 12
+ n58 |= ((uint64_t) a77[4] << 30); //37 ~ 30 : 12
+ n58 |= ((uint64_t) a77[5] << 22); //29 ~ 22 : 12
+ n58 |= ((uint64_t) a77[6] << 14); //21 ~ 14 : 12
+ n58 |= ((uint64_t) a77[7] << 6); //13 ~ 6 : 12
+ n58 |= ((uint64_t) a77[8] >> 2); //5 ~ 0 : 765432 10
+
+
+ iflip = (a77[8] >> 1) & 0x01; //76543210
+ nrpt = ((a77[8] & 0x01) << 1);
+ nrpt |= (a77[9] >> 7); //76543210
+ icq = ((a77[9] >> 6) & 0x01);
+
+
+ if (iflip==1){//h1==1
+ message->call_de_hash.hash12=n12;
+ } else{
+ message->call_to_hash.hash12=n12;
+ }
+
+ char c11[12];
+ c11[11] = '\0';
+
+ for (int i = 10; /* no condition */; --i) {
+ c11[i] = charn(n58 % 38, 5);
+ if (i == 0)
+ break;
+ n58 /= 38;
+ }
+
+ char call_3[15];
+ // should replace with hash12(n12, call_3);
+ strcpy(call_3, "<...>");
+ // call_3[0] = '<';
+ // int_to_dd(call_3 + 1, n12, 4, false);
+ // call_3[5] = '>';
+ // call_3[6] = '\0';
+
+ char *call_1 = (iflip) ? c11 : call_3;
+ char *call_2 = (iflip) ? call_3 : c11;
+ //save_hash_call(c11_trimmed);
+
+
+
+ if (icq == 0) {
+ strcpy(message->call_to, trim(call_1));
+ if (nrpt == 1)
+ strcpy(message->extra, "RRR");
+ else if (nrpt == 2)
+ strcpy(message->extra, "RR73");
+ else if (nrpt == 3)
+ strcpy(message->extra, "73");
+ else {
+ message->extra[0] = '\0';
+ }
+ } else {
+ strcpy(message->call_to, "CQ");
+ message->extra[0] = '\0';
+ }
+ strcpy(message->call_de, trim(call_2));
+
+ return 0;
+}
+
+//int unpack77_fields(const uint8_t *a77, char *call_to, char *call_de, char *extra) {
+// call_to[0] = call_de[0] = extra[0] = '\0';
+//
+// // Extract i3 (bits 74..76)
+// uint8_t i3 = (a77[9] >> 3) & 0x07;
+//
+// if (i3 == 0) {
+// // Extract n3 (bits 71..73)
+// uint8_t n3 = ((a77[8] << 2) & 0x04) | ((a77[9] >> 6) & 0x03);
+//
+// if (n3 == 0) {
+// // 0.0 Free text
+// return unpack_text(a77, extra);
+// }
+// // else if (i3 == 0 && n3 == 1) {
+// // // 0.1 K1ABC RR73; W9XYZ -11 28 28 10 5 71 DXpedition Mode
+// // }
+// // else if (i3 == 0 && n3 == 2) {
+// // // 0.2 PA3XYZ/P R 590003 IO91NP 28 1 1 3 12 25 70 EU VHF contest
+// // }
+// // else if (i3 == 0 && (n3 == 3 || n3 == 4)) {
+// // // 0.3 WA9XYZ KA1ABC R 16A EMA 28 28 1 4 3 7 71 ARRL Field Day
+// // // 0.4 WA9XYZ KA1ABC R 32A EMA 28 28 1 4 3 7 71 ARRL Field Day
+// // }
+// else if (n3 == 5) {
+// // 0.5 0123456789abcdef01 71 71 Telemetry (18 hex)
+// return unpack_telemetry(a77, extra);
+// }
+// } else if (i3 == 1 || i3 == 2) {
+// // Type 1 (standard message) or Type 2 ("/P" form for EU VHF contest)
+// return unpack_type1(a77, i3, call_to, call_de, extra);
+// }
+// // else if (i3 == 3) {
+// // // Type 3: ARRL RTTY Contest
+// // }
+// else if (i3 == 4) {
+// // // Type 4: Nonstandard calls, e.g. PJ4/KA1ABC RR73
+// // // One hashed call or "CQ"; one compound or nonstandard call with up
+// // // to 11 characters; and (if not "CQ") an optional RRR, RR73, or 73.
+// return unpack_nonstandard(a77, call_to, call_de, extra);
+// }
+// // else if (i3 == 5) {
+// // // Type 5: TU; W9XYZ K1ABC R-09 FN 1 28 28 1 7 9 74 WWROF contest
+// // }
+//
+// // unknown type, should never get here
+// return -1;
+//}
+//
+
+
+
+
+//int unpack77(const uint8_t* a77, char* message)
+//{
+// char call_to[14];
+// char call_de[14];
+// char extra[19];
+//
+// //// TO-DO:在此处修改。把Message的格式修改成i3,n3,from,to,extra,方便消息显示。
+// int rc = unpack77_fields(a77, call_to, call_de, extra);
+// if (rc < 0)
+// return rc;
+//
+// // int msg_sz = strlen(call_to) + strlen(call_de) + strlen(extra) + 2;
+// char* dst = message;
+//
+// dst[0] = '\0';
+//
+// if (call_to[0] != '\0')
+// {
+// // dst = stpcpy(dst, call_to); //除错
+// dst = strcpy(dst, call_to);
+// dst +=strlen(call_to);
+// *dst++ = ' ';
+// }
+//
+// if (call_de[0] != '\0')
+// {
+// // dst = stpcpy(dst, call_de);//除错
+// dst = strcpy(dst, call_de);
+// dst +=strlen(call_de);
+// *dst++ = ' ';
+// }
+//
+// // dst = stpcpy(dst, extra);//除错
+// dst = strcpy(dst, extra);
+// dst += strlen(extra);
+// *dst = '\0';
+//
+// return 0;
+//}
+
+
+
+int unpack77_fields_(const uint8_t *a77, message_t *message) {
+
+ //获取指令类型i3.n3
+ message->i3 = (a77[9] >> 3) & 0x07;
+ message->n3 = 0;
+
+ if (message->i3 == 0) {
+ //n3指令
+ message->n3 = ((a77[8] << 2) & 0x04) | ((a77[9] >> 6) & 0x03);
+
+ if (message->n3 == 0) {
+ // 0.0 Free text
+ return unpack_text(a77, message->extra);
+ }
+ // else if (i3 == 0 && n3 == 1) {
+ // // 0.1 K1ABC RR73; W9XYZ -11 28 28 10 5 71 DXpedition Mode
+ // }
+ // else if (i3 == 0 && n3 == 2) {
+ // // 0.2 PA3XYZ/P R 590003 IO91NP 28 1 1 3 12 25 70 EU VHF contest
+ // }
+ // else if (i3 == 0 && (n3 == 3 || n3 == 4)) {
+ // // 0.3 WA9XYZ KA1ABC R 16A EMA 28 28 1 4 3 7 71 ARRL Field Day
+ // // 0.4 WA9XYZ KA1ABC R 32A EMA 28 28 1 4 3 7 71 ARRL Field Day
+ // }
+ else if (message->n3 == 5) {
+ // 0.5 0123456789abcdef01 71 71 Telemetry (18 hex)
+ return unpack_telemetry(a77, message->extra);
+ }
+ } else if (message->i3 == 1 || message->i3 == 2) {
+ // Type 1 (standard message) or Type 2 ("/P" form for EU VHF contest)
+ return unpack_type1_(a77, message);
+ }
+ // else if (i3 == 3) {
+ // // Type 3: ARRL RTTY Contest
+ // }
+ else if (message->i3 == 4) {
+ // // Type 4: Nonstandard calls, e.g. PJ4/KA1ABC RR73
+ // // One hashed call or "CQ"; one compound or nonstandard call with up
+ // // to 11 characters; and (if not "CQ") an optional RRR, RR73, or 73.
+ //return unpack_nonstandard(a77, message->call_to, message->call_de, message->extra);
+ return unpack_nonstandard(a77, message);
+ }
+ // else if (i3 == 5) {
+ // // Type 5: TU; W9XYZ K1ABC R-09 FN 1 28 28 1 7 9 74 WWROF contest
+ // }
+
+ // unknown type, should never get here
+ return -1;
+
+
+}
+
+
+//把77位消息解码到message_t
+int unpackToMessage_t(const uint8_t *a77, message_t *message) {
+ //char call_to[14];
+ //char call_de[14];
+ //char extra[19];
+
+ //// TO-DO:在此处修改。把Message的格式修改成i3,n3,from,to,extra,方便消息显示。
+ int rc = unpack77_fields_(a77, message);
+
+ if (rc < 0)
+ return rc;
+
+
+ // int msg_sz = strlen(call_to) + strlen(call_de) + strlen(extra) + 2;
+ char *dst = message->text;
+
+ message->text[0] = '\0';
+
+ if (message->call_to[0] != '\0') {
+ // dst = stpcpy(dst, call_to); //除错
+ dst = strcpy(dst, message->call_to);
+ dst += strlen(message->call_to);
+ *dst++ = ' ';
+ }
+
+ if (message->call_de[0] != '\0') {
+ // dst = stpcpy(dst, call_de);//除错
+ dst = strcpy(dst, message->call_de);
+ dst += strlen(message->call_de);
+ *dst++ = ' ';
+ }
+
+ // dst = stpcpy(dst, extra);//除错
+ dst = strcpy(dst, message->extra);
+ dst += strlen(message->extra);
+ *dst = '\0';
+
+ return 0;
+}
\ No newline at end of file
diff --git a/ft8CN/app/src/main/cpp/ft8/unpack.h b/ft8CN/app/src/main/cpp/ft8/unpack.h
new file mode 100644
index 0000000..2c716f6
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8/unpack.h
@@ -0,0 +1,19 @@
+#ifndef _INCLUDE_UNPACK_H_
+#define _INCLUDE_UNPACK_H_
+
+#include
+#include "decode.h"
+// field1 - at least 14 bytes
+// field2 - at least 14 bytes
+// field3 - at least 7 bytes
+
+//int unpack77_fields(const uint8_t* a77, char* field1, char* field2, char* field3);
+
+// message should have at least 35 bytes allocated (34 characters + zero terminator)
+/// 消息应至少分配35个字节(34个字符+零终止符)
+int unpack77(const uint8_t* a77, char* message);
+
+//新增的函数,把消息解码成message_t
+int unpackToMessage_t(const uint8_t* a77, message_t* message);
+
+#endif // _INCLUDE_UNPACK_H_
diff --git a/ft8CN/app/src/main/cpp/ft8Decoder.c b/ft8CN/app/src/main/cpp/ft8Decoder.c
new file mode 100644
index 0000000..5f59ad1
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8Decoder.c
@@ -0,0 +1,292 @@
+//
+// Created by jmsmf on 2022/4/24.
+//
+
+#include "ft8Decoder.h"
+
+#define LOG_LEVEL LOG_INFO
+//decoder_t decoder;
+
+// Hanning窗(汉宁窗)适用于95%的情况。
+static float hann_i(int i, int N) {
+ float x = sinf((float) M_PI * i / N);
+ return x * x;
+}
+
+//把信号FFT,在解码decoder中减去信号
+void signalToFFT(decoder_t *decoder, float signal[], int sample_rate) {
+ int nfft = kFreq_osr * (int) (sample_rate * FT8_SYMBOL_PERIOD);//nfft=一个FSK符号的样本数*频率过采样率
+ float fft_norm = 2.0f / nfft;//< FFT归一化因子。FFT normalization factor
+ float *window = (float *) malloc(
+ nfft * sizeof(window[0]));// 申请窗空间,大小是fft块大小*sizeof(me->windows[0])
+ for (int i = 0; i < nfft; ++i) {
+ window[i] = hann_i(i, nfft);// 使用Hanning窗
+ // window[i] = blackman_i(i, me->nfft);// Blackman-Harris窗
+ // window[i] = hamming_i(i, me->nfft);// Hamming窗
+ // window[i] = (i < len_window) ? hann_i(i, len_window) : 0;
+ }
+
+ // 申请当前STFT(短时傅氏变换)分析框架(nfft样本)的空间。
+ //last_frame:申请傅里叶变换分析框架用的(nfft样本)。
+ // 空间大小=时域数据量*数据类型占用的空间=一个FSK符号占用的采样数据量*频率过采样率=12000*0.16*2*SizeOf(float)
+ float *last_frame = (float *) malloc(nfft * sizeof(last_frame[0]));
+
+ size_t fft_work_size;
+ // 第一步,获取可以用的FFT工作区域的大小到fft_work_size
+ //nfft=一个FSK符号的样本数*频率过采样率=0.16*12000*2=3840
+ kiss_fftr_alloc(nfft, 0, 0, &fft_work_size);
+
+ // 申请FFT工作区域的内存,38676个
+ void *fft_work = malloc(fft_work_size);
+ //第二步,返回fft的设置信息
+ kiss_fftr_cfg fft_cfg = kiss_fftr_alloc(nfft, 0, fft_work, &fft_work_size);
+
+
+ free(fft_work);
+ free(window);
+ free(last_frame);
+
+}
+
+void *init_decoder(int64_t utcTime, int sample_rate, int num_samples, bool is_ft8) {
+
+ //此处,改为一个变量,不是以指针,申请新内存的方式处理了。
+ //其实这种方式要注意一个问题,在一个周期之内,必须解码完毕,否则新的解码又要开始了
+
+ //此处不用申请内存的方式解决
+// decoder.utcTime = utcTime;
+// decoder.num_samples = num_samples;
+// decoder.mon_cfg = (monitor_config_t) {
+// .f_min = 100,//分析的最低频率边界
+// .f_max = 3000,//分析的最大频率边界
+// .sample_rate = sample_rate,//采样率12000Hz
+// .time_osr = kTime_osr,//时间过采样率=2
+// .freq_osr = kFreq_osr,//频率过采样率=2
+// .protocol = is_ft8 ? PROTO_FT8 : PROTO_FT4
+// };
+// //LOGD("Init decoder . address : %lld", decoder);
+// monitor_init(&decoder.mon, &decoder.mon_cfg);
+//
+//
+// return &decoder;
+
+ //此部分,是老的解决方式,是动态申请内存。
+ decoder_t *decoder;
+ decoder = malloc(sizeof(decoder_t));
+ decoder->utcTime = utcTime;
+ decoder->num_samples = num_samples;
+ decoder->mon_cfg = (monitor_config_t) {
+ .f_min = 100,//分析的最低频率边界
+ .f_max = 3000,//分析的最大频率边界
+ .sample_rate = sample_rate,//采样率12000Hz
+ .time_osr = kTime_osr,//时间过采样率=2
+ .freq_osr = kFreq_osr,//频率过采样率=2
+ .protocol = is_ft8 ? PROTO_FT8 : PROTO_FT4
+ };
+
+ decoder->kLDPC_iterations = fast_kLDPC_iterations;
+ //LOGD("Init decoder . address : %lld", decoder);
+ monitor_init(&decoder->mon, &decoder->mon_cfg);
+
+ return decoder;
+}
+
+void delete_decoder(decoder_t *decoder) {
+ //LOGD("Free decoder , address:%lld", decoder);
+ monitor_free(&decoder->mon);
+ free(decoder);
+}
+
+void decoder_monitor_press(float signal[], decoder_t *decoder) {
+
+ // 以每一个FSK符号占用的数据量为单位循环。
+ //block_size每个符号的样本数12000*0.16=1920
+
+ for (int frame_pos = 0;
+ frame_pos + decoder->mon.block_size <=
+ decoder->num_samples; frame_pos += decoder->mon.block_size) {
+ // Process the waveform data frame by frame - you could have a live loop here with data from an audio device
+ // 逐帧处理波形数据,这个位置可以使用音频设备的数据环。
+ //以每一个符号时间长度(0.16)内的数据做瀑布数据,最后会形成一个信号量mag数组。
+ // mag数组的总长度是:最大符号块数93*时间过采样率2*频率过采样率2*分析块960,也就是Waterfall size = 357120
+ //一次调用monitor_process,处理的是一个符号长度的信号量数据,生成2*2*960=3840个mag数据。
+ //一共有93个符号的循环,mag数组的大小:2*2*960*93=357120
+ //mag数据保存在monitor.wf.mag中。
+ monitor_process(&decoder->mon, signal + frame_pos);
+ }
+
+ // /data/user/0/com.bg7yoz.ft8cn/cache/
+
+ //把fft数据保存下来
+ //FILE * fp2 = fopen("/data/user/0/com.bg7yoz.ft8cn/cache/fft001.txt", "w");//打开输出文件
+ //for (int i = 0; i < 3840; ++i) {
+ // for (int j = 0; j < 93; ++j) {
+ // fprintf (fp2,"%d\n", decoder->mon.wf.mag[i*j]);//把数组a逆序写入到输出文件当中
+ // }
+ //}
+ //fclose(fp2);//关闭输出文件,相当于保存
+
+
+
+ LOG(LOG_DEBUG, "Waterfall accumulated %d symbols\n", decoder->mon.wf.num_blocks);//积累的信号
+ LOG(LOG_INFO, "Max magnitude: %.1f dB\n", decoder->mon.max_mag);//最大信号值dB
+
+}
+
+int decoder_ft8_find_sync(decoder_t *decoder) {
+ //检测ft8信号,kMax_candidates最大候选人数量=120,candidate_list候选人列表(size=120),kMin_score候选人的最低同步分数阈值=10
+ decoder->num_candidates = ft8_find_sync(&decoder->mon.wf, kMax_candidates,
+ decoder->candidate_list, kMin_score);
+ LOG(LOG_DEBUG, "ft8_find_sync finished. %d candidates\n", decoder->num_candidates);
+
+
+ // Hash table for decoded messages (to check for duplicates)
+ // 解码消息的哈希表(用于检查重复项)
+ //int num_decoded = 0;
+ //message_t decoded[kMax_decoded_messages];//kMax_decoded_messages=50
+ // 哈希表指针列表(指针数组)
+ //message_t *decoded_hashtable[kMax_decoded_messages];
+
+ // Initialize hash table pointers
+ // 初始化哈希表指针列表
+ for (int i = 0; i < kMax_decoded_messages; ++i) {
+ decoder->decoded_hashtable[i] = NULL;
+ }
+ return decoder->num_candidates;
+}
+
+
+ft8_message decoder_ft8_analysis(int idx, decoder_t *decoder) {
+
+ ft8_message ft8Message;
+ ft8Message.isValid = false;
+ ft8Message.utcTime = decoder->utcTime;
+ // 候选列表candidate_list,已经从ft8_fing_sync获得。
+ ft8Message.candidate = decoder->candidate_list[idx];
+
+
+ if (ft8Message.candidate.score < kMin_score) {
+ //ft8Message.isValid = false;
+ return ft8Message;
+ }
+
+ ft8Message.freq_hz =
+ (ft8Message.candidate.freq_offset +
+ (float) ft8Message.candidate.freq_sub / decoder->mon.wf.freq_osr) /
+ decoder->mon.symbol_period;
+ ft8Message.time_sec =
+ ((ft8Message.candidate.time_offset + (float) ft8Message.candidate.time_sub)
+ * decoder->mon.symbol_period) / decoder->mon.wf.time_osr;
+
+ //ft8Message.snr=ft8Message.candidate.snr;
+ //这是原来代码的时间偏移,同样的数据与JTDX的时间差异很大,改用上面的代码,稍微接近一些
+ //(ft8Message.candidate.time_offset +
+ //(float) ft8Message.candidate.time_sub / decoder->mon.wf.time_osr) *
+ //decoder->mon.symbol_period;
+
+
+ // 如果解码失败,跳到下一次循环 kLDPC_iterations=20 LDPC(低密度奇偶校验)的迭代次数。
+ if (!ft8_decode(&decoder->mon.wf, &ft8Message.candidate
+ //, &ft8Message.message, kLDPC_iterations,
+ , &ft8Message.message, decoder->kLDPC_iterations,
+ &ft8Message.status)) {
+ // printf("000000 %3d %+4.2f %4.0f ~ ---\n", cand->score, time_sec, freq_hz);
+ if (ft8Message.status.ldpc_errors > 0) {
+ // LDPC:低密度奇偶校验
+ LOG(LOG_DEBUG, "LDPC decode: %d errors\n", ft8Message.status.ldpc_errors);
+ } else if (ft8Message.status.crc_calculated != ft8Message.status.crc_extracted) {
+ LOG(LOG_DEBUG, "CRC mismatch!\n");
+ } else if (ft8Message.status.unpack_status != 0) {
+ LOG(LOG_DEBUG, "Error while unpacking!\n");
+ }
+ //ft8Message.isValid = false;
+ return ft8Message;
+ }
+
+ ft8Message.snr = ft8Message.candidate.snr;
+
+ LOG(LOG_DEBUG, "Checking hash table for %4.1fs / %4.1fHz [%d]...\n", ft8Message.time_sec,
+ ft8Message.freq_hz,
+ ft8Message.candidate.score);
+
+ int idx_hash =
+ ft8Message.message.hash % kMax_decoded_messages;//为啥是取模?稍后研究kMax_decoded_messages=50
+
+ bool found_empty_slot = false;
+ bool found_duplicate = false;
+ //检查哈希表,只有空插槽,或重复插槽(哈希值相同,并且消息相同)
+ do {
+ if (decoder->decoded_hashtable[idx_hash] == NULL) {
+ LOG(LOG_DEBUG, "Found an empty slot\n");
+ found_empty_slot = true;
+ } else if ((decoder->decoded_hashtable[idx_hash]->hash == ft8Message.message.hash) &&
+ (0 ==
+ strcmp(decoder->decoded_hashtable[idx_hash]->text, ft8Message.message.text))) {
+ LOG(LOG_DEBUG, "Found a duplicate [%s]\n", ft8Message.message.text);
+ found_duplicate = true;
+ } else {
+ LOG(LOG_DEBUG, "Hash table clash!\n");
+ // Move on to check the next entry in hash table
+ idx_hash = (idx_hash + 1) % kMax_decoded_messages;
+ }
+ } while (!found_empty_slot && !found_duplicate);
+
+
+ if (found_empty_slot) {
+ // Fill the empty hashtable slot
+ memcpy(&decoder->decoded[idx_hash], &ft8Message.message, sizeof(ft8Message.message));
+ decoder->decoded_hashtable[idx_hash] = &decoder->decoded[idx_hash];
+ ++decoder->num_decoded;
+
+
+ ft8Message.isValid = true;
+
+ LOG_PRINTF("%3d %+4.2f %4.0f ~ %s report:%d grid:%s,toHash:%x,fromHash:%x",
+ ft8Message.snr,
+ ft8Message.time_sec, ft8Message.freq_hz, ft8Message.message.text,
+ ft8Message.message.report, ft8Message.message.maidenGrid,
+ ft8Message.message.call_to_hash.hash12, ft8Message.message.call_de_hash.hash12);
+ }
+ memcpy(decoder->a91, ft8Message.message.a91, FTX_LDPC_K_BYTES);
+ return ft8Message;
+}
+
+void decoder_ft8_reset(decoder_t *decoder, long utcTime, int num_samples) {
+ LOG(LOG_DEBUG, "Monitor is resetting...");
+ decoder->mon.wf.num_blocks = 0;
+ //decoder->mon.max_mag = 0;
+ decoder->mon.max_mag = -120.0f;
+ decoder->utcTime = utcTime;
+ decoder->num_samples = num_samples;
+}
+
+/**
+ * 对174码,重新编生成79码
+ * @param a174 174个int
+ * @param a79 79个int
+ */
+void recode(int a174[], int a79[]) {
+ int i174 = 0;
+ //int costas[] = { 3, 1, 4, 0, 6, 5, 2 };
+ //std::vector out79;
+ for (int i79 = 0; i79 < 79; i79++) {
+ if (i79 < 7) {
+ //out79.push_back(costas[i79]);
+ a79[i79] = kFT8CostasPattern[i79];
+ } else if (i79 >= 36 && i79 < 36 + 7) {
+ //out79.push_back(costas[i79-36]);
+ a79[i79] = kFT8CostasPattern[i79 - 36];
+ } else if (i79 >= 72) {
+ //out79.push_back(costas[i79-72]);
+ a79[i79] = kFT8CostasPattern[i79 - 72];
+ } else {
+ int sym = (a174[i174 + 0] << 2) | (a174[i174 + 1] << 1) | (a174[i174 + 2] << 0);
+ i174 += 3;
+ // gray code
+ int map[] = {0, 1, 3, 2, 5, 6, 4, 7};
+ sym = map[sym];
+ //out79.push_back(sym);
+ a79[i79] = sym;
+ }
+ }
+};
diff --git a/ft8CN/app/src/main/cpp/ft8Decoder.h b/ft8CN/app/src/main/cpp/ft8Decoder.h
new file mode 100644
index 0000000..d8e88e1
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8Decoder.h
@@ -0,0 +1,69 @@
+//
+// Created by jmsmf on 2022/4/24.
+//
+//
+#include "ft8/decode.h"
+#include "monitor_opr.h"
+#include "ft8/constants.h"
+#include
+
+//const int kMin_score = 10; // 候选人的最低同步分数阈值。Minimum sync score threshold for candidates
+const int kMax_candidates = 120;//最大候选人数量
+
+const int kMax_decoded_messages = 100;
+//const int kMax_decoded_messages = 50;
+const int kLDPC_iterations = 20;//LDPC(低密度奇偶校验)的迭代次数,数值越大,精度越高,速度越慢
+const int deep_kLDPC_iterations = 200;//LDPC(低密度奇偶校验)的迭代次数
+const int fast_kLDPC_iterations = 20;//LDPC(低密度奇偶校验)的迭代次数
+
+typedef struct {
+ long long utcTime;//UTC时间
+ int num_samples;//采样率
+ int num_candidates;
+ int num_decoded;
+ message_t decoded[kMax_decoded_messages];//kMax_decoded_messages=50
+ // 哈希表指针列表(指针数组)
+ message_t *decoded_hashtable[kMax_decoded_messages];
+
+ // Find top candidates by Costas sync score and localize them in time and frequency
+ // 从科斯塔斯阵列(Costas)寻找最佳候选,并在时间和频率上对其进行本地化。候选数组最大120个
+ // candidate_t定义在decode.h
+ candidate_t candidate_list[kMax_candidates];//kMax_candidates=120
+
+ monitor_t mon;
+ monitor_config_t mon_cfg;
+ uint8_t a91[FTX_LDPC_K_BYTES];//用于生成减法代码的数据
+ int kLDPC_iterations;//ldpc 迭代次数,数值越大,精度越高,速度越慢:20或100
+} decoder_t;
+
+typedef struct {
+ int64_t utcTime;//消息的UTC时间
+ bool isValid;//是否为有效消息
+ int snr;//信噪比
+ candidate_t candidate;//消息的原始信号数据
+ float time_sec;//时间偏移值
+ float freq_hz;//频率偏移值
+ message_t message;//解码后的消息
+ decode_status_t status;
+} ft8_message;
+
+static const int kFreq_osr = 2; // 频率过采样率。Frequency oversampling rate (bin subdivision)
+static const int kTime_osr = 2; // 时间过采样率。Time oversampling rate (symbol subdivision)
+
+//把信号FFT,在解码decoder中减去信号
+void signalToFFT(decoder_t *decoder,float signal[], int sample_rate);
+//初始化解码器所需要的参数,最后通过指针的方式传递给java
+void *init_decoder(int64_t utcTime, int sample_rate, int num_samples, bool is_ft8);
+
+void delete_decoder(decoder_t *decoder);
+
+void decoder_monitor_press(float signal[], decoder_t *decoder);
+
+int decoder_ft8_find_sync(decoder_t *decoder);
+
+ft8_message decoder_ft8_analysis(int idx, decoder_t *decoder);
+
+void decoder_ft8_reset(decoder_t *decoder,long utcTime,int num_samples);
+
+void recode(int a174[],int a79[]);
+
diff --git a/ft8CN/app/src/main/cpp/ft8Encoder.c b/ft8CN/app/src/main/cpp/ft8Encoder.c
new file mode 100644
index 0000000..9363e51
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8Encoder.c
@@ -0,0 +1,150 @@
+//
+// Created by jmsmf on 2022/6/1.
+//
+
+#include "ft8Encoder.h"
+
+//#define LOG_LEVEL LOG_INFO
+
+#define FT8_SYMBOL_BT 2.0f /// 符号平滑滤波器带宽因子(BT)
+#define FT4_SYMBOL_BT 1.0f /// 符号平滑滤波器带宽因子(BT)
+#define GFSK_CONST_K 5.336446f ///< == pi * sqrt(2 / log(2))
+
+
+/// 生成高斯平滑脉冲
+/// 脉冲理论上是无限长的,然而,这里它被截断为符号长度的3倍。
+/// 这意味着脉冲阵列必须有空间容纳3*n_spsym元素。
+/// @param[in] n_spsym 每个符号的样本数 Number of samples per symbol
+/// @param[in] b 形状参数(为FT8/FT4定义的值)
+/// @param[out] pulse 脉冲采样输出阵列
+///
+void gfsk_pulse(int n_spsym, float symbol_bt, float *pulse) {
+ for (int i = 0; i < 3 * n_spsym; ++i) {
+ float t = i / (float) n_spsym - 1.5f;
+ float arg1 = GFSK_CONST_K * symbol_bt * (t + 0.5f);
+ float arg2 = GFSK_CONST_K * symbol_bt * (t - 0.5f);
+ pulse[i] = (erff(arg1) - erff(arg2)) / 2;
+ }
+}
+
+
+/// 使用GFSK相位整形合成波形数据。
+/// 输出波形将包含n_sym个符号。
+/// @param[in] symbols 符号(音调)数组 (0-7 for FT8)
+/// @param[in] n_sym 符号数组中的符号数
+/// @param[in] f0 符号0的音频频率(赫兹) (载波频率)
+/// @param[in] symbol_bt 符号平滑滤波器带宽 (2 for FT8, 1 for FT4)
+/// @param[in] symbol_period 符号周期(持续时间),秒
+/// @param[in] signal_rate 合成信号的采样率,赫兹
+/// @param[out] signal 信号波形样本的输出阵列(应为n_sym*n_spsym样本留出空间)
+///
+void synth_gfsk(const uint8_t *symbols, int n_sym, float f0, float symbol_bt, float symbol_period,
+ int signal_rate, float *signal) {
+ int n_spsym = (int) (0.5f + (float)signal_rate * symbol_period); // 每个符号的样本数12000*0.16=1920
+ int n_wave = n_sym * n_spsym; // 输出样本数79*1920=151680
+ float hmod = 1.0f;
+
+
+ // 计算平滑的频率波形。
+ // Length = (nsym+2)*n_spsym samples, 首个和最后一个扩展符号
+ float dphi_peak = 2 * M_PI * hmod / n_spsym;
+
+ //此处是与采样率有关,采样率提高后,可能会有闪退的问题
+ float *dphi;//此处使用内存申请的方式,而不是原来数组的方式,因为是采样率过高时,会报内存出错。
+ dphi = malloc(sizeof(float) * (n_wave + 2 * n_spsym));
+ if (dphi==0) return;//内存申请失败
+ //float dphi[n_wave + 2 * n_spsym];//原来的方式
+
+ // 频率上移f0
+ for (int i = 0; i < n_wave + 2 * n_spsym; ++i) {
+ dphi[i] = 2 * M_PI * f0 / signal_rate;
+ }
+
+ //float pulse[3 * n_spsym];
+ float *pulse=(float *) malloc(sizeof(float)*3 * n_spsym);
+ gfsk_pulse(n_spsym, symbol_bt, pulse);
+
+ for (int i = 0; i < n_sym; ++i) {
+ int ib = i * n_spsym;
+ for (int j = 0; j < 3 * n_spsym; ++j) {
+ dphi[j + ib] += dphi_peak * symbols[i] * pulse[j];
+ }
+ }
+ // 在开头和结尾添加伪符号,音调值分别等于第一个符号和最后一个符号
+ for (int j = 0; j < 2 * n_spsym; ++j) {
+ dphi[j] += dphi_peak * pulse[j + n_spsym] * symbols[0];
+ dphi[j + n_sym * n_spsym] += dphi_peak * pulse[j] * symbols[n_sym - 1];
+ }
+ // 计算并插入音频波形
+ float phi = 0;
+ for (int k = 0; k < n_wave; ++k) { // 不包括虚拟符号
+ signal[k] = sinf(phi);
+ phi = fmodf(phi + dphi[k + n_spsym], 2 * M_PI);
+ }
+ // 对第一个和最后一个符号应用封套成形,此处是前后增加斜坡函数,
+ int n_ramp = n_spsym / 8;//240个样本,20毫秒,T/8
+ for (int i = 0; i < n_ramp; ++i) {
+ float env = (1 - cosf(2 * M_PI * i / (2 * n_ramp))) / 2;
+ signal[i] *= env;
+ signal[n_wave - 1 - i] *= env;
+ }
+ free(pulse);
+ free(dphi);//要释放掉内存
+}
+
+//此代码已经弃用
+void generateFt8ToBuffer(char *message, float frequency, short *buffer) {
+// 首先,将文本数据打包为二进制消息
+ uint8_t packed[FTX_LDPC_K_BYTES];//91位,包括CRC。
+ int rc = pack77(message, packed);//生成数据
+ if (rc < 0) {
+ //LOGE("Cannot parse message!\n");
+ //LOGE("RC = %d\n", rc);
+ return;
+ }
+
+
+ //int num_tones = FT8_NN;//符号数量:FT8是79个,FT4是105个。
+ //float symbol_period = FT8_SYMBOL_PERIOD;//FT8_SYMBOL_PERIOD=0.160f
+ float symbol_bt = FT8_SYMBOL_BT;//FT8_SYMBOL_BT=2.0f
+ float slot_time = FT8_SLOT_TIME;//FT8_SLOT_TIME=15f
+
+ // 其次,将二进制消息编码为FSK音调序列
+ uint8_t tones[FT8_NN]; // 79音调(符号)数组
+ ft8_encode(packed, tones);
+
+
+
+ // 第三,将FSK音调转换为音频信号b
+ //int sample_rate = FT8_SAMPLE_RATE;//采样率
+ int num_samples = (int) (0.5f + FT8_NN * FT8_SYMBOL_PERIOD *
+ FT8_SAMPLE_RATE); // 数据信号中的采样数0.5+79*0.16*12000
+ //int num_silence = (slot_time * sample_rate - num_samples) / 2; // 两端填充静音到15秒(15*12000-num_samples)/2(1.18秒的样本数)
+ int num_silence = 20;//把前面的静音时长缩短为20毫秒,留出时间给解码
+ //int num_total_samples = num_silence + num_samples + num_silence; // 填充信号中的样本数2.36秒+12.64秒=15秒的样本数
+ float signal[Ft8num_samples];
+ //Ft8num_sampleFT8声音的总采样数,不是字节数。15*12000
+ for (int i = 0; i < Ft8num_samples; i++)//把数据全部静音。
+ {
+ signal[i] = 0;
+ //buffer[i + num_samples + num_silence] = 0;
+ }
+
+ // 合成波形数据(信号)并将其保存为WAV文件
+ synth_gfsk(tones, FT8_NN, frequency, symbol_bt, FT8_SYMBOL_PERIOD, FT8_SAMPLE_RATE,
+ signal + num_silence);
+
+
+ for (int i = 0; i < Ft8num_samples; i++) {
+ float x = signal[i];
+ if (x > 1.0)
+ x = 1.0;
+ else if (x < -1.0)
+ x = -1.0;
+ buffer[i] = (short) (0.5 + (x * 32767.0));
+ }
+
+
+ //save_wav(signal, num_total_samples, sample_rate, wav_path);
+
+}
diff --git a/ft8CN/app/src/main/cpp/ft8Encoder.h b/ft8CN/app/src/main/cpp/ft8Encoder.h
new file mode 100644
index 0000000..b33cdac
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8Encoder.h
@@ -0,0 +1,25 @@
+//
+// Created by jmsmf on 2022/6/1.
+//
+
+#ifndef FT8CN_FT8ENCODER_H
+#define FT8CN_FT8ENCODER_H
+
+#endif //FT8CN_FT8ENCODER_H
+#include
+#include
+#include
+#include
+#include
+
+#include "common/common.h"
+//#include "common/wave.h"
+#include "common/debug.h"
+#include "ft8/pack.h"
+#include "ft8/encode.h"
+#include "ft8/constants.h"
+
+const int Ft8num_samples = 15*12000;//FT8采样数,不是字节数,16bit,字节数要乘以2
+//void generateFt8ToFile(char* message,float frequency,char* wav_path,bool is_ft4);
+void generateFt8ToBuffer(char* message,float frequency,short * buffer);
+void synth_gfsk(const uint8_t* symbols, int n_sym, float f0, float symbol_bt, float symbol_period, int signal_rate, float* signal);
\ No newline at end of file
diff --git a/ft8CN/app/src/main/cpp/ft8Listener.cpp b/ft8CN/app/src/main/cpp/ft8Listener.cpp
new file mode 100644
index 0000000..0280973
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8Listener.cpp
@@ -0,0 +1,333 @@
+//
+// Created by jmsmf on 2022/6/2.
+//
+
+#include
+#include
+
+
+extern "C" {
+#include "common/debug.h"
+#include "ft8Decoder.h"
+#include "ft8Encoder.h"
+}
+
+//
+////将char类型转换成jstring类型
+//jstring CStr2Jstring(JNIEnv *env, const char *pat) {
+// // 定义java String类 strClass
+// jclass strClass = (env)->FindClass("java/lang/String");
+// // 获取java String类方法String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
+// jmethodID ctorID = (env)->GetMethodID(strClass, "", "([BLjava/lang/String;)V");
+// // 建立byte数组
+// jbyteArray bytes = (env)->NewByteArray((jsize) strlen(pat));
+// // 将char* 转换为byte数组
+// (env)->SetByteArrayRegion(bytes, 0, (jsize) strlen(pat), (jbyte *) pat);
+// //设置String, 保存语言类型,用于byte数组转换至String时的参数
+// jstring encoding = (env)->NewStringUTF("GB2312");
+// //将byte数组转换为java String,并输出
+// return (jstring) (env)->NewObject(strClass, ctorID, bytes, encoding);
+//
+//}
+//
+//char *Jstring2CStr(JNIEnv *env, jstring jstr) {
+// char *rtn = NULL;
+// jclass clsstring = env->FindClass("java/lang/String");
+// jstring strencode = env->NewStringUTF("GB2312");
+// jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
+// jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
+// jsize alen = env->GetArrayLength(barr);
+// jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
+// if (alen > 0) {
+// rtn = (char *) malloc(alen + 1); //new char[alen+1];
+// memcpy(rtn, ba, alen);
+// rtn[alen] = 0;
+// }
+// env->ReleaseByteArrayElements(barr, ba, 0);
+//
+// return rtn;
+//}
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DecoderFt8Reset(JNIEnv *env, jobject thiz,
+ jlong decoder, jlong utcTime,
+ jint num_samples) {
+ decoder_t *dd;
+ dd = (decoder_t *) decoder;
+ decoder_ft8_reset(dd, utcTime, num_samples);
+ //dd->utcTime=utcTime;
+}
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DeleteDecoder(JNIEnv *env, jobject,
+ jlong decoder) {
+ decoder_t *dd;
+ dd = (decoder_t *) decoder;
+ delete_decoder(dd);
+}
+extern "C"
+JNIEXPORT jboolean JNICALL
+Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DecoderFt8Analysis(JNIEnv *env, jobject,
+ jint idx,
+ jlong decoder,
+ jobject ft8Message) {
+ decoder_t *dd;
+ dd = (decoder_t *) decoder;
+
+
+ ft8_message message = decoder_ft8_analysis(idx, dd);
+
+
+ jclass objectClass = env->FindClass("com/bg7yoz/ft8cn/Ft8Message");
+
+ jfieldID utcTime = env->GetFieldID(objectClass, "utcTime", "J");
+ jfieldID isValid = env->GetFieldID(objectClass, "isValid", "Z");
+ jfieldID time_sec = env->GetFieldID(objectClass, "time_sec", "F");
+ jfieldID freq_hz = env->GetFieldID(objectClass, "freq_hz", "F");
+ jfieldID score = env->GetFieldID(objectClass, "score", "I");
+ jfieldID snr = env->GetFieldID(objectClass, "snr", "I");
+ jfieldID messageHash = env->GetFieldID(objectClass, "messageHash", "I");
+ env->SetBooleanField(ft8Message, isValid, message.isValid);
+
+ jfieldID i3 = env->GetFieldID(objectClass, "i3", "I");
+ jfieldID n3 = env->GetFieldID(objectClass, "n3", "I");
+ jfieldID callsignFrom = env->GetFieldID(objectClass, "callsignFrom", "Ljava/lang/String;");
+ jfieldID callsignTo = env->GetFieldID(objectClass, "callsignTo", "Ljava/lang/String;");
+ jfieldID extraInfo = env->GetFieldID(objectClass, "extraInfo", "Ljava/lang/String;");
+ jfieldID maidenGrid = env->GetFieldID(objectClass, "maidenGrid", "Ljava/lang/String;");
+ jfieldID report = env->GetFieldID(objectClass, "report", "I");
+ jfieldID callFromHash10 = env->GetFieldID(objectClass, "callFromHash10", "J");
+ jfieldID callFromHash12 = env->GetFieldID(objectClass, "callFromHash12", "J");
+ jfieldID callFromHash22 = env->GetFieldID(objectClass, "callFromHash22", "J");
+ jfieldID callToHash10 = env->GetFieldID(objectClass, "callToHash10", "J");
+ jfieldID callToHash12 = env->GetFieldID(objectClass, "callToHash12", "J");
+ jfieldID callToHash22 = env->GetFieldID(objectClass, "callToHash22", "J");
+
+
+ if (message.isValid) {
+
+ env->SetLongField(ft8Message, utcTime, message.utcTime);
+ env->SetFloatField(ft8Message, time_sec, message.time_sec);
+ env->SetFloatField(ft8Message, freq_hz, message.freq_hz);
+ env->SetIntField(ft8Message, score, message.candidate.score);
+ env->SetIntField(ft8Message, snr, message.snr);
+ //env->SetObjectField(ft8Message,messageText,env->NewStringUTF(message.message.text));
+ env->SetIntField(ft8Message, messageHash, message.message.hash);
+
+ env->SetIntField(ft8Message, i3, message.message.i3);
+ env->SetIntField(ft8Message, n3, message.message.n3);
+ env->SetObjectField(ft8Message, callsignFrom, env->NewStringUTF(message.message.call_de));
+ env->SetObjectField(ft8Message, callsignTo, env->NewStringUTF(message.message.call_to));
+ env->SetObjectField(ft8Message, extraInfo, env->NewStringUTF(message.message.extra));
+ env->SetObjectField(ft8Message, maidenGrid, env->NewStringUTF(message.message.maidenGrid));
+ env->SetIntField(ft8Message, report, message.message.report);
+ env->SetLongField(ft8Message, callFromHash10,
+ (long long) message.message.call_de_hash.hash10);
+ env->SetLongField(ft8Message, callFromHash12,
+ (long long) message.message.call_de_hash.hash12);
+ env->SetLongField(ft8Message, callFromHash22,
+ (long long) message.message.call_de_hash.hash22);
+ env->SetLongField(ft8Message, callToHash10,
+ (long long) message.message.call_to_hash.hash10);
+ env->SetLongField(ft8Message, callToHash12,
+ (long long) message.message.call_to_hash.hash12);
+ env->SetLongField(ft8Message, callToHash22,
+ (long long) message.message.call_to_hash.hash22);
+
+ }
+ return message.isValid;
+}
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DecoderFt8FindSync(JNIEnv *env, jobject,
+ jlong decoder) {
+ decoder_t *dd;
+ dd = (decoder_t *) decoder;
+ return decoder_ft8_find_sync(dd);
+}
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DecoderMonitorPress(JNIEnv *env, jobject,
+ jintArray buffer,
+ jlong decoder) {
+ decoder_t *dd;
+ dd = (decoder_t *) decoder;
+
+
+ // jfloat *arr;
+// jint length;
+// arr = (*env).GetFloatArrayElements(buffer, NULL);
+// decoder_monitor_press(arr, dd);
+ int arr_len = env->GetArrayLength(buffer);
+ //将java数组复制到c数组中
+ auto *c_array = (jint *) malloc(arr_len * sizeof(arr_len));
+
+ //env->GetFloatArrayRegion(buffer,0,arr_len,c_array);
+ (*env).GetIntArrayRegion(buffer, 0, arr_len, c_array);
+ auto *raw_data = (float_t *) malloc(sizeof(float_t) * arr_len);
+
+ for (int i = 0; i < arr_len; i++) {
+ raw_data[i] = c_array[i] / 32768.0f;
+ }
+
+ decoder_monitor_press(raw_data, dd);
+ free(raw_data);
+ free(c_array);
+
+
+
+// jint *arr;
+// jint length;
+// arr = (*env).GetIntArrayElements(buffer, nullptr);
+// length = (*env).GetArrayLength(buffer);
+// auto *raw_data = (float_t *) malloc(sizeof(float_t) * length);
+//
+// for (int i = 0; i < length; i++) {
+// raw_data[i] = arr[i] / 32768.0f;
+// }
+//
+// decoder_monitor_press(raw_data, dd);
+//
+// free(raw_data);
+
+}
+extern "C"
+JNIEXPORT jlong JNICALL
+Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_InitDecoder(JNIEnv *env, jobject, jlong utcTime,
+ jint sampleRate, jint num_samples,
+ jboolean isFt8) {
+ return (long) init_decoder(utcTime, sampleRate, num_samples, isFt8);
+}
+
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DecoderMonitorPressFloat(JNIEnv *env,
+ jobject thiz,
+ jfloatArray buffer,
+ jlong decoder) {
+ decoder_t *dd;
+ dd = (decoder_t *) decoder;
+
+
+// jfloat *arr;
+// jint length;
+// arr = (*env).GetFloatArrayElements(buffer, NULL);
+// decoder_monitor_press(arr, dd);
+ int arr_len = env->GetArrayLength(buffer);
+ //将java数组复制到c数组中
+ auto *c_array = (jfloat *) malloc(arr_len * sizeof(arr_len));
+
+ //env->GetFloatArrayRegion(buffer,0,arr_len,c_array);
+ (*env).GetFloatArrayRegion(buffer, 0, arr_len, c_array);
+ decoder_monitor_press(c_array, dd);
+ free(c_array);
+
+}
+
+extern "C"
+JNIEXPORT jbyteArray JNICALL
+Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DecoderGetA91(JNIEnv *env, jobject thiz,
+ jlong decoder) {
+ decoder_t *dd;
+ dd = (decoder_t *) decoder;
+
+ jbyteArray array;
+ array = env->NewByteArray(FTX_LDPC_K_BYTES);
+
+ jbyte buf[FTX_LDPC_K_BYTES];
+ memcpy(buf, dd->a91, FTX_LDPC_K_BYTES);
+
+ // 使用 setIntArrayRegion 来赋值
+ env->SetByteArrayRegion(array, 0, FTX_LDPC_K_BYTES, buf);
+ return array;
+}
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_setDecodeMode(JNIEnv *env, jobject thiz,
+ jlong decoder, jboolean is_deep) {
+ decoder_t *dd;
+ dd = (decoder_t *) decoder;
+ if (is_deep) {
+ dd->kLDPC_iterations = deep_kLDPC_iterations;
+ } else {
+ dd->kLDPC_iterations = fast_kLDPC_iterations;
+ }
+}
+
+/**
+ * 把频率减去
+ * @param dd
+ * @param index
+ * @param max_block_size
+ */
+void setMagToZero(decoder_t * dd ,int index,int max_block_size){
+ if (index>0 && indexmon.wf.mag[index]=0;
+ }
+}
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ft8listener_ReBuildSignal_doSubtractSignal(JNIEnv *env, jclass clazz,
+ jlong decoder,
+ jbyteArray payload,
+ jint sample_rate,
+ jfloat frequency,
+ jfloat time_sec) {
+ decoder_t *dd;
+ dd = (decoder_t *) decoder;
+
+ int arr_len = env->GetArrayLength(payload);
+ //将java数组复制到c数组中
+ auto *c_array = (jbyte *) malloc(arr_len * sizeof(arr_len));
+
+
+ //env->GetFloatArrayRegion(buffer,0,arr_len,c_array);
+ (*env).GetByteArrayRegion(payload, 0, arr_len, c_array);
+
+ uint8_t tones[FT8_NN];// 79音调(符号)数组,
+ //此处是12个字节(91+7)/8,可以使用a91生成音频
+ ft8_encode((uint8_t *) c_array, tones);
+
+ //相当于二维数组,freq优先
+ int max_block_size=(int) (FT8_SLOT_TIME / FT8_SYMBOL_PERIOD) * kTime_osr * kFreq_osr
+ * (int) (sample_rate * FT8_SYMBOL_PERIOD / 2);
+ LOG_PRINTF("max_block_size:%d",max_block_size);
+ int block_size = FT8_SYMBOL_PERIOD * dd->mon_cfg.sample_rate;//1920,一个0.08秒的数据块大小,x轴的总长度
+ LOG_PRINTF("block_size +++:%d", block_size);
+ int freq_offset = (int) (frequency * FT8_SYMBOL_PERIOD) * kFreq_osr;//频率的偏移量,x轴
+ int time_offset = (int) ((time_sec / FT8_SYMBOL_PERIOD) * kTime_osr+0.5f);// + 0.5);,y轴
+ LOG_PRINTF("freq_offset +++:%f,%d", (frequency * FT8_SYMBOL_PERIOD) * kFreq_osr, freq_offset);
+ LOG_PRINTF("time_offset +++:%f ,%d,time_offset:%d, time_sec:%f, freq_offset:%d, freq:%f",
+ (time_sec / 0.08),
+ (int) (time_sec / 0.08 + 0.5), time_offset, time_sec, freq_offset, frequency);
+ for (int i = 0; i < FT8_NN; ++i) {//y轴自增循环
+ int index = (i + time_offset) * 2;
+ int index1 = index * block_size + freq_offset+tones[i];
+ int index2 = (index + 1) * block_size + freq_offset+tones[i];
+ int index3 =index1+1;
+ int index4 =index2+1;
+ int index5 =index1-1;
+ int index6 =index2-1;
+ int index7 =index1-2;
+ int index8 =index2-2;
+ int index9 =index1+2;
+ int index10 =index2+2;
+
+ setMagToZero(dd,index1,max_block_size);
+ setMagToZero(dd,index2,max_block_size);
+ setMagToZero(dd,index3,max_block_size);
+ setMagToZero(dd,index4,max_block_size);
+ setMagToZero(dd,index5,max_block_size);
+ setMagToZero(dd,index6,max_block_size);
+ setMagToZero(dd,index7,max_block_size);
+ setMagToZero(dd,index8,max_block_size);
+ setMagToZero(dd,index9,max_block_size);
+ setMagToZero(dd,index10,max_block_size);
+
+ }
+ free(c_array);
+
+}
\ No newline at end of file
diff --git a/ft8CN/app/src/main/cpp/ft8Spectrum.cpp b/ft8CN/app/src/main/cpp/ft8Spectrum.cpp
new file mode 100644
index 0000000..206d334
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/ft8Spectrum.cpp
@@ -0,0 +1,293 @@
+#include
+
+//
+// Created by jmsmf on 2022/6/11.
+//
+
+extern "C" {
+#include "common/debug.h"
+#include "spectrum_data.h"
+}
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ui_SpectrumFragment_getFFTData(JNIEnv *env, jobject thiz, jintArray data,
+ jintArray fft_data) {
+
+ int arr_len=env->GetArrayLength(data);
+ //将java数组复制到c数组中
+ auto c_array=(jint *) malloc(arr_len * sizeof(arr_len));
+
+ env->GetIntArrayRegion(data,0,arr_len,c_array);
+ auto *raw_data = (float *) malloc(sizeof(float) * arr_len);
+ for (int i = 0; i < arr_len; i++) {
+ raw_data[i] = c_array[i] / 32768.0f;
+ }
+
+
+ jint temp[arr_len/2];
+
+ do_fftr(raw_data,arr_len,temp);
+ (*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
+
+ free(c_array);
+ free(raw_data);
+
+//
+// jint *arr;
+// jint length;
+// arr = (*env).GetIntArrayElements(data, NULL);
+// length = (*env).GetArrayLength(data);
+// float *raw_data = (float *) malloc(sizeof(float) * length);
+// for (int i = 0; i < length; i++) {
+// raw_data[i] = arr[i] / 32768.0f;
+// }
+//
+// jint temp[length/2];
+//
+// do_fftr(raw_data,length,temp);
+//
+// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
+// free(raw_data);
+}
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ui_SpectrumFragment_getFFTDataRaw(JNIEnv *env, jobject thiz, jintArray data,
+ jintArray fft_data) {
+
+ int arr_len=env->GetArrayLength(data);
+ //将java数组复制到c数组中
+ auto c_array=(jint *) malloc(arr_len * sizeof(arr_len));
+
+ env->GetIntArrayRegion(data,0,arr_len,c_array);
+ auto *raw_data = (float *) malloc(sizeof(float) * arr_len);
+ for (int i = 0; i < arr_len; i++) {
+ raw_data[i] = c_array[i] / 32768.0f;
+ }
+
+
+ jint temp[arr_len/2];
+
+ do_fftr_raw(raw_data,arr_len,temp);
+ (*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
+
+ free(c_array);
+ free(raw_data);
+
+
+//
+// jint *arr;
+// jint length;
+// arr = (*env).GetIntArrayElements(data, NULL);
+// length = (*env).GetArrayLength(data);
+// float *raw_data = (float *) malloc(sizeof(float) * length);
+// for (int i = 0; i < length; i++) {
+// raw_data[i] = arr[i] / 32768.0f;
+// }
+//
+// jint temp[length/2];
+//
+// do_fftr_raw(raw_data,length,temp);
+//
+// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
+// free(raw_data);
+}
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ui_SpectrumView_getFFTData(JNIEnv *env, jobject thiz, jintArray data,
+ jintArray fft_data) {
+ int arr_len=env->GetArrayLength(data);
+ //将java数组复制到c数组中
+ auto c_array=(jint *) malloc(arr_len * sizeof(arr_len));
+
+ env->GetIntArrayRegion(data,0,arr_len,c_array);
+ auto *raw_data = (float *) malloc(sizeof(float) * arr_len);
+ for (int i = 0; i < arr_len; i++) {
+ raw_data[i] = c_array[i] / 32768.0f;
+ }
+
+
+ jint temp[arr_len/2];
+
+ do_fftr(raw_data,arr_len,temp);
+ (*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
+
+ free(c_array);
+ free(raw_data);
+
+
+// jint *arr;
+// jint length;
+// arr = (*env).GetIntArrayElements(data, NULL);
+// length = (*env).GetArrayLength(data);
+// float *raw_data = (float *) malloc(sizeof(float) * length);
+// for (int i = 0; i < length; i++) {
+// raw_data[i] = arr[i] / 32768.0f;
+// }
+//
+// jint temp[length/2];
+//
+// do_fftr(raw_data,length,temp);
+//
+// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
+// free(raw_data);
+}
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ui_SpectrumView_getFFTDataRaw(JNIEnv *env, jobject thiz, jintArray data,
+ jintArray fft_data) {
+ int arr_len=env->GetArrayLength(data);
+ //将java数组复制到c数组中
+ auto c_array=(jint *) malloc(arr_len * sizeof(arr_len));
+
+ env->GetIntArrayRegion(data,0,arr_len,c_array);
+ auto *raw_data = (float *) malloc(sizeof(float) * arr_len);
+ for (int i = 0; i < arr_len; i++) {
+ raw_data[i] = c_array[i] / 32768.0f;
+ }
+
+
+ jint temp[arr_len/2];
+
+ do_fftr_raw(raw_data,arr_len,temp);
+ (*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
+
+ free(c_array);
+ free(raw_data);
+
+// jint *arr;
+// jint length;
+// arr = (*env).GetIntArrayElements(data, NULL);
+// length = (*env).GetArrayLength(data);
+// float *raw_data = (float *) malloc(sizeof(float) * length);
+// for (int i = 0; i < length; i++) {
+// raw_data[i] = arr[i] / 32768.0f;
+// }
+//
+// jint temp[length/2];
+// //jint *fftdata;
+// //fftdata=(*env).GetIntArrayElements(fft_data, NULL);
+//
+// do_fftr_raw(raw_data,length,temp);
+//
+// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
+// free(raw_data);
+}
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ui_SpectrumFragment_getFFTDataFloat(JNIEnv *env, jobject thiz,
+ jfloatArray data, jintArray fft_data) {
+
+
+ int arr_len=env->GetArrayLength(data);
+ //将java数组复制到c数组中
+ auto *c_array=(jfloat *) malloc(arr_len * sizeof(arr_len));
+
+ env->GetFloatArrayRegion(data,0,arr_len,c_array);
+ jint temp[arr_len/2];
+
+ do_fftr(c_array,arr_len,temp);
+ (*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
+
+ free(c_array);
+
+
+
+// jfloat *arr;
+// jint length;
+// arr = (*env).GetFloatArrayElements(data, NULL);
+// length = (*env).GetArrayLength(data);
+//
+// jint temp[length/2];
+//
+// do_fftr(arr,length,temp);
+//
+// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
+}
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ui_SpectrumFragment_getFFTDataRawFloat(JNIEnv *env, jobject thiz,
+ jfloatArray data, jintArray fft_data) {
+
+ int arr_len=env->GetArrayLength(data);
+ //将java数组复制到c数组中
+ auto *c_array=(jfloat *) malloc(arr_len * sizeof(arr_len));
+
+ env->GetFloatArrayRegion(data,0,arr_len,c_array);
+ jint temp[arr_len/2];
+
+ do_fftr_raw(c_array,arr_len,temp);
+ (*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
+
+ free(c_array);
+
+
+
+// jfloat *arr;
+// jint length;
+// arr = (*env).GetFloatArrayElements(data, NULL);
+// length = (*env).GetArrayLength(data);
+//
+// jint temp[length/2];
+// do_fftr_raw(arr,length,temp);
+
+// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
+}
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ui_SpectrumView_getFFTDataFloat(JNIEnv *env, jobject thiz, jfloatArray data,
+ jintArray fft_data) {
+
+ int arr_len=env->GetArrayLength(data);
+ //将java数组复制到c数组中
+ auto *c_array=(jfloat *) malloc(arr_len * sizeof(arr_len));
+
+ //env->GetFloatArrayRegion(data,0,arr_len,c_array);
+ (*env).GetFloatArrayRegion(data,0,arr_len,c_array);
+ jint temp[arr_len/2];
+
+ do_fftr(c_array,arr_len,temp);
+ (*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
+ free(c_array);
+
+// jfloat *arr;
+// jint length;
+// arr = (*env).GetFloatArrayElements(data, NULL);
+// length = (*env).GetArrayLength(data);
+//
+// jint temp[length/2];
+//
+// do_fftr(arr,length,temp);
+//
+// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
+}
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ui_SpectrumView_getFFTDataRawFloat(JNIEnv *env, jobject thiz,
+ jfloatArray data, jintArray fft_data) {
+
+
+ int arr_len=env->GetArrayLength(data);
+ //将java数组复制到c数组中
+ auto *c_array=(jfloat *) malloc(arr_len * sizeof(arr_len));
+
+ //env->GetFloatArrayRegion(data,0,arr_len,c_array);
+ (*env).GetFloatArrayRegion(data,0,arr_len,c_array);
+ jint temp[arr_len/2];
+
+ do_fftr_raw(c_array,arr_len,temp);
+ (*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
+
+ free(c_array);
+
+// jfloat *arr;
+// jint length;
+// arr = (*env).GetFloatArrayElements(data, NULL);
+// length = (*env).GetArrayLength(data);
+//
+// jint temp[length/2];
+//
+// do_fftr_raw(arr,length,temp);
+//
+// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
+}
diff --git a/ft8CN/app/src/main/cpp/generate_ft8.cpp b/ft8CN/app/src/main/cpp/generate_ft8.cpp
new file mode 100644
index 0000000..7d460f1
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/generate_ft8.cpp
@@ -0,0 +1,148 @@
+//
+// Created by jmsmf on 2022/6/1.
+//
+#include
+#include
+
+extern "C" {
+#include "common/debug.h"
+#include "ft8Encoder.h"
+#include "ft8/pack.h"
+#include "ft8/encode.h"
+#include "ft8/hash22.h"
+}
+#define GFSK_CONST_K 5.336446f ///< == pi * sqrt(2 / log(2))
+
+char *Jstring2CStr(JNIEnv *env, jstring jstr) {
+ char *rtn = nullptr;
+ jclass clsstring = env->FindClass("java/lang/String");
+ jstring strencode = env->NewStringUTF("GB2312");
+ jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
+ auto barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
+ int alen = env->GetArrayLength(barr);
+ jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
+ if (alen > 0) {
+ rtn = (char *) malloc(alen + 1); //new char[alen+1];
+ memcpy(rtn, ba, alen);
+ rtn[alen] = 0;
+ }
+ env->ReleaseByteArrayElements(barr, ba, 0);
+
+ return rtn;
+}
+
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ft8transmit_FT8TransmitSignal_GenerateFt8(JNIEnv *env, jobject,
+ jstring message,
+ jfloat frequency,
+ jshortArray buffer) {
+ jshort *_buffer;
+ _buffer = (*env).GetShortArrayElements(buffer, nullptr);
+ char *str=Jstring2CStr(env, message);
+ generateFt8ToBuffer(str, frequency, _buffer);
+ (*env).ReleaseShortArrayElements(buffer,_buffer,JNI_COMMIT);
+ free(str);
+}
+
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_bg7yoz_ft8cn_ft8transmit_GenerateFT8_pack77(JNIEnv *env, jclass, jstring msg,
+ jbyteArray c77) {
+ jbyte *_buffer;
+ _buffer = (*env).GetByteArrayElements(c77, nullptr);
+ char *str=Jstring2CStr(env, msg);
+ int result=pack77(str,(uint8_t *)_buffer);
+ (*env).ReleaseByteArrayElements(c77,_buffer,JNI_COMMIT);
+ free(str);
+ return result;
+}
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ft8transmit_GenerateFT8_ft8_1encode(JNIEnv *env, jclass clazz,
+ jbyteArray payload, jbyteArray tones) {
+ jbyte *_payload;
+ jbyte *_tones;
+ _payload = (*env).GetByteArrayElements(payload, nullptr);
+ _tones = (*env).GetByteArrayElements(tones, nullptr);
+ ft8_encode((uint8_t *)_payload,(uint8_t *)_tones);
+ (*env).ReleaseByteArrayElements(payload,_payload,JNI_COMMIT);
+ (*env).ReleaseByteArrayElements(tones,_tones,JNI_COMMIT);
+
+
+}
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ft8transmit_GenerateFT8_gfsk_1pulse(JNIEnv *env, jclass clazz, jint n_spsym,
+ jfloat symbol_bt, jfloatArray pulse) {
+ jfloat *_pulse;
+ _pulse=(*env).GetFloatArrayElements(pulse, nullptr);
+
+ for (int i = 0; i < 3 * n_spsym; ++i)
+ {
+ float t = i / (float)n_spsym - 1.5f;
+ float arg1 = GFSK_CONST_K * symbol_bt * (t + 0.5f);
+ float arg2 = GFSK_CONST_K * symbol_bt * (t - 0.5f);
+ _pulse[i] = (erff(arg1) - erff(arg2)) / 2;
+ }
+ (*env).ReleaseFloatArrayElements(pulse,_pulse,JNI_COMMIT);
+
+}
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_bg7yoz_ft8cn_ft8transmit_GenerateFT8_synth_1gfsk(JNIEnv *env, jclass clazz,
+ jbyteArray symbols, jint n_sym, jfloat f0,
+ jfloat symbol_bt, jfloat symbol_period,
+ jint signal_rate, jfloatArray signal,
+ jint offset) {
+ jbyte *_symbols;
+ jfloat *_signal;
+ _symbols = (*env).GetByteArrayElements(symbols, nullptr);
+ _signal = (*env).GetFloatArrayElements(signal, nullptr);
+ synth_gfsk((uint8_t *)_symbols,n_sym,f0,symbol_bt,symbol_period,signal_rate,_signal+offset);
+
+ (*env).ReleaseByteArrayElements(symbols,_symbols,JNI_COMMIT);
+ (*env).ReleaseFloatArrayElements(signal,_signal,JNI_COMMIT);
+
+}
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_bg7yoz_ft8cn_ft8signal_FT8Package_getHash12(JNIEnv *env, jclass clazz, jstring callsign) {
+ char *str=Jstring2CStr(env, callsign);
+ uint32_t hash=hashcall_12(str);
+ free(str);
+ return hash;
+}
+extern "C"
+
+
+JNIEXPORT jint JNICALL
+Java_com_bg7yoz_ft8cn_ft8transmit_GenerateFT8_packFreeTextTo77(JNIEnv *env, jclass clazz,
+ jstring msg, jbyteArray c77) {
+
+ jbyte *_buffer;
+ _buffer = (*env).GetByteArrayElements(c77, nullptr);
+ char *str=Jstring2CStr(env, msg);
+ packtext77(str,(uint8_t *)_buffer);
+ (*env).ReleaseByteArrayElements(c77,_buffer,JNI_COMMIT);
+ free(str);
+ return 0;
+}
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_bg7yoz_ft8cn_ft8signal_FT8Package_getHash10(JNIEnv *env, jclass clazz, jstring callsign) {
+ char *str=Jstring2CStr(env, callsign);
+ uint32_t hash=(hashcall_10(str));
+ free(str);
+ return hash;
+}
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_bg7yoz_ft8cn_ft8signal_FT8Package_getHash22(JNIEnv *env, jclass clazz, jstring callsign) {
+ char *str=Jstring2CStr(env, callsign);
+ u_int32_t hash=hashcall_22(str);
+ free(str);
+ return hash;
+}
\ No newline at end of file
diff --git a/ft8CN/app/src/main/cpp/monitor_opr.c b/ft8CN/app/src/main/cpp/monitor_opr.c
new file mode 100644
index 0000000..8cdaaae
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/monitor_opr.c
@@ -0,0 +1,257 @@
+//
+// Created by jmsmf on 2022/4/22.
+//
+
+#include "monitor_opr.h"
+#include "ft8/constants.h"
+//#define LOG_LEVEL LOG_DEBUG
+#define LOG_LEVEL LOG_FATAL
+
+
+// FFT转换,采用加窗函数,减少频谱泄露的问题。
+// Hanning窗(汉宁窗)适用于95%的情况。
+static float hann_i(int i, int N) {
+ float x = sinf((float) M_PI * i / N);
+ return x * x;
+}
+
+static float hamming_i(int i, int N) {
+ const float a0 = (float) 25 / 46;
+ const float a1 = 1 - a0;
+
+ float x1 = cosf(2 * (float) M_PI * i / N);
+ return a0 - a1 * x1;
+}
+
+static float blackman_i(int i, int N) {
+ const float alpha = 0.16f; // or 2860/18608
+ const float a0 = (1 - alpha) / 2;
+ const float a1 = 1.0f / 2;
+ const float a2 = alpha / 2;
+
+ float x1 = cosf(2 * (float) M_PI * i / N);
+ float x2 = 2 * x1 * x1 - 1; // Use double angle formula
+
+ return a0 - a1 * x1 + a2 * x2;
+}
+
+static void
+waterfall_init(waterfall_t *me, int max_blocks, int num_bins, int time_osr, int freq_osr) {
+ //mag_size,信号量数组的大小。最大块数93*时间过采样率2*频率过采样率2*分析块960*sizeOf(U_int8)
+ size_t mag_size = max_blocks * time_osr * freq_osr * num_bins * sizeof(me->mag[0]);
+ me->max_blocks = max_blocks;
+ me->num_blocks = 0;
+ me->num_bins = num_bins;//num_bins = 12000 * 0.16 / 2 = 960
+ me->time_osr = time_osr;
+ me->freq_osr = freq_osr;
+ me->block_stride = (time_osr * freq_osr * num_bins);
+ me->mag = (uint8_t *) malloc(mag_size);//申请信号量数组,用于计算得分,357120个
+ me->mag2 = (float *) malloc(mag_size * sizeof(float));//申请信号量数组,用于计算信噪比
+ LOG(LOG_DEBUG, "Waterfall size = %zu\n", mag_size);
+}
+
+static void waterfall_free(waterfall_t *me) {
+ free(me->mag);
+ free(me->mag2);
+}
+
+
+void monitor_init(monitor_t *me, const monitor_config_t *cfg) {
+ LOG(LOG_DEBUG, "Monitor is initializing...");
+ //协议的时长,FT8_SLOT_TIME=15.0f,FT4_SLOT_TIME=7.5f
+ float slot_time = (cfg->protocol == PROTO_FT4) ? FT4_SLOT_TIME : FT8_SLOT_TIME;
+ //协议每个符号的时长,FT8_SYMBOL_PERIOD=0.160f,FT4_SYMBOL_PERIOD=0.048f
+ float symbol_period = (cfg->protocol == PROTO_FT4) ? FT4_SYMBOL_PERIOD : FT8_SYMBOL_PERIOD;
+
+ //**************************************************
+ // Compute DSP parameters that depend on the sample rate
+ // 根据采样率计算DSP参数
+ // block_size:每一个FSK符号占用的样本数,FT8:12000*0.16=1920个
+ // subblock_size:分析移动的大小(样本数),每符号样本数/时间过采样率。FT8:1920/2=960
+ // nfft:fft size。fft大小=每个FSK符号占用的样本数*频率过采样率=1920*2
+ // fft_norm:FFT归一化因子。2/fft size。
+ me->block_size = (int) (cfg->sample_rate *
+ symbol_period); //12000*0.16 对应于一个FSK符号的样本=1920
+ me->subblock_size = me->block_size / cfg->time_osr;//移动的样本数。一个FSK符号的样本数/过采样率
+ //nfft是傅里叶变换前,时域的实数序列的数量。目前是一个FSK符号的样本数*频率过采样率
+ me->nfft = me->block_size * cfg->freq_osr;//nfft=一个FSK符号的样本数*频率过采样率
+ me->fft_norm = 2.0f / me->nfft;//< FFT归一化因子。FFT normalization factor
+ // const int len_window = 1.8f * me->block_size; // hand-picked and optimized
+ //**************************************************
+
+ // 申请窗空间,大小是fft块大小*me->windows[0]的大小
+ // 采集周期如果是实际信号的非整数时,端点是不连续的。
+ // 这些不连续片段在FFT中显示为高频成分。这些高频成分不存在于原信号中。
+ // 这些频率可能远高于奈奎斯特频率,在0~ 采样率的一半的频率区间内产生混叠。
+ // 使用FFT获得的频率,不是原信号的实际频率,而是一个改变过的频率。
+ // 类似于某个频率的能量泄漏至其他频率。 这种现象叫做频谱泄漏。
+ // 频率泄漏使好的频谱线扩散到更宽的信号范围中。这些不连续片段在FFT中显示为高频成分。
+ // 通过加窗来尽可能减少在非整数个周期上进行FFT产生的误差。
+ // 加窗可减少这些不连续部分的幅值。
+ // 加窗包括将时间记录乘以有限长度的窗,窗的幅值逐渐变小,在边沿处为0。
+ // 加窗的结果是尽可能呈现出一个连续的波形,减少剧烈的变化。 这种方法也叫应用一个加窗。
+ // 窗函数分很多种,常见的的如:Hamming窗、Hanning窗、Blackman-Harris窗、Kaiser-Bessel窗、Flat top窗。
+ // Hanning窗适用于95%的情况。
+ me->window = (float *) malloc(
+ me->nfft * sizeof(me->window[0]));// 申请窗空间,大小是fft块大小*sizeof(me->windows[0])
+ //此处是窗函数的设置,使用常用的hanning窗
+ for (int i = 0; i < me->nfft; ++i) {
+ // window[i] = 1;
+ me->window[i] = hann_i(i, me->nfft);// 使用Hanning窗
+ // me->window[i] = blackman_i(i, me->nfft);// Blackman-Harris窗
+ // me->window[i] = hamming_i(i, me->nfft);// Hamming窗
+ // me->window[i] = (i < len_window) ? hann_i(i, len_window) : 0;
+ }
+
+
+ // 申请当前STFT(短时傅氏变换)分析框架(nfft样本)的空间。
+ //last_frame:申请傅里叶变换分析框架用的(nfft样本)。
+ // 空间大小=时域数据量*数据类型占用的空间=一个FSK符号占用的采样数据量*频率过采样率=12000*0.16*2*SizeOf(float)
+ me->last_frame = (float *) malloc(me->nfft * sizeof(me->last_frame[0]));
+
+ // size_t 类型定义在cstddef头文件中,该文件是C标准库的头文件stddef.h的C++版。
+ // 它是一个与机器相关的unsigned类型,其大小足以保证存储内存中对象的大小。
+ size_t fft_work_size;
+ // 第一步,获取可以用的FFT工作区域的大小到fft_work_size
+ //nfft=一个FSK符号的样本数*频率过采样率=0.16*12000*2=3840
+ kiss_fftr_alloc(me->nfft, 0, 0, &fft_work_size);
+ //fft_work_size
+
+
+ // 申请FFT工作区域的内存,38676个
+ me->fft_work = malloc(fft_work_size);
+ //第二步,返回fft的设置信息
+ me->fft_cfg = kiss_fftr_alloc(me->nfft, 0, me->fft_work, &fft_work_size);
+
+ // 最大块数,FT8的周期时长/每个符号时长,FT8:15/0.16 =93
+ const int max_blocks = (int) (slot_time / symbol_period);
+
+ // num_bins:如果以6.25 Hz为单位的FFT箱数量。6.25:每秒6.25个FSK符号,0.16*6.25=1。
+ // num_bins = 12000 * 0.16 / 2 = 960。2是啥?是生成的频域数据量是采集数据量的一半
+ const int num_bins = (int) (cfg->sample_rate * symbol_period / 2);
+
+ //初始化瀑布图。
+ waterfall_init(&me->wf, max_blocks, num_bins, cfg->time_osr, cfg->freq_osr);
+ me->wf.protocol = cfg->protocol;
+ me->symbol_period = symbol_period;
+
+ me->max_mag = -120.0f;
+
+
+ LOG(LOG_INFO, "Block size = %d\n", me->block_size);
+ LOG(LOG_INFO, "Subblock size = %d\n", me->subblock_size);
+ LOG(LOG_INFO, "N_FFT = %d\n", me->nfft);
+ LOG(LOG_DEBUG, "FFT work area = %zu\n", fft_work_size);
+ // 瀑布图中最多能申请max_blocks个块数
+ LOG(LOG_DEBUG, "Waterfall allocated %d symbols\n", me->wf.max_blocks);
+}
+
+void monitor_free(monitor_t *me) {
+
+ waterfall_free(&me->wf);
+ free(me->fft_work);
+ free(me->last_frame);
+ free(me->window);
+ LOG(LOG_DEBUG, "Monitor is free .");
+}
+
+
+// Compute FFT magnitudes (log wf) for a frame in the signal and update waterfall data
+// 计算信号中一帧的FFT幅度(log wf),并更新瀑布数据
+void monitor_process(monitor_t *me, const float *frame) {
+ // Check if we can still store more waterfall data
+ //防止溢出
+ //mag阵列中存储的块(符号)编号
+ if (me->wf.num_blocks >= me->wf.max_blocks)
+ return;
+
+ //num_bins 的值是 12000 * 0.16 / 2 = 960
+ //wf.block_strid= (time_osr * freq_osr * num_bins)=2*2*960
+ //offset是在mag数组中的偏移量。wf.num_blocks是当前符号的块编号,以num_bins(数据片段)*时间过采样*频率过采样为单位。
+ //mag的数组大小实际上是时间过采样*频率过采样*符号的最大量(93)*每符号真实采样数据(过采样的数据,960).
+ int offset = me->wf.num_blocks * me->wf.block_stride;
+ int frame_pos = 0;
+
+ // Loop over block subdivisions
+ //循环块细分,wf.time_osr=2 时间过采样率
+ for (int time_sub = 0; time_sub < me->wf.time_osr; ++time_sub) {
+ kiss_fft_scalar timedata[me->nfft];
+ kiss_fft_cpx freqdata[me->nfft / 2 + 1];
+
+ // Shift the new data into analysis frame
+ //将新数据转移到分析框架中。
+ // subblock_size:分析移动的大小(样本数)blocksize/2 每秒块数/时间过采样率=FT8:12000*0.16/2=1920/2=960个
+ //last_frame的空间已经申请好了。空间大小=时域数据量*数据类型占用的空间=一个FSK符号占用的采样数据量*频率过采样率=12000*0.16*2*SizeOf(float)=3840
+ //nfft=一个FSK符号的样本数*频率过采样率=1920*2=3840
+ //subblock_size。移动的样本数。一个FSK符号的样本数/过采样率,1920/2=960
+ //第一个循环,把过采样的后半段数据向前移960个数据,
+ //第二个循环,把新的声音数据导入到last_frame的后半部分,新的声音数据960个。外面的时间过采样率循环2遍,正好960*2=1920,一个符号
+ //这样就可以对一个符号周期的时域数据做傅里叶变换了
+ for (int pos = 0; pos < me->nfft - me->subblock_size; ++pos) {
+ me->last_frame[pos] = me->last_frame[pos + me->subblock_size];
+ }
+ for (int pos = me->nfft - me->subblock_size; pos < me->nfft; ++pos) {
+ me->last_frame[pos] = frame[frame_pos];
+ ++frame_pos;
+ }
+
+
+ // Compute windowed analysis frame
+ //用窗函数做一次转换,汉宁窗。
+ for (int pos = 0; pos < me->nfft; ++pos) {
+ //把last_frame中的数据赋值到timedata中来,timedata是时域数据,要做一次归一化、窗函数处理
+ timedata[pos] = me->fft_norm * me->window[pos] * me->last_frame[pos];
+ //timedata[pos] =me->window[pos] * me->last_frame[pos];
+ }
+
+ //傅里叶变换把timedata的时域数据(长度是nfft)转换到频域数据上来。频域数据是复数数组,数组长度是nfft/2+1
+ //nfft=一个FSK符号的样本数*频率过采样率=12000*0.16*2=3840
+ kiss_fftr(me->fft_cfg, timedata, freqdata);
+
+ // Loop over two possible frequency bin offsets (for averaging)
+ //在两个可能的频率单元偏移上循环(用于平均)
+ //两个循环的意义是:在一个符号采样数据的范围内(12000*0.16)对freqdata的能量做计算
+ for (int freq_sub = 0; freq_sub < me->wf.freq_osr; ++freq_sub) {
+ for (int bin = 0; bin < me->wf.num_bins; ++bin) { //num_bins 的值是 12000 * 0.16 / 2 = 960
+
+ //循环次数=2*960=1920
+ //信号量位置src_bin,
+ int src_bin = (bin * me->wf.freq_osr) + freq_sub;
+
+ ////此位置可能是计算信噪比的位置
+ //求各频率点上的信号强度是傅里叶之后的平方?少了开方,mag2应当是信号量的平方
+ float mag2 = (freqdata[src_bin].i * freqdata[src_bin].i) +
+ (freqdata[src_bin].r * freqdata[src_bin].r);
+ float db = 10.0f * log10f(1E-12f + mag2);
+
+ //把信号量保存下来
+ //offset=me->wf.num_blocks * me->wf.block_stride;
+ //wf.block_strid= (time_osr * freq_osr * num_bins)=2*2*960
+ //偏移量就是当前块编号*每个符号ftt数据量
+
+ me->wf.mag2[offset] = mag2;
+
+ //每循环一次,偏移量移一位。共移动time_osr*freq_osr*num_bins=2*2*960=3840
+
+ // Scale decibels to unsigned 8-bit range and clamp the value
+ //将分贝缩放到无符号8位范围,并钳制该值
+ // Range 0-240 covers -120..0 dB in 0.5 dB steps
+ int scaled = (int) (2 * db + 240);
+
+ //0~255之间
+ me->wf.mag[offset] = (scaled < 0) ? 0 : ((scaled > 255) ? 255 : scaled);
+ ++offset;
+
+ if (db > me->max_mag)
+ me->max_mag = db;
+ }
+ }
+ }
+ ++me->wf.num_blocks;//mag阵列中存储的块(符号)编号,块大小:2*2*960
+}
+
+void monitor_reset(monitor_t *me) {
+ LOG(LOG_DEBUG, "Monitor is resetting...");
+ me->wf.num_blocks = 0;
+ me->max_mag = -120.0f;
+}
diff --git a/ft8CN/app/src/main/cpp/monitor_opr.h b/ft8CN/app/src/main/cpp/monitor_opr.h
new file mode 100644
index 0000000..dd7cc61
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/monitor_opr.h
@@ -0,0 +1,47 @@
+
+#include
+#include
+#include
+#include
+#include
+#include "ft8/decode.h"
+#include "fft/kiss_fftr.h"
+#include "common/debug.h"
+
+#define LOG_LEVEL LOG_INFO
+
+
+
+/// Configuration options for FT4/FT8 monitor
+typedef struct {
+ float f_min; ///< 最低频率界限,Lower frequency bound for analysis
+ float f_max; ///< 最高频率界限,Upper frequency bound for analysis
+ int sample_rate; ///< 采样率,Sample rate in Hertz
+ int time_osr; ///< 时间过采样率,Number of time subdivisions
+ int freq_osr; ///< 频率过采样率,Number of frequency subdivisions
+ ftx_protocol_t protocol; ///< Protocol: FT4 or FT8
+} monitor_config_t;
+
+/// FT4/FT8 monitor object that manages DSP processing of incoming audio data
+/// and prepares a waterfall object
+typedef struct {
+ float symbol_period; ///< FT4/FT8符号周期(秒)。FT4/FT8 symbol period in seconds
+ int block_size; ///< 每个符号(FSK)的样本数。Number of samples per symbol (block)
+ int subblock_size; ///< 分析移动的大小(样本数)。Analysis shift size (number of samples)
+ int nfft; ///< FFT size
+ float fft_norm; ///< FFT归一化因子。FFT normalization factor
+ float *window; ///< STFT分析的窗口函数(nfft样本)。Window function for STFT analysis (nfft samples)
+ float *last_frame; ///< 当前STFT分析框架(nfft样本)。Current STFT analysis frame (nfft samples)
+ waterfall_t wf; ///< 瀑布对象。Waterfall object
+ float max_mag; ///< 最大检测量(调试统计)。Maximum detected magnitude (debug stats)
+
+ // KISS FFT housekeeping variables
+ void *fft_work; ///< FFT需要的工作区域。Work area required by Kiss FFT
+ kiss_fftr_cfg fft_cfg; ///< Kiss FFT需要的设置信息。Kiss FFT housekeeping object
+} monitor_t;
+
+
+void monitor_init(monitor_t *me, const monitor_config_t *cfg);
+void monitor_free(monitor_t* me);
+void monitor_process(monitor_t *me, const float *frame);
+void monitor_reset(monitor_t *me);
diff --git a/ft8CN/app/src/main/cpp/spectrum_data.c b/ft8CN/app/src/main/cpp/spectrum_data.c
new file mode 100644
index 0000000..a608ff0
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/spectrum_data.c
@@ -0,0 +1,125 @@
+#include
+#include "spectrum_data.h"
+
+static float hann_i(int i, int N) {
+ float x = sinf((float) M_PI * i / N);
+ return x * x;
+}
+
+void do_fftr(float *voiceData, int dataSize, int *fftData) {
+// int block_size = FT8_SYMBOL_PERIOD * 12000; //=1920
+ int fftSize = FT8_SYMBOL_PERIOD * 12000; //=1920
+ float *window = (float *) malloc(
+ fftSize * sizeof(window[0])); // 申请窗空间,大小是fft块大小*sizeof(windows[0])
+ for (int i = 0; i < fftSize; ++i) //汉宁窗
+ {
+ window[i] = hann_i(i, fftSize);
+ }
+ // 申请当前STFT分析框架(nfft样本)的空间。
+ //float *last_frame = (float *) malloc(fftSize * sizeof(last_frame[0]));
+
+ size_t fft_work_size;
+
+ // 获取FFT工作区域的大小到fft_work_size
+ kiss_fftr_alloc(fftSize, 0, 0, &fft_work_size);
+
+ // 申请FFT工作区域的内存
+ void *fft_work = malloc(fft_work_size);
+ kiss_fftr_cfg fft_cfg = kiss_fftr_alloc(fftSize, 0, fft_work, &fft_work_size);
+
+ // 最大块数,FT8的周期时长/每个符号时长,FT8:15/0.16 =93
+ const int max_blocks = (int) (FT8_SLOT_TIME / FT8_SYMBOL_PERIOD);
+ const int num_bins = (int) (12000 * FT8_SYMBOL_PERIOD / 2);
+ int fftOffset = 0;
+ int offset = 0;
+ float maxMag = 0;
+ float minMag = 65535.0f;
+ float mags[dataSize / 2];
+ for (int pos = 0; pos < dataSize / fftSize; pos++) {
+ kiss_fft_scalar timedata[fftSize];
+ kiss_fft_cpx freqdata[fftSize / 2 + 1];
+ for (int i = 0; i < fftSize; i++) {//fftSize=3840
+ timedata[i] = window[i] * voiceData[offset];//window[i] *
+ offset++;
+ }
+ kiss_fftr(fft_cfg, timedata, freqdata);
+
+ for (int i = 1; i < fftSize / 2 + 1; i++) {
+ float mag2 = sqrtf(freqdata[i].i * freqdata[i].i + freqdata[i].r * freqdata[i].r);
+ mags[fftOffset] = mag2;
+ if (maxMag < mag2) {
+ maxMag = mag2;
+ }
+ if (minMag > mag2) {
+ minMag = mag2;
+ }
+ fftOffset++;
+ }
+
+ float normal = (maxMag - minMag) / 256;
+ for (int i = 0; i < dataSize / 2; ++i) {
+ fftData[i] = roundf((mags[i] - minMag) / normal);
+ }
+ }
+ free(fft_work);
+ free(window);
+ //free(last_frame);
+ //free(fft_cfg);
+}
+
+void do_fftr_raw(float *voiceData, int dataSize, int *fftData) {
+ //int block_size = FT8_SYMBOL_PERIOD * 12000; //=1920
+ int fftSize = FT8_SYMBOL_PERIOD * 12000; //=1920
+ float *window = (float *) malloc(
+ fftSize * sizeof(window[0])); // 申请窗空间,大小是fft块大小*sizeof(windows[0])
+ for (int i = 0; i < fftSize; ++i) //汉宁窗
+ {
+ window[i] = hann_i(i, fftSize);
+ }
+ // 申请当前STFT分析框架(nfft样本)的空间。
+ //float *last_frame = (float *) malloc(fftSize * sizeof(last_frame[0]));
+
+ size_t fft_work_size;
+
+ // 获取FFT工作区域的大小到fft_work_size
+ kiss_fftr_alloc(fftSize, 0, 0, &fft_work_size);
+
+ // 申请FFT工作区域的内存
+ void *fft_work = malloc(fft_work_size);
+ kiss_fftr_cfg fft_cfg = kiss_fftr_alloc(fftSize, 0, fft_work, &fft_work_size);
+
+ // 最大块数,FT8的周期时长/每个符号时长,FT8:15/0.16 =93
+ const int max_blocks = (int) (FT8_SLOT_TIME / FT8_SYMBOL_PERIOD);
+ const int num_bins = (int) (12000 * FT8_SYMBOL_PERIOD / 2);
+ int fftOffset = 0;
+ int offset = 0;
+
+ //float mags[dataSize / 2];
+ for (int pos = 0; pos < dataSize / fftSize; pos++) {
+ kiss_fft_scalar timedata[fftSize];
+ kiss_fft_cpx freqdata[fftSize / 2 + 1];
+ for (int i = 0; i < fftSize; i++) {//fftSize=3840
+ timedata[i] = window[i] * voiceData[offset];//window[i] *
+ offset++;
+ }
+ kiss_fftr(fft_cfg, timedata, freqdata);
+
+ for (int i = 1; i < fftSize / 2 + 1; i++) {
+ float mag2 =(freqdata[i].i * freqdata[i].i + freqdata[i].r * freqdata[i].r);
+ mag2= 10.0f * log10f(1E-12f + mag2);
+ int scaled = (int) (mag2 +20)*4;
+
+ //0~255之间
+ fftData[fftOffset] = (scaled < 0) ? 0 : ((scaled > 255) ? 255 : scaled);
+
+ fftOffset++;
+ }
+
+// float normal = (maxMag - minMag) / 256;
+// for (int i = 0; i < dataSize / 2; ++i) {
+// fftData[i] = roundf((mags[i] - minMag) / normal);
+// }
+ }
+ free(fft_work);
+ free(window);
+}
\ No newline at end of file
diff --git a/ft8CN/app/src/main/cpp/spectrum_data.h b/ft8CN/app/src/main/cpp/spectrum_data.h
new file mode 100644
index 0000000..9714f25
--- /dev/null
+++ b/ft8CN/app/src/main/cpp/spectrum_data.h
@@ -0,0 +1,21 @@
+
+
+#include
+#include
+#include
+#include
+
+#include "ft8/constants.h"
+#include "common/debug.h"
+#include "fft/kiss_fftr.h"
+
+/**
+ * 对符号进行快速傅里叶变换,以1920块数据变换,生成960块。0~3000Hz
+ * normalization=1对数据归一化处理,方便显示FT8信号的频率
+ * @param voiceData 声音数据
+ * @param dataSize 声音数据的大小
+ * @param fftData fft数据
+ * @param normalization 是否是归一化处理
+ */
+void do_fftr(float* voiceData, int dataSize, int* fftData);
+void do_fftr_raw(float *voiceData, int dataSize, int *fftData);
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/FAQActivity.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/FAQActivity.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/FT8Common.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/FT8Common.java
old mode 100755
new mode 100644
index 4799f9a..67e2312
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/FT8Common.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/FT8Common.java
@@ -20,4 +20,6 @@ public final class FT8Common {
public static final int FT8_5_SYMBOLS_TIME_M =8;//5个符号的时间长度0.8秒
public static final int FT4_SLOT_TIME_M=75;//7.5秒
public static final int FT8_TRANSMIT_DELAY=500;//默认发射延迟时长,毫秒
+ public static final long DEEP_DECODE_TIMEOUT=7*1000;//深度解码的最长时间范围
+ public static final int DECODE_MAX_ITERATIONS=1;//迭代次数
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/Ft8Message.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/Ft8Message.java
old mode 100755
new mode 100644
index d755700..fe6519a
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/Ft8Message.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/Ft8Message.java
@@ -17,6 +17,7 @@ import androidx.annotation.NonNull;
import com.bg7yoz.ft8cn.database.DatabaseOpr;
import com.bg7yoz.ft8cn.ft8signal.FT8Package;
+import com.bg7yoz.ft8cn.ft8transmit.GenerateFT8;
import com.bg7yoz.ft8cn.ft8transmit.TransmitCallsign;
import com.bg7yoz.ft8cn.maidenhead.MaidenheadGrid;
import com.bg7yoz.ft8cn.rigs.BaseRigOperation;
@@ -34,7 +35,7 @@ public class Ft8Message {
public long utcTime;//UTC时间
public boolean isValid;//是否是有效信息
public int snr = 0;//信噪比
- public float time_sec = 0;//时间偏移
+ public float time_sec = 0;//时间偏移(秒)
public float freq_hz = 0;//频率
public int score = 0;//得分
public int messageHash;//消息的哈希
@@ -74,6 +75,10 @@ public class Ft8Message {
public LatLng fromLatLng = null;
public LatLng toLatLng = null;
+ public boolean isWeakSignal=false;
+
+
+
@NonNull
@SuppressLint({"SimpleDateFormat", "DefaultLocale"})
@@ -182,6 +187,14 @@ public class Ft8Message {
return String.format("%04.0f", freq_hz);
}
+ public String getMessageText(boolean showWeekSignal){
+ if (isWeakSignal && showWeekSignal){
+ return "*"+getMessageText();
+ }else {
+ return getMessageText();
+ }
+ }
+
/**
* 返回解码消息的文本内容
*
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/GeneralVariables.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/GeneralVariables.java
old mode 100755
new mode 100644
index 09f605a..d1fd408
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/GeneralVariables.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/GeneralVariables.java
@@ -14,6 +14,8 @@ import com.bg7yoz.ft8cn.connector.ConnectMode;
import com.bg7yoz.ft8cn.database.ControlMode;
import com.bg7yoz.ft8cn.database.DatabaseOpr;
import com.bg7yoz.ft8cn.ft8transmit.QslRecordList;
+import com.bg7yoz.ft8cn.html.HtmlContext;
+import com.bg7yoz.ft8cn.icom.IcomAudioUdp;
import com.bg7yoz.ft8cn.log.QSLRecord;
import com.bg7yoz.ft8cn.rigs.BaseRigOperation;
import com.bg7yoz.ft8cn.timer.UtcTimer;
@@ -33,6 +35,11 @@ public class GeneralVariables {
public static boolean saveSWLMessage=false;//保存解码消息开关
public static boolean saveSWL_QSO=false;//保存解码消息消息中的QSO开关
+ public static boolean deepDecodeMode=false;//是否开启深度解码
+
+ public static boolean audioOutput32Bit =true;//音频输出类型true=float,false=int16
+ public static int audioSampleRate=12000;//发射音频的采样率
+
public static MutableLiveData mutableVolumePercent = new MutableLiveData<>();
public static float volumePercent = 0.5f;//播放音频的音量,是百分比
@@ -151,7 +158,7 @@ public class GeneralVariables {
//private static final Map callsignAndGrids=new HashMap<>();
public static String myCallsign = "";//我的呼号
- public static String toModifier = "ADFG";//呼叫的修饰符
+ public static String toModifier = "";//呼叫的修饰符
private static float baseFrequency = 1000;//声音频率
public static MutableLiveData mutableBaseFrequency = new MutableLiveData<>();
@@ -543,21 +550,9 @@ public class GeneralVariables {
int order = 0;
for (String key : callsignAndGrids.keySet()) {
order++;
- if (order % 2 == 0) {
- result.append("");
- } else {
- result.append(" ");
- }
- result.append("");
- result.append(key);
- result.append(" \n");
- result.append("");
- result.append(callsignAndGrids.get(key));
- result.append(" \n");
-
+ HtmlContext.tableKeyRow(result,order % 2 != 0,key,callsignAndGrids.get(key));
}
return result.toString();
-
}
public static synchronized void deleteArrayListMore(ArrayList list) {
@@ -582,4 +577,11 @@ public class GeneralVariables {
return false;
}
+ /**
+ * 输出音频的数据类型,网络模式不可用
+ */
+ public enum AudioOutputBitMode{
+ Float32,
+ Int16
+ }
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/MainActivity.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/MainActivity.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/MainViewModel.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/MainViewModel.java
old mode 100755
new mode 100644
index b4bdd6b..ebdc682
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/MainViewModel.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/MainViewModel.java
@@ -5,19 +5,16 @@ package com.bg7yoz.ft8cn;
* 1.解码的总条数。decoded_counter和mutable_Decoded_Counter。
* 2.解码消息的列表。消息以Ft8Message展示,列表用ArrayList泛型实现。ft8Messages,mutableFt8MessageList。
* 3.解码和录音都需要时间同步,也就是以UTC时间的每15秒为一个周期。同步事件的触发由UtcTimer类来实现。
- * 录音方法runRecode,解码暂时用testFt8实现,后续应改为类方式。--------TODO------------
* 4.当前的UTC时间。timerSec,更新频率(心跳频率)由UtcTimer确定,暂定100毫秒。
- *
* 5.通过类方法getInstance获取当前的MainViewModel的实例,确保有唯一的实例。
* 6.用HamAudioRecorder类实现录音,目前只实现录音成文件,然后读取文件的数据给解码模块,后面要改成直接给数组的方式----TO DO---
- *
* 7.解码采用JNI接口调用原生C语言。调用接口名时ft8cn,由cpp文件夹下的CMakeLists.txt维护。各函数的调用接口在decode_ft8.cpp中。
- *
* -----2022.5.9-----
* 如果系统没有发射信号,触发器会在每一个周期触发录音动作,因录音开始和结束要浪费一些时间,如果不干预上一个录音的动作,将出现
* 连续的周期内录音动作重叠,造成第二个录音动作失败。所以,第二个周期的录音开始前,要停止前一个周期的录音,造成的结果就是每一次录音
* 的开始时间要晚于周期开始300毫秒(模拟器的结果),实际录音的长度一般在14.77秒左右
*
+ *
* @author BG7YOZ
* @date 2022.5.6
*/
@@ -71,6 +68,7 @@ import com.bg7yoz.ft8cn.rigs.GuoHeQ900Rig;
import com.bg7yoz.ft8cn.rigs.IcomRig;
import com.bg7yoz.ft8cn.rigs.InstructionSet;
import com.bg7yoz.ft8cn.rigs.KenwoodKT90Rig;
+import com.bg7yoz.ft8cn.rigs.KenwoodTS2000Rig;
import com.bg7yoz.ft8cn.rigs.KenwoodTS590Rig;
import com.bg7yoz.ft8cn.rigs.OnRigStateChanged;
import com.bg7yoz.ft8cn.rigs.XieGu6100Rig;
@@ -97,7 +95,7 @@ import java.util.concurrent.Executors;
public class MainViewModel extends ViewModel {
String TAG = "ft8cn MainViewModel";
- public boolean configIsLoaded=false;
+ public boolean configIsLoaded = false;
private static MainViewModel viewModel = null;//当前存在的实例。
//public static Application application;
@@ -123,12 +121,12 @@ public class MainViewModel extends ViewModel {
public MutableLiveData mutableIsDecoding = new MutableLiveData<>();//会触发频谱图中的标记动作
public ArrayList currentMessages = null;//本周期解码的消息(用于画到频谱上)
- public MutableLiveData mutableIsFlexRadio=new MutableLiveData<>();//是不是flex电台
+ public MutableLiveData mutableIsFlexRadio = new MutableLiveData<>();//是不是flex电台
private final ExecutorService getQTHThreadPool = Executors.newCachedThreadPool();
private final ExecutorService sendWaveDataThreadPool = Executors.newCachedThreadPool();
- private final GetQTHRunnable getQTHRunnable=new GetQTHRunnable(this);
- private final SendWaveDataRunnable sendWaveDataRunnable=new SendWaveDataRunnable();
+ private final GetQTHRunnable getQTHRunnable = new GetQTHRunnable(this);
+ private final SendWaveDataRunnable sendWaveDataRunnable = new SendWaveDataRunnable();
public HamRecorder hamRecorder;//用于录音的对象
@@ -140,7 +138,7 @@ public class MainViewModel extends ViewModel {
//控制电台的方式
public OperationBand operationBand = null;
- private SWLQsoList swlQsoList=new SWLQsoList();//用于记录SWL的QSO对象,对SWL QSO做判断,防止重复。
+ private SWLQsoList swlQsoList = new SWLQsoList();//用于记录SWL的QSO对象,对SWL QSO做判断,防止重复。
public MutableLiveData> mutableSerialPorts = new MutableLiveData<>();
@@ -189,7 +187,7 @@ public class MainViewModel extends ViewModel {
//发射信号用的消息列表
//public ArrayList transmitMessages = new ArrayList<>();
//public MutableLiveData> mutableTransmitMessages = new MutableLiveData<>();
- public MutableLiveData mutableTransmitMessagesCount=new MutableLiveData<>();
+ public MutableLiveData mutableTransmitMessagesCount = new MutableLiveData<>();
public boolean deNoise = false;//在频谱中抑制噪声
@@ -199,7 +197,7 @@ public class MainViewModel extends ViewModel {
public String queryKey = "";//查询的关键字
public int queryFilter = 0;//过滤,0全部,1,确认,2,未确认
public MutableLiveData mutableQueryFilter = new MutableLiveData<>();
- public ArrayList callsignRecords=new ArrayList<>();
+ public ArrayList callsignRecords = new ArrayList<>();
//public ArrayList qslRecords=new ArrayList<>();
//********************************************
//关注呼号的列表
@@ -279,7 +277,9 @@ public class MainViewModel extends ViewModel {
}
@Override
- public void afterDecode(long utc, float time_sec, int sequential, ArrayList messages) {
+ public void afterDecode(long utc, float time_sec, int sequential
+ , ArrayList messages, boolean isDeep) {
+ if (messages.size() == 0) return;//没有解码出消息,不触发动作
synchronized (ft8Messages) {
ft8Messages.addAll(messages);//添加消息到列表
@@ -293,20 +293,37 @@ public class MainViewModel extends ViewModel {
findIncludedCallsigns(messages);//查找符合条件的消息,放到呼叫列表中
//检查发射程序。从消息列表中解析发射的程序
- ft8TransmitSignal.parseMessageToFunction(messages);
+ //超出周期2秒钟,就不应该解析了
+ if (!ft8TransmitSignal.isTransmitting()
+ && (ft8SignalListener.timeSec
+ + GeneralVariables.pttDelay
+ + GeneralVariables.transmitDelay <= 2000)) {//考虑网络模式,发射时长是13秒
+ ft8TransmitSignal.parseMessageToFunction(messages);//解析消息,并处理
+ }
+
+ currentMessages = messages;
+
+ if (isDeep) {
+ currentDecodeCount += messages.size();
+ } else {
+ currentDecodeCount = messages.size();
+ }
mutableIsDecoding.postValue(false);//解码的状态,会触发频谱图中的标记动作
- currentMessages = messages;//保存本次解码的消息
- getQTHRunnable.messages=messages;
+ getQTHRunnable.messages = messages;
getQTHThreadPool.execute(getQTHRunnable);//用线程池的方式查询归属地
+ //此变量也是告诉消息列表变化的
+ mutable_Decoded_Counter.postValue(
+ currentDecodeCount);//告知界面消息的总数量
+
if (GeneralVariables.saveSWLMessage) {
databaseOpr.writeMessage(messages);//把SWL消息写到数据库
}
//检查QSO of SWL,并保存到SWLQSOTable中的通联列表qsoList中
- if (GeneralVariables.saveSWL_QSO){
+ if (GeneralVariables.saveSWL_QSO) {
swlQsoList.findSwlQso(messages, ft8Messages, new SWLQsoList.OnFoundSwlQso() {
@Override
public void doFound(QSLRecord record) {
@@ -366,12 +383,12 @@ public class MainViewModel extends ViewModel {
}
@Override
- public void onAfterGenerate(float[] data) {
+ public void onTransmitByWifi(Ft8Message msg) {
if (GeneralVariables.connectMode == ConnectMode.NETWORK) {
if (baseRig != null) {
if (baseRig.isConnected()) {
- sendWaveDataRunnable.baseRig=baseRig;
- sendWaveDataRunnable.data=data;
+ sendWaveDataRunnable.baseRig = baseRig;
+ sendWaveDataRunnable.message = msg;
//以线程池的方式执行网络数据包发送
sendWaveDataThreadPool.execute(sendWaveDataRunnable);
}
@@ -406,13 +423,14 @@ public class MainViewModel extends ViewModel {
}
}
- public void setTransmitIsFreeText(boolean isFreeText){
- if (ft8TransmitSignal!=null) {
+ public void setTransmitIsFreeText(boolean isFreeText) {
+ if (ft8TransmitSignal != null) {
ft8TransmitSignal.setTransmitFreeText(isFreeText);
}
}
- public boolean getTransitIsFreeText(){
- if (ft8TransmitSignal!=null){
+
+ public boolean getTransitIsFreeText() {
+ if (ft8TransmitSignal != null) {
return ft8TransmitSignal.isTransmitFreeText();
}
return false;
@@ -424,12 +442,12 @@ public class MainViewModel extends ViewModel {
*
* @param messages 消息
*/
- private void findIncludedCallsigns(ArrayList messages) {
+ private synchronized void findIncludedCallsigns(ArrayList messages) {
Log.d(TAG, "findIncludedCallsigns: 查找关注的呼号");
if (ft8TransmitSignal.isActivated() && ft8TransmitSignal.sequential != UtcTimer.getNowSequential()) {
return;
}
- int count=0;
+ int count = 0;
for (Ft8Message msg : messages) {
//与我的呼号有关,与关注的呼号有关
if (msg.getCallsignFrom().equals(GeneralVariables.myCallsign)
@@ -746,9 +764,12 @@ public class MainViewModel extends ViewModel {
case InstructionSet.XIEGU_6100:
baseRig = new XieGu6100Rig(GeneralVariables.civAddress);//协谷6100
break;
+ case InstructionSet.KENWOOD_TS2000:
+ baseRig = new KenwoodTS2000Rig();//建伍TS2000
+ break;
}
- mutableIsFlexRadio.postValue(GeneralVariables.instructionSet==InstructionSet.FLEX_NETWORK);
+ mutableIsFlexRadio.postValue(GeneralVariables.instructionSet == InstructionSet.FLEX_NETWORK);
}
@@ -863,8 +884,9 @@ public class MainViewModel extends ViewModel {
private static class GetQTHRunnable implements Runnable {
MainViewModel mainViewModel;
ArrayList messages;
+
public GetQTHRunnable(MainViewModel mainViewModel) {
- this.mainViewModel=mainViewModel;
+ this.mainViewModel = mainViewModel;
}
@@ -872,20 +894,19 @@ public class MainViewModel extends ViewModel {
public void run() {
CallsignDatabase.getMessagesLocation(
GeneralVariables.callsignDatabase.getDb(), messages);
-
- mainViewModel.currentDecodeCount = messages.size();
- //此变量也是告诉消息列表变化的
- mainViewModel.mutable_Decoded_Counter.postValue(
- mainViewModel.currentDecodeCount);//告知界面消息的总数量
+ mainViewModel.mutableFt8MessageList.postValue(mainViewModel.ft8Messages);
}
}
- private static class SendWaveDataRunnable implements Runnable{
+
+ private static class SendWaveDataRunnable implements Runnable {
BaseRig baseRig;
- float[] data;
+ //float[] data;
+ Ft8Message message;
+
@Override
public void run() {
- if (baseRig!=null&&data!=null){
- baseRig.sendWaveData(data);//实际生成的数据是12.64+0.04,0.04是生成的0数据
+ if (baseRig != null && message != null) {
+ baseRig.sendWaveData(message);//实际生成的数据是12.64+0.04,0.04是生成的0数据
}
}
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/MessageHashMap.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/MessageHashMap.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/bluetooth/BluetoothConstants.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/bluetooth/BluetoothConstants.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/bluetooth/BluetoothSerialListener.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/bluetooth/BluetoothSerialListener.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/bluetooth/BluetoothSerialService.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/bluetooth/BluetoothSerialService.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/bluetooth/BluetoothSerialSocket.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/bluetooth/BluetoothSerialSocket.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/bluetooth/BluetoothStateBroadcastReceive.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/bluetooth/BluetoothStateBroadcastReceive.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/callsign/CallsignDatabase.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/callsign/CallsignDatabase.java
old mode 100755
new mode 100644
index 353b01f..dde5f67
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/callsign/CallsignDatabase.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/callsign/CallsignDatabase.java
@@ -105,9 +105,12 @@ public class CallsignDatabase extends SQLiteOpenHelper {
/**
* 更新消息中的位置及经纬度信息
*
- * @param messages 消息列表
+ * @param ft8Messages 消息列表
*/
- public static void getMessagesLocation(SQLiteDatabase db, ArrayList messages) {
+ public static synchronized void getMessagesLocation(SQLiteDatabase db, ArrayList ft8Messages ) {
+ if (ft8Messages==null) return;
+ ArrayList messages = new ArrayList<>(ft8Messages);//防止线程访问冲突
+
for (Ft8Message msg : messages) {
if (msg.i3==0&&msg.n3==0) continue;//如果是自由文本,就不查了
CallsignInfo fromCallsignInfo = getCallsignInfo(db,
@@ -142,7 +145,6 @@ public class CallsignDatabase extends SQLiteOpenHelper {
}
msg.toLatLng = new LatLng(toCallsignInfo.Latitude, toCallsignInfo.Longitude*-1);
}
-
}
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/callsign/CallsignFileOperation.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/callsign/CallsignFileOperation.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/callsign/CallsignInfo.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/callsign/CallsignInfo.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/callsign/OnAfterQueryCallsignLocation.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/callsign/OnAfterQueryCallsignLocation.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/BaseRigConnector.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/BaseRigConnector.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/BluetoothRigConnector.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/BluetoothRigConnector.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/CableConnector.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/CableConnector.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/CableSerialPort.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/CableSerialPort.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/ConnectMode.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/ConnectMode.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/FlexConnector.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/FlexConnector.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/IComWifiConnector.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/IComWifiConnector.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/OnConnectorStateChanged.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/connector/OnConnectorStateChanged.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/count/CountDbOpr.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/count/CountDbOpr.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/count/CountFragment.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/count/CountFragment.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/count/CountInfoAdapter.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/count/CountInfoAdapter.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/ControlMode.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/ControlMode.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/DatabaseOpr.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/DatabaseOpr.java
old mode 100755
new mode 100644
index fa524c1..0a9feb2
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/DatabaseOpr.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/DatabaseOpr.java
@@ -823,8 +823,6 @@ public class DatabaseOpr extends SQLiteOpenHelper {
return false;
}
- //record.getToCallsign()
-
String querySQL;
if (!checkQSLCallsign(record)) {//如果不存在记录,就添加
querySQL = "INSERT INTO QslCallsigns (callsign" +
@@ -1147,9 +1145,19 @@ public class DatabaseOpr extends SQLiteOpenHelper {
@Override
protected Void doInBackground(Void... voids) {
String querySQL;
- querySQL = "INSERT INTO SWLQSOTable(call, gridsquare, mode, rst_sent, rst_rcvd, qso_date, " +
- "time_on, qso_date_off, time_off, band, freq, station_callsign, my_gridsquare," +
- "comment)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
+ //删除之前重复的记录
+ querySQL = "DELETE FROM SWLQSOTable where ([call]=?) and (station_callsign=?) and (qso_date=?) and(time_on=?) and (freq=?)";
+ databaseOpr.db.execSQL(querySQL, new String[]{
+ qslRecord.getToCallsign()
+ , qslRecord.getMyCallsign()
+ , qslRecord.getQso_date()
+ , qslRecord.getTime_on()
+ , BaseRigOperation.getFrequencyFloat(qslRecord.getBandFreq())
+ });
+ //添加记录
+ querySQL = "INSERT INTO SWLQSOTable([call], gridsquare, mode, rst_sent, rst_rcvd, qso_date, " +
+ "time_on, qso_date_off, time_off, band, freq, station_callsign, my_gridsquare,comment)\n" +
+ "VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
databaseOpr.db.execSQL(querySQL, new String[]{qslRecord.getToCallsign()
, qslRecord.getToMaidenGrid()
@@ -1449,7 +1457,8 @@ public class DatabaseOpr extends SQLiteOpenHelper {
}
String querySQL = "select * from QSLTable where ([call] like ?) \n" +
filterStr +
- " order by ID desc\n"+
+ " ORDER BY qso_date DESC, time_off DESC\n"+
+ //" order by ID desc\n"+
limitStr;
Cursor cursor = db.rawQuery(querySQL, new String[]{"%" + callsign + "%"});
ArrayList records = new ArrayList<>();
@@ -1774,8 +1783,7 @@ public class DatabaseOpr extends SQLiteOpenHelper {
GeneralVariables.baudRate = result.equals("") ? 19200 : Integer.parseInt(result);
}
if (name.equalsIgnoreCase("bandFreq")) {
- //--todo---把波段的索引数改成频率。用bandFreq值
- GeneralVariables.band = result.equals("") ? 14074000 : Long.valueOf(result);
+ GeneralVariables.band = result.equals("") ? 14074000 : Long.parseLong(result);
GeneralVariables.bandListIndex = OperationBand.getIndexByFreq(GeneralVariables.band);
}
if (name.equalsIgnoreCase("ctrMode")) {
@@ -1833,6 +1841,15 @@ public class DatabaseOpr extends SQLiteOpenHelper {
if (name.equalsIgnoreCase("saveSWLQSO")) {//保存解码信息
GeneralVariables.saveSWL_QSO = result.equals("1");
}
+ if (name.equalsIgnoreCase("audioBits")) {//输出音频是否32位浮点
+ GeneralVariables.audioOutput32Bit = result.equals("1");
+ }
+ if (name.equalsIgnoreCase("audioRate")) {//输出音频是否32位浮点
+ GeneralVariables.audioSampleRate =Integer.parseInt( result);
+ }
+ if (name.equalsIgnoreCase("deepMode")) {//是不是深度解码模式
+ GeneralVariables.deepDecodeMode =result.equals("1");
+ }
}
cursor.close();
@@ -1843,106 +1860,6 @@ public class DatabaseOpr extends SQLiteOpenHelper {
onAfterQueryConfig.doOnAfterQueryConfig(null, null);
}
- /*
- String result = "";
- GeneralVariables.setMyMaidenheadGrid(getConfigByKey("grid"));
-
- GeneralVariables.myCallsign = getConfigByKey("callsign");
-
- GeneralVariables.toModifier = getConfigByKey("toModifier");
-
- String callsign = GeneralVariables.myCallsign;
- if (callsign.length() > 0) {
- Ft8Message.hashList.addHash(FT8Package.getHash22(callsign), callsign);
- Ft8Message.hashList.addHash(FT8Package.getHash12(callsign), callsign);
- Ft8Message.hashList.addHash(FT8Package.getHash10(callsign), callsign);
- }
- result = getConfigByKey("freq");
- float freq = 1000;
- try {
- freq = Float.parseFloat(result);
- } catch (Exception e) {
- Log.e(TAG, "doInBackground: " + e.getMessage());
- }
- //GeneralVariables.setBaseFrequency(result.equals("") ? 1000 : Float.parseFloat(result));
- GeneralVariables.setBaseFrequency(freq);
-
- result = getConfigByKey("synFreq");
- GeneralVariables.synFrequency = !(result.equals("") || result.equals("0"));
-
- result = getConfigByKey("transDelay");
- if (result.matches("^\\d{1,4}$")) {//正则表达式,1-4位长度的数字
- GeneralVariables.transmitDelay = Integer.parseInt(result);
- } else {
- GeneralVariables.transmitDelay = FT8Common.FT8_TRANSMIT_DELAY;
- }
-
- result = getConfigByKey("civ");
- GeneralVariables.civAddress = result.equals("") ? 0xa4 : Integer.parseInt(result, 16);
-
- result = getConfigByKey("baudRate");
- GeneralVariables.baudRate = result.equals("") ? 19200 : Integer.parseInt(result);
-
- //--todo---把波段的索引数改成频率。用bandFreq值
- result = getConfigByKey("bandFreq");
-
- GeneralVariables.band = result.equals("") ? 14074000 : Long.valueOf(result);
- GeneralVariables.bandListIndex = OperationBand.getIndexByFreq(GeneralVariables.band);
-
-
- result = getConfigByKey("ctrMode");
- GeneralVariables.controlMode = result.equals("") ? ControlMode.VOX : Integer.parseInt(result);
-
- result = getConfigByKey("model");//电台型号
- GeneralVariables.modelNo = result.equals("") ? 0 : Integer.parseInt(result);
-
- result = getConfigByKey("instruction");//指令集
- GeneralVariables.instructionSet = result.equals("") ? 0 : Integer.parseInt(result);
-
-
- result = getConfigByKey("launchSupervision");//发射监管
- GeneralVariables.launchSupervision = result.equals("") ?
- GeneralVariables.DEFAULT_LAUNCH_SUPERVISION : Integer.parseInt(result);
-
- result = getConfigByKey("noReplyLimit");//发射监管
- GeneralVariables.noReplyLimit = result.equals("") ? 0 : Integer.parseInt(result);
-
- result = getConfigByKey("autoFollowCQ");//自动关注CQ
- GeneralVariables.autoFollowCQ = (result.equals("") || result.equals("1"));
-
- result = getConfigByKey("autoCallFollow");//自动关注CQ
- GeneralVariables.autoCallFollow = (result.equals("") || result.equals("1"));
-
- result = getConfigByKey("pttDelay");//ptt延时设置
- GeneralVariables.pttDelay = result.equals("") ? 100 : Integer.parseInt(result);
-
-
- //获取icom网络连接参数
- result = getConfigByKey("icomIp");//IcomIp地址
- GeneralVariables.icomIp = result.equals("") ? "255.255.255.255" : result;
- result = getConfigByKey("icomPort");//Icom端口
- GeneralVariables.icomUdpPort = result.equals("") ? 50001 : Integer.parseInt(result);
- result = getConfigByKey("icomUserName");//Icom用户名
- GeneralVariables.icomUserName = result.equals("") ? "ic705" : result;
- result = getConfigByKey("icomPassword");//Icom密码
- GeneralVariables.icomPassword = result;
-
- //输出音量大小
- result = getConfigByKey("volumeValue");
- GeneralVariables.volumePercent = result.equals("") ? 1.0f : Float.parseFloat(result) / 100f;
-
- //排除的呼号
- result = getConfigByKey("excludedCallsigns");
- GeneralVariables.addExcludedCallsigns(result);
-
- GetAllQSLCallsign.get(db);//获取通联过的呼号
-
- if (onAfterQueryConfig != null) {
- onAfterQueryConfig.doOnAfterQueryConfig(null, null);
- }
-
-
- */
return null;
}
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/DxccObject.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/DxccObject.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OnAfterQueryConfig.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OnAfterQueryConfig.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OnAfterQueryFollowCallsigns.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OnAfterQueryFollowCallsigns.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OnAfterWriteConfig.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OnAfterWriteConfig.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OnGetCallsign.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OnGetCallsign.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OperationBand.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OperationBand.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/RigNameList.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/RigNameList.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexCommand.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexCommand.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexMeterInfos.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexMeterInfos.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexMeterList.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexMeterList.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexMeterType.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexMeterType.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexMeters.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexMeters.java
deleted file mode 100755
index d5b7bee..0000000
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexMeters.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.bg7yoz.ft8cn.flex;
-/**
- * flexRadio仪表处理
- * @author BG7YOZ
- */
-
-import android.util.Log;
-
-import java.util.HashMap;
-
-public class FlexMeters extends HashMap {
- private static final String TAG="FlexMeters";
- public FlexMeters(String content) {
- String[] temp =content.substring(content.indexOf("meter ")+"meter ".length()).split("#");
- for (int i = 0; i < temp.length; i++) {
- String[] val = temp[i].split("=");
- if (val.length == 2) {
- if (val[0].contains(".")) {
- int index = Integer.parseInt(val[0].substring(0, val[0].indexOf(".")));
- FlexMeter meter;
- if (this.containsKey(index)){
- meter=this.get(index);
- }else {
- meter=new FlexMeter();
- this.put(index,meter);
- }
-
- if (val[0].toLowerCase().contains(".src")) {
- meter.src = val[1];
- }
- if (val[0].toLowerCase().contains(".num")) {
- meter.num = val[1];
- }
- if (val[0].toLowerCase().contains(".nam")) {
- meter.nam = val[1];
- }
- if (val[0].toLowerCase().contains(".low")) {
- meter.low = val[1];
- }
- if (val[0].toLowerCase().contains(".hi")) {
- meter.hi = val[1];
- }
- if (val[0].toLowerCase().contains(".desc")) {
- meter.desc = val[1];
- }
- if (val[0].toLowerCase().contains(".unit")) {
- meter.unit = val[1];
- }
- if (val[0].toLowerCase().contains(".fps")) {
- meter.fps = val[1];
- }
- if (val[0].toLowerCase().contains(".peak")) {
- meter.peak = val[1];
- }
- }
- }
- }
- }
-
- public void getAllMeters(){
- for (int key:this.keySet()) {
- //s.append(String.format("%d->%s\n",key,get(key)));
- Log.e(TAG, "getAllMeters: "+String.format("ID:%d FIELDS:%s\n",key,get(key)) );
- }
- }
-
-
- public static class FlexMeter {
- public String src;
- public String num;
- public String nam;
- public String low;
- public String hi;
- public String desc;
- public String unit;
- public String fps;
- public String peak;
-
- @Override
- public String toString() {
- return "{" +
- "src='" + src + '\'' +
- ", num='" + num + '\'' +
- ", nam='" + nam + '\'' +
- ", low='" + low + '\'' +
- ", hi='" + hi + '\'' +
- ", desc='" + desc + '\'' +
- ", unit='" + unit + '\'' +
- ", fps='" + fps + '\'' +
- ", peak='" + peak + '\'' +
- '}';
- }
- }
-}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexRadio.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexRadio.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexRadioFactory.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexRadioFactory.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexResponseStyle.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexResponseStyle.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/RadioTcpClient.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/RadioTcpClient.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/RadioUdpClient.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/RadioUdpClient.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/VITA.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/VITA.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/floatview/FloatView.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/floatview/FloatView.java
old mode 100755
new mode 100644
index 4e6e38c..618b442
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/floatview/FloatView.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/floatview/FloatView.java
@@ -215,7 +215,7 @@ public class FloatView extends ConstraintLayout {
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
width = wm.getDefaultDisplay().getWidth();
height = wm.getDefaultDisplay().getHeight();
- } else {//--TODO--这部分基本没有执行过
+ } else {//这部分基本没有执行过
width = parentViewWidth;
height = parentViewHeight;
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/floatview/FloatViewButton.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/floatview/FloatViewButton.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/A91List.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/A91List.java
new file mode 100644
index 0000000..c516314
--- /dev/null
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/A91List.java
@@ -0,0 +1,31 @@
+package com.bg7yoz.ft8cn.ft8listener;
+
+import com.bg7yoz.ft8cn.ft8transmit.GenerateFT8;
+
+import java.util.ArrayList;
+
+public class A91List {
+
+ public ArrayList list=new ArrayList<>();
+ public void clear(){
+ list.clear();
+ }
+ public void add(byte[] data,float freq,float sec){
+ A91 a91=new A91(data,sec,freq);
+ list.add(a91);
+ }
+ public int size(){
+ return list.size();
+ }
+ public static class A91{
+ public byte[] a91 ;//= new byte[GenerateFT8.FTX_LDPC_K_BYTES];
+ public float time_sec = 0;//时间偏移(秒)
+ public float freq_hz = 0;//频率
+
+ public A91(byte[] a91, float time_sec, float freq_hz) {
+ this.a91 = a91;
+ this.time_sec = time_sec;
+ this.freq_hz = freq_hz;
+ }
+ }
+}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/FT8SignalListener.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/FT8SignalListener.java
old mode 100755
new mode 100644
index 81bfffa..850d04d
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/FT8SignalListener.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/FT8SignalListener.java
@@ -14,9 +14,12 @@ import com.bg7yoz.ft8cn.FT8Common;
import com.bg7yoz.ft8cn.Ft8Message;
import com.bg7yoz.ft8cn.GeneralVariables;
import com.bg7yoz.ft8cn.database.DatabaseOpr;
+import com.bg7yoz.ft8cn.ft8transmit.GenerateFT8;
import com.bg7yoz.ft8cn.timer.OnUtcTimer;
import com.bg7yoz.ft8cn.timer.UtcTimer;
import com.bg7yoz.ft8cn.wave.OnGetVoiceDataDone;
+import com.bg7yoz.ft8cn.wave.WaveFileReader;
+import com.bg7yoz.ft8cn.wave.WaveFileWriter;
import java.util.ArrayList;
@@ -27,15 +30,15 @@ public class FT8SignalListener {
private final OnFt8Listen onFt8Listen;//当开始监听,解码结束后触发的事件
//private long band;
public MutableLiveData decodeTimeSec = new MutableLiveData<>();//解码的时长
+ public long timeSec=0;//解码的时长
+
private OnWaveDataListener onWaveDataListener;
+
private DatabaseOpr db;
- //用于保存解码数据的地址
-// private final long ft8Decoder=InitDecoder(0, FT8Common.SAMPLE_RATE
-// , 15*24000, true);;
+ private final A91List a91List = new A91List();//a91列表
- //private String myCallsign;
static {
System.loadLibrary("ft8cn");
@@ -50,7 +53,6 @@ public class FT8SignalListener {
this.onFt8Listen = onFt8Listen;
this.db = db;
-
//创建动作触发器,与UTC时间同步,以15秒一个周期,DoOnSecTimer是在周期起始时触发的事件。150是15秒
utcTimer = new UtcTimer(FT8Common.FT8_SLOT_TIME_M, false, new OnUtcTimer() {
@Override
@@ -108,6 +110,7 @@ public class FT8SignalListener {
}
public void decodeFt8(long utc, float[] voiceData) {
+
new Thread(new Runnable() {
@Override
public void run() {
@@ -115,70 +118,137 @@ public class FT8SignalListener {
if (onFt8Listen != null) {
onFt8Listen.beforeListen(utc);
}
- ArrayList ft8Messages = new ArrayList<>();
- Ft8Message ft8Message = new Ft8Message(FT8Common.FT8_MODE);
- ft8Message.utcTime = utc;
- ft8Message.band = GeneralVariables.band;
- //此处,改为reset,是因为在cpp部分,改为一个变量,不是以指针,申请新内存的方式处理了。
+
+ ///读入音频数据,并做预处理
//其实这种方式要注意一个问题,在一个周期之内,必须解码完毕,否则新的解码又要开始了
- long ft8Decoder = InitDecoder(ft8Message.utcTime, FT8Common.SAMPLE_RATE
+ long ft8Decoder = InitDecoder(utc, FT8Common.SAMPLE_RATE
, voiceData.length, true);
-
- //DecoderFt8Reset(ft8Decoder, ft8Message.utcTime, voiceData.length);
-
- DecoderMonitorPressFloat(voiceData, ft8Decoder);
+ DecoderMonitorPressFloat(voiceData, ft8Decoder);//读入音频数据
- int num_candidates = DecoderFt8FindSync(ft8Decoder);
- float dt = 0;
- int dtAverage = 0;
- for (int idx = 0; idx < num_candidates; ++idx) {
+ ArrayList allMsg = new ArrayList<>();
+ ArrayList msgs = runDecode(ft8Decoder, utc, false);
+ addMsgToList(allMsg, msgs);
+ timeSec = System.currentTimeMillis() - time;
+ decodeTimeSec.postValue(timeSec);//解码耗时
+ if (onFt8Listen != null) {
+ onFt8Listen.afterDecode(utc, averageOffset(allMsg), UtcTimer.sequential(utc), msgs, false);
+ }
- try {//做一下解码失败保护
- if (DecoderFt8Analysis(idx, ft8Decoder, ft8Message)) {
- if (ft8Message.isValid) {
- Ft8Message msg = new Ft8Message(ft8Message);//此处使用msg,是因为有的哈希呼号会把<...>替换掉
- if (checkMessageSame(ft8Messages, msg)) {
- continue;
- }
- dt += ft8Message.time_sec;
- dtAverage++;
- //ft8Messages.add(new Ft8Message(ft8Message));
- ft8Messages.add(msg);
- }
- }
- } catch (Exception e) {
- Log.e(TAG, "run: " + e.getMessage());
+
+ if (GeneralVariables.deepDecodeMode) {//进入深度解码模式
+ msgs = runDecode(ft8Decoder, utc, true);
+ addMsgToList(allMsg, msgs);
+ timeSec = System.currentTimeMillis() - time;
+ decodeTimeSec.postValue(timeSec);//解码耗时
+ if (onFt8Listen != null) {
+ onFt8Listen.afterDecode(utc, averageOffset(allMsg), UtcTimer.sequential(utc), msgs, true);
}
- }
- float time_sec = 0f;
- if (dtAverage != 0) {
- time_sec = dt / dtAverage;
- //utcTimer.setTime_sec(Math.round(time_sec * 1000));
- } else {//当检测不到时,不要偏移时间
- utcTimer.setTime_sec(Math.round(0));
- }
+ do {
+ if (timeSec > FT8Common.DEEP_DECODE_TIMEOUT) break;//此处做超时检测,超过一定时间(7秒),就不做减码操作了
+ //减去解码的信号
+ ReBuildSignal.subtractSignal(ft8Decoder, a91List);
+
+ //再做一次解码
+ msgs = runDecode(ft8Decoder, utc, true);
+ addMsgToList(allMsg, msgs);
+ timeSec = System.currentTimeMillis() - time;
+ decodeTimeSec.postValue(timeSec);//解码耗时
+ if (onFt8Listen != null) {
+ onFt8Listen.afterDecode(utc, averageOffset(allMsg), UtcTimer.sequential(utc), msgs, true);
+ }
+
+ } while (msgs.size() > 0 );
+
+ }
//移到finalize() 方法中调用了
DeleteDecoder(ft8Decoder);
-
- if (onFt8Listen != null) {
- onFt8Listen.afterDecode(utc, time_sec, UtcTimer.sequential(utc), ft8Messages);
- }
-
- decodeTimeSec.postValue(System.currentTimeMillis() - time);//解码耗时
-
Log.d(TAG, String.format("解码耗时:%d毫秒", System.currentTimeMillis() - time));
+
}
}).start();
}
+ private ArrayList runDecode(long ft8Decoder, long utc, boolean isDeep) {
+ ArrayList ft8Messages = new ArrayList<>();
+ Ft8Message ft8Message = new Ft8Message(FT8Common.FT8_MODE);
+
+ ft8Message.utcTime = utc;
+ ft8Message.band = GeneralVariables.band;
+ a91List.clear();
+
+ setDecodeMode(ft8Decoder, isDeep);//设置迭代次数,isDeep==true,迭代次数增加
+
+ int num_candidates = DecoderFt8FindSync(ft8Decoder);//最多120个
+ //long startTime = System.currentTimeMillis();
+ for (int idx = 0; idx < num_candidates; ++idx) {
+ //todo 应当做一下超时计算
+ try {//做一下解码失败保护
+ if (DecoderFt8Analysis(idx, ft8Decoder, ft8Message)) {
+
+ if (ft8Message.isValid) {
+ Ft8Message msg = new Ft8Message(ft8Message);//此处使用msg,是因为有的哈希呼号会把<...>替换掉
+ byte[] a91 = DecoderGetA91(ft8Decoder);
+ a91List.add(a91, ft8Message.freq_hz, ft8Message.time_sec);
+
+ if (checkMessageSame(ft8Messages, msg)) {
+ continue;
+ }
+
+ msg.isWeakSignal = isDeep;//是不是弱信号
+ ft8Messages.add(msg);
+
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "run: " + e.getMessage());
+ }
+
+ }
+
+
+ return ft8Messages;
+ }
+
+ /**
+ * 计算平均时间偏移值
+ *
+ * @param messages 消息列表
+ * @return 偏移值
+ */
+ private float averageOffset(ArrayList messages) {
+ if (messages.size() == 0) return 0f;
+ float dt = 0;
+ //int dtAverage = 0;
+ for (Ft8Message msg : messages) {
+ dt += msg.time_sec;
+ }
+ return dt / messages.size();
+ }
+
+ /**
+ * 把消息添加到列表中
+ *
+ * @param allMsg 消息列表
+ * @param newMsg 新的消息
+ */
+ private void addMsgToList(ArrayList allMsg, ArrayList newMsg) {
+ for (int i = newMsg.size() - 1; i >= 0; i--) {
+ if (checkMessageSame(allMsg, newMsg.get(i))) {
+ newMsg.remove(i);
+ } else {
+ allMsg.add(newMsg.get(i));
+ }
+ }
+ }
+
/**
* 检查消息列表里同样的内容是否存在
*
@@ -204,14 +274,15 @@ public class FT8SignalListener {
super.finalize();
}
- public OnWaveDataListener getOnWaveDataListener() {
- return onWaveDataListener;
- }
+
public void setOnWaveDataListener(OnWaveDataListener onWaveDataListener) {
this.onWaveDataListener = onWaveDataListener;
}
+
+
+
/**
* 解码的第一步,初始化解码器,获取解码器的地址。
*
@@ -261,5 +332,7 @@ public class FT8SignalListener {
public native void DecoderFt8Reset(long decoder, long utcTime, int num_samples);
+ public native byte[] DecoderGetA91(long decoder);//获取当前message的a91数据
+ public native void setDecodeMode(long decoder, boolean isDeep);//设置解码的模式,isDeep=true是多次迭代,=false是快速迭代
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/OnFt8Listen.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/OnFt8Listen.java
old mode 100755
new mode 100644
index 919625f..04492d0
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/OnFt8Listen.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/OnFt8Listen.java
@@ -23,5 +23,5 @@ public interface OnFt8Listen {
* @param sequential 当前的时序
* @param messages 消息列表
*/
- void afterDecode(long utc,float time_sec,int sequential, ArrayList messages);
+ void afterDecode(long utc,float time_sec,int sequential, ArrayList messages,boolean isDeep);
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/ReBuildSignal.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/ReBuildSignal.java
new file mode 100644
index 0000000..5e2a3a5
--- /dev/null
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/ReBuildSignal.java
@@ -0,0 +1,28 @@
+package com.bg7yoz.ft8cn.ft8listener;
+
+import android.util.Log;
+
+import com.bg7yoz.ft8cn.FT8Common;
+import com.bg7yoz.ft8cn.Ft8Message;
+import com.bg7yoz.ft8cn.ft8transmit.GenerateFT8;
+import com.bg7yoz.ft8cn.wave.WaveFileWriter;
+
+import java.util.ArrayList;
+
+public class ReBuildSignal {
+ private static String TAG = "ReBuildSignal";
+ static {
+ System.loadLibrary("ft8cn");
+ }
+
+
+ public static void subtractSignal(long decoder,A91List a91List){
+ for (A91List.A91 a91 : a91List.list) {
+ doSubtractSignal(decoder,a91.a91,FT8Common.SAMPLE_RATE,a91.freq_hz,a91.time_sec);
+ }
+ }
+
+ private static native void doSubtractSignal(long decoder,byte[] payload,int sample_rate
+ ,float frequency,float time_sec);
+
+}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8signal/FT8Package.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8signal/FT8Package.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/FT8TransmitSignal.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/FT8TransmitSignal.java
old mode 100755
new mode 100644
index 8e3607c..14626ce
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/FT8TransmitSignal.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/FT8TransmitSignal.java
@@ -1,6 +1,7 @@
package com.bg7yoz.ft8cn.ft8transmit;
/**
* 与发射信号有关的类。包括分析通联过程的自动程序。
+ *
* @author BGY70Z
* @date 2023-03-20
*/
@@ -33,8 +34,8 @@ import java.util.concurrent.Executors;
public class FT8TransmitSignal {
private static final String TAG = "FT8TransmitSignal";
- private boolean transmitFreeText=false;
- private String freeText="FREE TEXT";
+ private boolean transmitFreeText = false;
+ private String freeText = "FREE TEXT";
private final DatabaseOpr databaseOpr;//配置信息,和相关数据的数据库
private TransmitCallsign toCallsign;//目标呼号
@@ -64,7 +65,7 @@ public class FT8TransmitSignal {
private int receivedReport = 0;//我接收到的报告
private int receiveTargetReport = -100;//发送给对方的信号报告
//********************************************
- private final OnTransmitSuccess onTransmitSuccess ;//一般是用于保存QSL数据
+ private final OnTransmitSuccess onTransmitSuccess;//一般是用于保存QSL数据
//防止播放中止,变量不能放在方法中
@@ -80,7 +81,7 @@ public class FT8TransmitSignal {
private final OnDoTransmitted onDoTransmitted;//一般是用于打开关闭PTT
private final ExecutorService doTransmitThreadPool = Executors.newCachedThreadPool();
- private final DoTransmitRunnable doTransmitRunnable=new DoTransmitRunnable(this);
+ private final DoTransmitRunnable doTransmitRunnable = new DoTransmitRunnable(this);
static {
System.loadLibrary("ft8cn");
@@ -107,7 +108,7 @@ public class FT8TransmitSignal {
GeneralVariables.mutableVolumePercent.observeForever(new Observer() {
@Override
public void onChanged(Float aFloat) {
- if (audioTrack!=null){
+ if (audioTrack != null) {
audioTrack.setVolume(aFloat);
}
}
@@ -187,7 +188,6 @@ public class FT8TransmitSignal {
// @SuppressLint("DefaultLocale")
// @Override
// public void run() {
-// //--todo----
// //此处可能要修改,维护一个列表。把每个呼号,网格,时间,波段,记录下来
// if (functionOrder == 1 || functionOrder == 2) {//当消息处于1或2时,说明开始了通联
// messageStartTime = UtcTimer.getSystemTime();
@@ -336,9 +336,9 @@ public class FT8TransmitSignal {
//发射模式6,CQ BG7YOZ OL50
case 6:
resetTargetReport();//把给对方的信号报告,接收到对方的信号报告记录复位成-100
- Ft8Message msg=new Ft8Message(1, 0, "CQ", GeneralVariables.myCallsign
+ Ft8Message msg = new Ft8Message(1, 0, "CQ", GeneralVariables.myCallsign
, GeneralVariables.getMyMaidenhead4Grid());
- msg.modifier=GeneralVariables.toModifier;
+ msg.modifier = GeneralVariables.toModifier;
return msg;
}
@@ -365,18 +365,42 @@ public class FT8TransmitSignal {
setCurrentFunctionOrder(functionOrder);//设置当前消息
}
+ /**
+ * 为了最大限度兼容,把32位浮点转换成16位整型,有些声卡不支持32位的浮点。
+ * @param buffer 32位浮点音频
+ * @return 16位整型
+ */
+ private short[] float2Short(float[] buffer) {
+ short[] temp = new short[buffer.length + 8];//多出8个为0的数据包,是为了兼容QP-7C的RP2040音频判断
+ for (int i = 0; i < buffer.length; i++) {
+ float x = buffer[i];
+ if (x > 1.0)
+ x = 1.0f;
+ else if (x < -1.0)
+ x = -1.0f;
+ temp[i] = (short) (x * 32767.0);
+ }
+ return temp;
+ }
- private void playFT8Signal(float[] buffer) {
+ //private void playFT8Signal(float[] buffer) {
+ private void playFT8Signal(Ft8Message msg) {
- //todo--实现网络发送模式
if (GeneralVariables.connectMode == ConnectMode.NETWORK) {//网络方式就不播放音频了
Log.d(TAG, "playFT8Signal: 进入网络发射程序,等待音频发送。");
+
+
+ if (onDoTransmitted != null) {//处理音频数据,可以给ICOM的网络模式发送
+ onDoTransmitted.onTransmitByWifi(msg);
+ }
+
+
long now = System.currentTimeMillis();
while (isTransmitting) {//等待音频数据包发送完毕再退出,以触发afterTransmitting
try {
Thread.sleep(1);
long current = System.currentTimeMillis() - now;
- if (current > 13500) {//实际发射的时长
+ if (current > 13000) {//实际发射的时长
isTransmitting = false;
break;
}
@@ -390,26 +414,45 @@ public class FT8TransmitSignal {
}
- Log.d(TAG, "playFT8Signal: 准备声卡播放....");
+ //进入声卡模式
+ float[] buffer;
+ buffer = GenerateFT8.generateFt8(msg, GeneralVariables.getBaseFrequency()
+ , GeneralVariables.audioSampleRate);
+ if (buffer == null) {
+ afterPlayAudio();
+ return;
+ }
+
+ Log.d(TAG, String.format("playFT8Signal: 准备声卡播放....位数:%s,采样率:%d"
+ , GeneralVariables.audioOutput32Bit ? "Float32" : "Int16"
+ , GeneralVariables.audioSampleRate));
attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
- myFormat = new AudioFormat.Builder().setSampleRate(FT8Common.SAMPLE_RATE)
- //.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
- .setEncoding(AudioFormat.ENCODING_PCM_FLOAT)
+ //myFormat = new AudioFormat.Builder().setSampleRate(FT8Common.SAMPLE_RATE)
+ myFormat = new AudioFormat.Builder().setSampleRate(GeneralVariables.audioSampleRate)
+ .setEncoding(GeneralVariables.audioOutput32Bit ? //浮点与整型
+ AudioFormat.ENCODING_PCM_FLOAT : AudioFormat.ENCODING_PCM_16BIT)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO).build();
int mySession = 0;
audioTrack = new AudioTrack(attributes, myFormat
- , 12000 * 15 * 4, AudioTrack.MODE_STATIC
+ , GeneralVariables.audioOutput32Bit ? GeneralVariables.audioSampleRate * 15 * 4
+ : GeneralVariables.audioSampleRate * 15 * 2//浮点与整型
+ , AudioTrack.MODE_STATIC
, mySession);
-
- //写入数据大小 array 就是预先将音频数据加载到array数组中
- int writeResult = audioTrack.write(buffer, 0, buffer.length
- , AudioTrack.WRITE_NON_BLOCKING);
-
+ //区分32浮点和整型
+ int writeResult;
+ if (GeneralVariables.audioOutput32Bit) {
+ writeResult = audioTrack.write(buffer, 0, buffer.length
+ , AudioTrack.WRITE_NON_BLOCKING);
+ } else {
+ short[] audio_data = float2Short(buffer);
+ writeResult = audioTrack.write(audio_data, 0, audio_data.length
+ , AudioTrack.WRITE_NON_BLOCKING);
+ }
if (buffer.length > writeResult) {
Log.e(TAG, String.format("播放缓冲区不足:%d--->%d", buffer.length, writeResult));
@@ -463,10 +506,7 @@ public class FT8TransmitSignal {
messageEndTime = UtcTimer.getSystemTime();//获取结束的时间
//如对方没有网格,就从历史呼号与网格对应表中查找
- toMaidenheadGrid =GeneralVariables.getGridByCallsign(toCallsign.callsign,databaseOpr);
-// if (toMaidenheadGrid.equals("")) {
-// toMaidenheadGrid = GeneralVariables.getGridByCallsign(toCallsign.callsign,databaseOpr);
-// }
+ toMaidenheadGrid = GeneralVariables.getGridByCallsign(toCallsign.callsign, databaseOpr);
if (messageStartTime == 0) {//如果起始时间没有,就取现在的
messageStartTime = UtcTimer.getSystemTime();
@@ -557,6 +597,29 @@ public class FT8TransmitSignal {
}
}
+ /**
+ * 检查消息中from中有目标呼号的数量。当有目标呼号呼叫我的消息,返回0,
+ * @param messages 消息列表
+ * @return 0:有目标呼叫我的,1:没有任何目标呼号发出的消息,>1:有目标呼号呼叫别人的消息
+ */
+ private int checkTargetCallMe(ArrayList messages){
+ int fromCount=1;
+ for (int i = messages.size() - 1; i >= 0; i--) {
+ Ft8Message ft8Message = messages.get(i);
+ if (ft8Message.getSequence() == sequential) continue;//同一个时序下的消息不做解析
+ if (toCallsign == null) {
+ continue;
+ }
+ if (ft8Message.getCallsignTo().equals(GeneralVariables.myCallsign)
+ && checkCallsignIsCallTo(ft8Message.getCallsignFrom(), toCallsign.callsign)) {
+ return 0;
+ }
+ if (checkCallsignIsCallTo(ft8Message.getCallsignFrom(), toCallsign.callsign)){
+ fromCount++;//计数器,from是目标呼号的情况
+ }
+ }
+ return fromCount;
+ }
/**
* 检测本消息列表中对方回复消息的序号,如果没有,返回-1
*
@@ -566,6 +629,7 @@ public class FT8TransmitSignal {
private int checkFunctionOrdFromMessages(ArrayList messages) {
for (int i = messages.size() - 1; i >= 0; i--) {
Ft8Message ft8Message = messages.get(i);
+ if (ft8Message.getSequence() == sequential) continue;//同一个时序下的消息不做解析
if (toCallsign == null) {
continue;
}
@@ -627,7 +691,7 @@ public class FT8TransmitSignal {
if (msg.band != GeneralVariables.band) {//如果消息不在相同的波段内,不呼叫
continue;
}
- if (GeneralVariables.checkIsExcludeCallsign(msg.callsignFrom)){//如果是在过滤范围内的呼叫,不理会
+ if (GeneralVariables.checkIsExcludeCallsign(msg.callsignFrom)) {//如果是在过滤范围内的呼叫,不理会
continue;
}
@@ -693,7 +757,7 @@ public class FT8TransmitSignal {
QSLRecord record = GeneralVariables.qslRecordList.getRecordByCallsign(toCall.callsign);
if (record == null) {
- toMaidenheadGrid=GeneralVariables.getGridByCallsign(toCallsign.callsign,databaseOpr);
+ toMaidenheadGrid = GeneralVariables.getGridByCallsign(toCallsign.callsign, databaseOpr);
record = GeneralVariables.qslRecordList.addQSLRecord(new QSLRecord(
messageStartTime,
messageEndTime,
@@ -739,20 +803,19 @@ public class FT8TransmitSignal {
/**
* 从关注列表解码的消息中,此处是变化发射程序的入口
*
- * @param messages 消息列表
+ * @param msgList 消息列表
*/
//@RequiresApi(api = Build.VERSION_CODES.N)
- public void parseMessageToFunction(ArrayList messages) {
+ public void parseMessageToFunction(ArrayList msgList) {
if (GeneralVariables.myCallsign.length() < 3) {
return;
}
- if (messages.size() > 0) {//如果当前消息的时序与发射的时序相同,不解析
- if (messages.get(0).getSequence() == sequential) {
- return;
- }
- } else {//没有消息解析,返回
+ if (msgList.size() == 0) return;//没有消息解析,返回
+
+ if (msgList.get(0).getSequence() == sequential) {
return;
}
+ ArrayList messages =new ArrayList<>(msgList);//防止线程冲突
int newOrder = checkFunctionOrdFromMessages(messages);//检查消息中对方回复的消息序号,-1为没有收到
@@ -760,16 +823,20 @@ public class FT8TransmitSignal {
GeneralVariables.noReplyCount = 0;
}
-
//更新一下通联的列表检查是不是在通联列表中,如果没有记录下来,就保存
updateQSlRecordList(newOrder, toCallsign);
- //判断通联成功:对方回73(5)||我是73(5),且对方没回(-1)||我是RR73(4),且已经达到无回应阈值,且有无回应限制
+
+ // 判断通联成功:对方回73(5)||我是73(5),且对方没回(-1)
+ // 或者我是RR73(4),且已经达到无回应阈值,且有无回应限制
+ // 或我是RR73(4),且对方开始呼叫别人了,解决RR73卡死的问题
if (newOrder == 5
- || (functionOrder == 5 && newOrder == -1)
+ || (functionOrder == 5 && newOrder == -1)// 判断通联成功:对方回73(5)||我是73(5),且对方没回(-1)
|| (functionOrder == 4 &&
(GeneralVariables.noReplyCount > GeneralVariables.noReplyLimit * 2)
- && (GeneralVariables.noReplyLimit > 0))) {
+ && (GeneralVariables.noReplyLimit > 0)) // 或者我是RR73(4),且已经达到无回应阈值,且有无回应限制
+ || (functionOrder ==4 && checkTargetCallMe(messages)>1)
+ ) { // 或我是RR73(4),且对方开始呼叫别人了
//doComplete();//做保存的动作
//进入到CQ状态
resetToCQ();
@@ -812,8 +879,10 @@ public class FT8TransmitSignal {
}
- //到此位置,说明没有回应,错误次数要加1
- GeneralVariables.noReplyCount++;
+ //到此位置,说明没有回应,错误次数要加1,弱信号检测不记无回应
+ if (!messages.get(0).isWeakSignal) {
+ GeneralVariables.noReplyCount++;
+ }
//如果超出无反应限定值,复位到CQ状态
if ((GeneralVariables.noReplyCount > GeneralVariables.noReplyLimit) && (GeneralVariables.noReplyLimit > 0)) {
//检查关注消息列表,如果没有新的CQ,就进入到CQ状态,如果有,就转入到呼叫新的目标。
@@ -911,7 +980,7 @@ public class FT8TransmitSignal {
if (GeneralVariables.myCallsign.length() < 3) {
return;
}
- //todo---要判断我的呼号类型,才能确定i3n3 !!!
+ //要判断我的呼号类型,才能确定i3n3 !!!
int i3 = GenerateFT8.checkI3ByCallsign(GeneralVariables.myCallsign);
setTransmit(new TransmitCallsign(i3, 0, "CQ", UtcTimer.getNowSequential())
, 6, "");
@@ -964,15 +1033,15 @@ public class FT8TransmitSignal {
public void setTransmitFreeText(boolean transmitFreeText) {
this.transmitFreeText = transmitFreeText;
- if (transmitFreeText){
+ if (transmitFreeText) {
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.trans_free_text_mode));
- }else {
+ } else {
ToastMessage.show((GeneralVariables.getStringFromResource(R.string.trans_standard_messge_mode)));
}
}
- private static class DoTransmitRunnable implements Runnable{
+ private static class DoTransmitRunnable implements Runnable {
FT8TransmitSignal transmitSignal;
public DoTransmitRunnable(FT8TransmitSignal transmitSignal) {
@@ -992,25 +1061,19 @@ public class FT8TransmitSignal {
//用于显示将要发射的消息内容
Ft8Message msg;
- if (transmitSignal.transmitFreeText){
- msg=new Ft8Message("CQ",GeneralVariables.myCallsign,transmitSignal.freeText);
- msg.i3=0;
- msg.n3=0;
- }else {
+ if (transmitSignal.transmitFreeText) {
+ msg = new Ft8Message("CQ", GeneralVariables.myCallsign, transmitSignal.freeText);
+ msg.i3 = 0;
+ msg.n3 = 0;
+ } else {
msg = transmitSignal.getFunctionCommand(transmitSignal.functionOrder);
}
- msg.modifier=GeneralVariables.toModifier;
+ msg.modifier = GeneralVariables.toModifier;
if (transmitSignal.onDoTransmitted != null) {
//此处用于处理PTT等事件
transmitSignal.onDoTransmitted.onBeforeTransmit(msg, transmitSignal.functionOrder);
}
- //short[] buffer = new short[FT8Common.SAMPLE_RATE * FT8Common.FT8_SLOT_TIME];
- //79个符号,每个符号0.16秒,采样率12000,
-// short[] buffer = new short[(int) (0.5f +
-// GenerateFT8.num_tones * GenerateFT8.symbol_period
-// * GenerateFT8.sample_rate)]; // 数据信号中的采样数0.5+79*0.16*12000];
-
transmitSignal.isTransmitting = true;
transmitSignal.mutableIsTransmitting.postValue(true);
@@ -1020,11 +1083,11 @@ public class FT8TransmitSignal {
, GeneralVariables.getBaseFrequency()
, msg.getMessageText()));
//生成信号
- float[] buffer=GenerateFT8.generateFt8(msg, GeneralVariables.getBaseFrequency());
- if (buffer==null) {
- return;
- }
- ;
+// float[] buffer=GenerateFT8.generateFt8(msg, GeneralVariables.getBaseFrequency());
+// if (buffer==null) {
+// return;
+// }
+
//电台动作可能有要有个延迟时间,所以时间并不一定完全准确
try {//给电台一个100毫秒的响应时间
Thread.sleep(GeneralVariables.pttDelay);//给PTT指令后,电台一个响应时间,默认100毫秒
@@ -1032,11 +1095,12 @@ public class FT8TransmitSignal {
e.printStackTrace();
}
- if (transmitSignal.onDoTransmitted != null) {//处理音频数据,可以给ICOM的网络模式发送
- transmitSignal.onDoTransmitted.onAfterGenerate(buffer);
- }
+// if (transmitSignal.onDoTransmitted != null) {//处理音频数据,可以给ICOM的网络模式发送
+// transmitSignal.onDoTransmitted.onAfterGenerate(buffer);
+// }
//播放音频
- transmitSignal.playFT8Signal(buffer);
+ //transmitSignal.playFT8Signal(buffer);
+ transmitSignal.playFT8Signal(msg);
}
}
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/FunctionOfTransmit.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/FunctionOfTransmit.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/GenerateFT8.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/GenerateFT8.java
old mode 100755
new mode 100644
index 5fc3a6e..675252b
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/GenerateFT8.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/GenerateFT8.java
@@ -16,7 +16,7 @@ import com.bg7yoz.ft8cn.ui.ToastMessage;
public class GenerateFT8 {
private static final String TAG = "GenerateFT8";
private static final int FTX_LDPC_K = 91;
- private static final int FTX_LDPC_K_BYTES = (FTX_LDPC_K + 7) / 8;
+ public static final int FTX_LDPC_K_BYTES = (FTX_LDPC_K + 7) / 8;
private static final int FT8_NN = 79;
private static final float FT8_SYMBOL_PERIOD = 0.160f;
private static final float FT8_SYMBOL_BT = 2.0f;
@@ -29,7 +29,7 @@ public class GenerateFT8 {
private static final float symbol_bt = FT8_SYMBOL_BT;//FT8_SYMBOL_BT=2.0f
private static final float slot_time = FT8_SLOT_TIME;//FT8_SLOT_TIME=15f
//public static int sample_rate = 48000;//采样率
- public static int sample_rate = 12000;//采样率
+ //public static int sample_rate = 12000;//采样率
static {
@@ -120,8 +120,19 @@ public class GenerateFT8 {
return !extraInfo.trim().matches("[A-Z][A-Z][0-9][0-9]");
}
+ public static float[] generateFt8(Ft8Message msg, float frequency,int sample_rate){
+ return generateFt8(msg,frequency,sample_rate,true);
+ }
- public static float[] generateFt8(Ft8Message msg, float frequency) {
+ /**
+ * 生成FT8信号
+ * @param msg 消息
+ * @param frequency 频率
+ * @param sample_rate 采样率
+ * @param hasModifier 是否有修饰符
+ * @return
+ */
+ public static float[] generateFt8(Ft8Message msg, float frequency,int sample_rate,boolean hasModifier) {
if (msg.callsignFrom.length()<3){
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.callsign_error));
return null;
@@ -131,7 +142,11 @@ public class GenerateFT8 {
//把"<>"去掉
msg.callsignTo = msg.callsignTo.replace("<", "").replace(">", "");
msg.callsignFrom = msg.callsignFrom.replace("<", "").replace(">", "");
- msg.modifier=GeneralVariables.toModifier;//修饰符
+ if (hasModifier) {
+ msg.modifier = GeneralVariables.toModifier;//修饰符
+ }else {
+ msg.modifier="";
+ }
//msg.callsignTo="CQ AzCz";
//判定用非标准呼号i3=4的条件:
@@ -160,13 +175,49 @@ public class GenerateFT8 {
packFreeTextTo77(msg.getMessageText(), packed);
}
+ return generateFt8ByA91(packed,frequency,sample_rate);
+ /*
// 其次,将二进制消息编码为FSK音调序列,79个字节
byte[] tones = new byte[num_tones]; // 79音调(符号)数组
+ //此处是88个字节(91+7)/8,可以使用a91生成音频
ft8_encode(packed, tones);
// 第三,将FSK音调转换为音频信号b
+
+ int num_samples = (int) (0.5f + num_tones * symbol_period * sample_rate); // 数据信号中的采样数0.5+79*0.16*12000
+
+ //float[] signal = new float[Ft8num_samples];
+ float[] signal = new float[num_samples];
+
+ //Ft8num_sampleFT8声音的总采样数,不是字节数。15*12000
+ //for (int i = 0; i < Ft8num_samples; i++)//把数据全部静音。
+ for (int i = 0; i < num_samples; i++)//把数据全部静音。
+ {
+ signal[i] = 0;
+ }
+
+ // 用79个字节符号,生成FT8音频
+ synth_gfsk(tones, num_tones, frequency, symbol_bt, symbol_period, sample_rate, signal, 0);
+ for (int i = 0; i < num_samples; i++)//把数据全部静音。
+ {
+ if (signal[i]>1.0||signal[i]<-1.0){
+ Log.e(TAG, "generateFt8: "+signal[i] );
+ }
+ }
+ return signal;
+ */
+ }
+
+ public static float[] generateFt8ByA91(byte[] a91, float frequency,int sample_rate){
+ byte[] tones = new byte[num_tones]; // 79音调(符号)数组
+ //此处是12个字节(91+7)/8,可以使用a91生成音频
+ ft8_encode(a91, tones);
+
+ // 第三,将FSK音调转换为音频信号b
+
+
int num_samples = (int) (0.5f + num_tones * symbol_period * sample_rate); // 数据信号中的采样数0.5+79*0.16*12000
@@ -184,17 +235,18 @@ public class GenerateFT8 {
signal[i] = 0;
}
+ // 用79个字节符号,生成FT8音频
synth_gfsk(tones, num_tones, frequency, symbol_bt, symbol_period, sample_rate, signal, 0);
- for (int i = 0; i < num_samples; i++)//把数据全部静音。
- {
- if (signal[i]>1.0||signal[i]<-1.0){
- Log.e(TAG, "generateFt8: "+signal[i] );
- }
- }
+// for (int i = 0; i < num_samples; i++)//把数据全部静音。
+// {
+// if (signal[i]>1.0||signal[i]<-1.0){
+// Log.e(TAG, "generateFt8: "+signal[i] );
+// }
+// }
return signal;
-
}
+
private static native int packFreeTextTo77(String msg, byte[] c77);
private static native int pack77(String msg, byte[] c77);
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/OnDoTransmitted.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/OnDoTransmitted.java
old mode 100755
new mode 100644
index 9dca03a..c519e83
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/OnDoTransmitted.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/OnDoTransmitted.java
@@ -10,5 +10,5 @@ import com.bg7yoz.ft8cn.Ft8Message;
public interface OnDoTransmitted {
void onBeforeTransmit(Ft8Message message,int functionOder);
void onAfterTransmit(Ft8Message message, int functionOder);
- void onAfterGenerate(float[] data);
+ void onTransmitByWifi(Ft8Message message);
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/OnTransmitSuccess.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/OnTransmitSuccess.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/QSLRecord.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/QSLRecord.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/QslRecordList.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/QslRecordList.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/TransmitCallsign.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/TransmitCallsign.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridInfoWindow.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridInfoWindow.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridMarkerInfoWindow.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridMarkerInfoWindow.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridOsmMapView.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridOsmMapView.java
old mode 100755
new mode 100644
index 84f03b8..eff74c3
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridOsmMapView.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridOsmMapView.java
@@ -449,13 +449,11 @@ public class GridOsmMapView {
if (fromLatLng == null) {
//todo 把呼号转为国家的经纬度
return null;
- //fromLatLng = message.fromLatLng;
}
if (toLatLng == null) {
//todo 把呼号转为国家的经纬度
return null;
- //toLatLng = message.toLatLng;
}
final GridPolyLine line = new GridPolyLine(gridMapView, fromLatLng, toLatLng, recordStr);
return line;
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridRecordInfoWindow.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridRecordInfoWindow.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridTrackerMainActivity.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridTrackerMainActivity.java
old mode 100755
new mode 100644
index 3e195a3..baf5424
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridTrackerMainActivity.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridTrackerMainActivity.java
@@ -1,6 +1,7 @@
package com.bg7yoz.ft8cn.grid_tracker;
/**
* 网格追踪的主窗口。
+ *
* @author BGY70Z
* @date 2023-03-20
*/
@@ -78,7 +79,6 @@ public class GridTrackerMainActivity extends AppCompatActivity {
private MutableLiveData> qslRecordList = new MutableLiveData<>();
-
@SuppressLint("NotifyDataSetChanged")
protected void doAfterCreate() {
//设置消息列表
@@ -101,7 +101,7 @@ public class GridTrackerMainActivity extends AppCompatActivity {
}
//画日志界面查询出的全部消息
String queryKey = intentGet.getStringExtra("qslAll");
- int queryFilter=intentGet.getIntExtra("queryFilter",0);
+ int queryFilter = intentGet.getIntExtra("queryFilter", 0);
if (queryKey != null) {
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.tracker_query_qso_info));
mainViewModel.databaseOpr.getQSLRecordByCallsign(true, 0, queryKey, queryFilter
@@ -187,30 +187,68 @@ public class GridTrackerMainActivity extends AppCompatActivity {
@SuppressLint({"DefaultLocale", "NotifyDataSetChanged"})
@Override
public void onChanged(Integer integer) {
- callingListAdapter.notifyDataSetChanged();
+// callingListAdapter.notifyDataSetChanged();
//当列表下部稍微多出一些,自动上移
+// if (callMessagesRecyclerView.computeVerticalScrollRange()
+// - callMessagesRecyclerView.computeVerticalScrollExtent()
+// - callMessagesRecyclerView.computeVerticalScrollOffset() < 500) {
+// callMessagesRecyclerView.scrollToPosition(callingListAdapter.getItemCount() - 1);
+// }
+// if (mainViewModel.currentMessages != null) {
+//
+// ToastMessage.show(String.format(GeneralVariables.getStringFromResource(
+// R.string.tracker_decoded_new)
+// , mainViewModel.currentDecodeCount)
+// + " " + String.format(
+// getString(R.string.decoding_takes_milliseconds)
+// , mainViewModel.ft8SignalListener.decodeTimeSec.getValue()));
+// //画电台之间的连线
+// //对CQ的电台打点
+// gridOsmMapView.clearLines();
+// gridOsmMapView.clearMarkers();
+// for (Ft8Message msg : mainViewModel.currentMessages) {
+// drawMessage(msg);//在地图上画每一个消息
+// }
+// gridOsmMapView.showInfoWindows();
+// }
+ }
+ });
+ mainViewModel.mutableIsDecoding.observe(this, new Observer() {
+ @Override
+ public void onChanged(Boolean aBoolean) {
+ if (aBoolean) {
+ gridOsmMapView.clearLines();
+ gridOsmMapView.clearMarkers();
+ }
+ }
+ });
+ mainViewModel.mutableFt8MessageList.observe(this, new Observer>() {
+ @SuppressLint("NotifyDataSetChanged")
+ @Override
+ public void onChanged(ArrayList messages) {
+ if (mainViewModel.currentMessages == null) return;
+ ArrayList tempMsg = new ArrayList<>(mainViewModel.currentMessages);
+ callingListAdapter.notifyDataSetChanged();
if (callMessagesRecyclerView.computeVerticalScrollRange()
- callMessagesRecyclerView.computeVerticalScrollExtent()
- callMessagesRecyclerView.computeVerticalScrollOffset() < 500) {
callMessagesRecyclerView.scrollToPosition(callingListAdapter.getItemCount() - 1);
}
- if (mainViewModel.currentMessages != null) {
- ToastMessage.show(String.format(GeneralVariables.getStringFromResource(
- R.string.tracker_decoded_new)
- , mainViewModel.currentDecodeCount)
- + " " + String.format(
- getString(R.string.decoding_takes_milliseconds)
- , mainViewModel.ft8SignalListener.decodeTimeSec.getValue()));
- //画电台之间的连线
- //对CQ的电台打点
- gridOsmMapView.clearLines();
- gridOsmMapView.clearMarkers();
- for (Ft8Message msg : mainViewModel.currentMessages) {
- drawMessage(msg);//在地图上画每一个消息
- }
- gridOsmMapView.showInfoWindows();
+ binding.gridMessageTextView.setText(String.format("%s %s"
+ , String.format(GeneralVariables.getStringFromResource(
+ R.string.tracker_decoded_new)
+ , mainViewModel.currentDecodeCount), String.format(
+ getString(R.string.decoding_takes_milliseconds)
+ , mainViewModel.ft8SignalListener.decodeTimeSec.getValue())));
+
+ //画电台之间的连线
+ //对CQ的电台打点
+ for (Ft8Message msg : tempMsg) {
+ drawMessage(msg);//在地图上画每一个消息
}
+ gridOsmMapView.showInfoWindows();
+ //}
}
});
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/html/HtmlContext.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/html/HtmlContext.java
old mode 100755
new mode 100644
index a876798..9d4a309
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/html/HtmlContext.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/html/HtmlContext.java
@@ -5,6 +5,7 @@ package com.bg7yoz.ft8cn.html;
* @date 2023-03-20
*/
+import android.annotation.SuppressLint;
import android.database.Cursor;
import com.bg7yoz.ft8cn.BuildConfig;
@@ -29,9 +30,10 @@ public class HtmlContext {
" UL {\tCOLOR: #333333; FONT-FAMILY: tahoma,helvetica,sans-serif }\n" +
" P {\tCOLOR: #333333; FONT-FAMILY: tahoma,helvetica,sans-serif }\n" +
" BODY {\tCOLOR: #333333; FONT-FAMILY: tahoma,helvetica,sans-serif }\n" +
- " TD {\tCOLOR: #333333; FONT-FAMILY: tahoma,helvetica,sans-serif }\n" +
+ " FIELDSET {max-width: fit-content}\n" +
+ " TD {\tCOLOR: #333333; FONT-FAMILY: tahoma,helvetica,sans-serif; FONT-SIZE: 8pt}\n" +
" TR {\tBACKGROUND-COLOR: WHITE;COLOR: #333333; FONT-FAMILY: tahoma,helvetica,sans-serif }\n" +
- " TR.bbb {\tBACKGROUND-COLOR: #F6FBFF; COLOR: #333333; FONT-FAMILY: tahoma,helvetica,sans-serif }\n" +
+ " TR.bbb {\tBACKGROUND-COLOR: #DDF0FE; COLOR: #333333; FONT-FAMILY: tahoma,helvetica,sans-serif }\n" +
" TH {\tBACKGROUND-COLOR: C8C2FF;COLOR: #333333; FONT-FAMILY: tahoma,helvetica,sans-serif;FONT-SIZE: 8pt; }\n" +
" FONT.title {\tBACKGROUND-COLOR: white; COLOR: #363636; FONT-FAMILY:tahoma,helvetica,verdana,lucida console,utopia; FONT-SIZE: 10pt; FONT-WEIGHT: bold }\n" +
" FONT.sub {\tBACKGROUND-COLOR: white; COLOR: #000000; FONT-FAMILY:tahoma,helvetica,verdana,lucida console,utopia; FONT-SIZE: 10pt }\n" +
@@ -48,11 +50,11 @@ public class HtmlContext {
"\n" +
"\n" +
"\n";
- private static final String HTML_TITLE = "" +
+ private static final String HTML_TITLE = "\n";
- private static final String HTML_FOOTER = "" +
+ private static final String HTML_FOOTER = "\n";
@@ -66,7 +68,7 @@ public class HtmlContext {
public static String DEFAULT_HTML() {
- return HTML_STRING("" +
+ return HTML_STRING("");
}
- public static String ListTableContext(Cursor cursor) {
+ /**
+ * 生成表格的内容
+ * @param sb html
+ * @param s 内容
+ * @return html
+ */
+ public static StringBuilder tableCell(StringBuilder sb, String... s){
+ for (String c: s) {
+ sb.append(String.format("%s ",c));
+ }
+ sb.append("\n");
+ return sb;
+ }
+
+ /**
+ * 生成表格标题
+ * @param sb html
+ * @param s 标题
+ * @return html
+ */
+ public static StringBuilder tableCellHeader(StringBuilder sb, String... s){
+ for (String c: s) {
+ sb.append(String.format("%s ",c));
+ }
+ sb.append("\n");
+ return sb;
+ }
+ @SuppressLint("DefaultLocale")
+ public static StringBuilder tableKeyRow(StringBuilder sb , Boolean darkMode, String key, int value){
+ sb.append(String.format("%s %d \n"
+ ,darkMode ? "class=\"bbb\"" : ""
+ ,key,value));
+ return sb;
+ }
+
+ public static StringBuilder tableKeyRow(StringBuilder sb ,Boolean darkMode,String key,String value){
+ sb.append(String.format("%s %s \n"
+ ,darkMode ? "class=\"bbb\"" : ""
+ ,key,value));
+ return sb;
+ }
+
+ public static StringBuilder tableRowBegin(StringBuilder sb){
+ return tableRowBegin(sb,false,false);
+ }
+ public static StringBuilder tableRowBegin(StringBuilder sb,Boolean alignCenter,boolean darkMode){
+ sb.append(String.format(""
+ ,alignCenter?"align=center":""
+ ,darkMode ? "class=\"bbb\"":""));
+ return sb;
+ }
+ public static StringBuilder tableRowEnd(StringBuilder sb){
+ sb.append(" ");
+ return sb;
+ }
+
+ public static StringBuilder tableBegin(StringBuilder sb){
+ return tableBegin(sb,false,1,true);
+ }
+ public static StringBuilder tableBegin(StringBuilder sb,boolean hasBorder,boolean fullWidth){
+ return tableBegin(sb,hasBorder,1,fullWidth);
+ }
+ @SuppressLint("DefaultLocale")
+ public static StringBuilder tableBegin(StringBuilder sb, boolean hasBorder, int cellpadding,boolean fullWidth){
+ sb.append(String.format(""
+ ,hasBorder ? "border=\"1\"" :""
+ ,cellpadding
+ ,fullWidth ? "width=\"100%\"":""));
+ return sb;
+ }
+
+ public static StringBuilder tableEnd(StringBuilder sb){
+ sb.append("
");
+ return sb;
+ }
+ public static String ListTableContext(Cursor cursor,boolean fullWidth){
+ return ListTableContext(cursor,false,0,fullWidth);
+ }
+ public static String ListTableContext(Cursor cursor,boolean hasBorder,int cellpadding ,boolean fullWidth) {
StringBuilder result = new StringBuilder();
- result.append("");
+ HtmlContext.tableBegin(result,hasBorder,cellpadding,fullWidth).append("\n");
//写字段名
- result.append("");
+ HtmlContext.tableRowBegin(result);
for (int i = 0; i < cursor.getColumnCount(); i++) {
- result.append("");
- result.append(cursor.getColumnName(i));
- result.append(" ");
+ HtmlContext.tableCellHeader(result,cursor.getColumnName(i));
}
- result.append(" \n");
+ HtmlContext.tableRowEnd(result).append("\n");
int order=0;
while (cursor.moveToNext()) {
- if (order%2==0) {
- result.append("");
- }else {
- result.append(" ");
- }
+ HtmlContext.tableRowBegin(result,false,order % 2 !=0);
for (int i = 0; i < cursor.getColumnCount(); i++) {
- result.append("");
- if (cursor.getString(i)!=null) {
- result.append(cursor.getString(i));
- }
- result.append(" ");
+ HtmlContext.tableCell(result,(cursor.getString(i)!=null) ? cursor.getString(i) :"");
}
- result.append(" \n");
+ HtmlContext.tableRowEnd(result).append("\n");
order++;
}
- result.append("
");
+ HtmlContext.tableEnd(result).append("\n");
cursor.close();
return result.toString();
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/html/LogHttpServer.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/html/LogHttpServer.java
old mode 100755
new mode 100644
index ea80ce8..49f32a4
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/html/LogHttpServer.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/html/LogHttpServer.java
@@ -1,6 +1,7 @@
package com.bg7yoz.ft8cn.html;
/**
* Http服务的具体内容。数据库访问不需要异步方式。
+ *
* @author BGY70Z
* @date 2023-03-20
*/
@@ -35,6 +36,7 @@ import java.util.Objects;
import fi.iki.elonen.NanoHTTPD;
+@SuppressWarnings("ConstantConditions")
public class LogHttpServer extends NanoHTTPD {
private final MainViewModel mainViewModel;
public static int DEFAULT_PORT = 7050;
@@ -70,12 +72,14 @@ public class LogHttpServer extends NanoHTTPD {
msg = HTML_STRING(getNewMessages());
} else if (uri.equalsIgnoreCase("MESSAGE")) {//查保存的SWL通联消息表
return getMessages(session);
- }else if (uri.equalsIgnoreCase("QSOSWLMSG")) {//查SWL QSO通联消息表
+ } else if (uri.equalsIgnoreCase("QSOSWLMSG")) {//查SWL QSO通联消息表
return getSWLQsoMessages(session);
+ } else if (uri.equalsIgnoreCase("QSOLogs")) {//查QSO日志
+ return getQsoLogs(session);
} else if (uri.equalsIgnoreCase("CALLSIGNGRID")) {//查呼号与网格的对应关系
msg = HTML_STRING(showCallGridList());
} else if (uri.equalsIgnoreCase("GETCALLSIGNQTH")) {
- msg = HTML_STRING(getCallsignQTH());
+ msg = HTML_STRING(getCallsignQTH(session));
} else if (uri.equalsIgnoreCase("ALLTABLE")) {//查所有的表
msg = HTML_STRING(getAllTableName());
} else if (uri.equalsIgnoreCase("FOLLOWCALLSIGNS")) {//查关注的呼号
@@ -151,9 +155,8 @@ public class LogHttpServer extends NanoHTTPD {
//判断是不是POST日志文件
if (session.getMethod().equals(Method.POST)
|| session.getMethod().equals(Method.PUT)) {
- //Log.e(TAG, "serve uri: --------->" + session.getUri());
- Map files = new HashMap();
- Map header = session.getHeaders();
+ Map files = new HashMap<>();
+ //Map header = session.getHeaders();
try {
session.parseBody(files);
String param = files.get("file1");//这个是post或put文件的key
@@ -183,9 +186,8 @@ public class LogHttpServer extends NanoHTTPD {
temp.append("
");
}
+ mainViewModel.databaseOpr.getQslDxccToMap();//更新一下已经通联的分区
return temp.toString();
-// return String.format(GeneralVariables.getStringFromResource(R.string.html_import_count) + " "
-// , recordList.size(), importCount,logFileImport.getErrorCount());
} catch (IOException | ResponseException e) {
e.printStackTrace();
return String.format(GeneralVariables.getStringFromResource(R.string.html_import_failed)
@@ -202,8 +204,8 @@ public class LogHttpServer extends NanoHTTPD {
*/
private String getConfig() {
Cursor cursor = mainViewModel.databaseOpr.getDb()
- .rawQuery("select * from config", null);
- return HtmlContext.ListTableContext(cursor);
+ .rawQuery("select KeyName,Value from config", null);
+ return HtmlContext.ListTableContext(cursor, true, 4, false);
}
/**
@@ -233,7 +235,7 @@ public class LogHttpServer extends NanoHTTPD {
"where q.[call] like ?\n" +
"group by q.[call] ,q.gridsquare,q.freq ,q.qso_date,q.time_on,q.band\n" +
"HAVING q.qso_date||q.time_on =MAX(q2.qso_date||q2.time_on) \n", new String[]{where});
- return html + HtmlContext.ListTableContext(cursor);
+ return html + HtmlContext.ListTableContext(cursor, true, 3, false);
}
@@ -245,51 +247,68 @@ public class LogHttpServer extends NanoHTTPD {
private String getAllTableName() {
Cursor cursor = mainViewModel.databaseOpr.getDb()
.rawQuery("select * from sqlite_master where type='table'", null);
- return HtmlContext.ListTableContext(cursor);
+ return HtmlContext.ListTableContext(cursor, true, 4, true);
}
@SuppressLint({"Range", "DefaultLocale"})
- private String getCallsignQTH() {
- Cursor cursor = mainViewModel.databaseOpr.getDb().rawQuery("select callsign ,grid,updateTime from CallsignQTH", null);
+ private String getCallsignQTH(IHTTPSession session) {
+ String callsign = "";
+ String grid = "";
+ //读取查询的参数
+ Map pars = session.getParms();
+
+ if (pars.get("callsign") != null) {
+ callsign = Objects.requireNonNull(pars.get("callsign"));
+ }
+ if (pars.get("grid") != null) {
+ grid = Objects.requireNonNull(pars.get("grid"));
+ }
+ String whereCallsign = String.format("%%%s%%", callsign.toUpperCase());
+ String whereGrid = String.format("%%%s%%", grid.toUpperCase());
+ Cursor cursor = mainViewModel.databaseOpr.getDb()
+ .rawQuery("select callsign ,grid,updateTime from CallsignQTH where (callsign like ?) and (grid like ?)"
+ , new String[]{whereCallsign, whereGrid});
StringBuilder result = new StringBuilder();
- result.append("");
+ HtmlContext.tableBegin(result, true, 2, false).append("\n");
+ result.append(String.format(" \n"
+ , GeneralVariables.getStringFromResource(R.string.html_callsign)
+ , callsign
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_grid)
+ , grid
+ , GeneralVariables.getStringFromResource(R.string.html_message_query)));
//写字段名
- result.append("");
+ HtmlContext.tableRowBegin(result).append("\n");
+ HtmlContext.tableCellHeader(result
+ , GeneralVariables.getStringFromResource(R.string.html_callsign)
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_grid)
+ , GeneralVariables.getStringFromResource(R.string.html_distance)
+ , GeneralVariables.getStringFromResource(R.string.html_update_time)
+ ).append("\n");
+ HtmlContext.tableRowEnd(result).append("\n");
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_callsign)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_qsl_grid)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_distance)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_update_time)));
- result.append(" \n");
@SuppressLint("SimpleDateFormat") SimpleDateFormat formatTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
int order = 0;
while (cursor.moveToNext()) {
- if (order % 2 == 0) {
- result.append("");
- } else {
- result.append(" ");
- }
-
- result.append("");
- result.append(String.format("%s %s \n"
- , cursor.getString(cursor.getColumnIndex("callsign"))
- , cursor.getString(cursor.getColumnIndex("grid"))));
- result.append(String.format("%s ",
- MaidenheadGrid.getDistStr(GeneralVariables.getMyMaidenhead4Grid()
- , cursor.getString(cursor.getColumnIndex("grid")))));
-
+ HtmlContext.tableRowBegin(result, true, order % 2 != 0);
Date date = new Date(cursor.getLong(cursor.getColumnIndex("updateTime")));
- result.append(String.format("%s ",
- formatTime.format(date)));
-
- result.append(" \n");
+ HtmlContext.tableCell(result
+ , cursor.getString(cursor.getColumnIndex("callsign"))
+ , cursor.getString(cursor.getColumnIndex("grid"))
+ , MaidenheadGrid.getDistStr(GeneralVariables.getMyMaidenhead4Grid()
+ , cursor.getString(cursor.getColumnIndex("grid")))
+ , formatTime.format(date)
+ ).append("\n");
+ HtmlContext.tableRowEnd(result).append("\n");
order++;
}
- result.append("
");
+ HtmlContext.tableEnd(result).append(" \n");
result.append(String.format("%d", order));
+ cursor.close();
return result.toString();
}
@@ -302,33 +321,32 @@ public class LogHttpServer extends NanoHTTPD {
private String getFollowCallsigns() {
Cursor cursor = mainViewModel.databaseOpr.getDb().rawQuery("select * from followCallsigns", null);
StringBuilder result = new StringBuilder();
- result.append("");
+ HtmlContext.tableBegin(result, true, 3, false).append("\n");
//写字段名
- result.append("");
+ HtmlContext.tableRowBegin(result).append("\n");
for (int i = 0; i < cursor.getColumnCount(); i++) {
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_callsign)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_operation)));
+ HtmlContext.tableCellHeader(result
+ , GeneralVariables.getStringFromResource(R.string.html_callsign)
+ , GeneralVariables.getStringFromResource(R.string.html_operation)).append("\n");
}
- result.append(" \n");
+ HtmlContext.tableRowEnd(result).append("\n");
int order = 0;
while (cursor.moveToNext()) {
- if (order % 2 == 0) {
- result.append("");
- } else {
- result.append(" ");
- }
+ HtmlContext.tableRowBegin(result, true, order % 2 != 0).append("\n");
for (int i = 0; i < cursor.getColumnCount(); i++) {
- result.append("");
- result.append(String.format("%s %s \n"
- , cursor.getString(i), cursor.getString(i).replace("/", "_")
- , GeneralVariables.getStringFromResource(R.string.html_delete)));
-
+ HtmlContext.tableCell(result
+ , cursor.getString(i)
+ , String.format("%s "
+ , cursor.getString(i).replace("/", "_")
+ , GeneralVariables.getStringFromResource(R.string.html_delete))
+ ).append("\n");
}
- result.append(" \n");
+ HtmlContext.tableRowEnd(result).append("\n");
order++;
}
- result.append("
");
+ HtmlContext.tableEnd(result).append("\n");
+ cursor.close();
return result.toString();
}
@@ -357,84 +375,49 @@ public class LogHttpServer extends NanoHTTPD {
Cursor cursor = mainViewModel.databaseOpr.getDb().rawQuery(
"select * from QslCallsigns order by ID desc", null);
StringBuilder result = new StringBuilder();
- result.append("\n");
+ HtmlContext.tableBegin(result, false, 0, true).append("\n");
//写字段名
- result.append("");
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_qsl_start_time)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_qsl_end_time)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_callsign)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_qsl_mode)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_qsl_grid)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_qsl_band)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_qsl_freq)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_qsl_manual_confirmation)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_qsl_lotw_confirmation)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_qsl_data_source)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_operation)));
- result.append(" \n");
+ HtmlContext.tableRowBegin(result).append("\n");
+ HtmlContext.tableCellHeader(result
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_start_time)
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_end_time)
+ , GeneralVariables.getStringFromResource(R.string.html_callsign)
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_mode)
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_grid)
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_band)
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_freq)
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_manual_confirmation)
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_lotw_confirmation)
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_data_source)
+ , GeneralVariables.getStringFromResource(R.string.html_operation)).append("\n");
+ HtmlContext.tableRowEnd(result).append("\n");
int order = 0;
while (cursor.moveToNext()) {
- if (order % 2 == 0) {
- result.append("");
- } else {
- result.append(" ");
- }
- result.append("");
- result.append(cursor.getString(cursor.getColumnIndex("startTime")));
- result.append(" ");
-
- result.append("");
- result.append(cursor.getString(cursor.getColumnIndex("finishTime")));
- result.append(" ");
-
- result.append("");
- result.append(cursor.getString(cursor.getColumnIndex("callsign")));
- result.append(" \n");
-
- result.append("");
- result.append(cursor.getString(cursor.getColumnIndex("mode")));
- result.append(" ");
- result.append("");
- result.append(cursor.getString(cursor.getColumnIndex("grid")));
- result.append(" ");
- result.append("");
- result.append(cursor.getString(cursor.getColumnIndex("band")));
- result.append(" ");
- result.append("");
- result.append(cursor.getString(cursor.getColumnIndex("band_i")));
- result.append("Hz ");
- result.append("");
- if (cursor.getInt(cursor.getColumnIndex("isQSL")) == 1) {
- result.append("√ ");
- } else {
- result.append("× ");
- }
- result.append(" ");
- result.append("");
- if (cursor.getInt(cursor.getColumnIndex("isLotW_QSL")) == 1) {
- result.append("√ ");
- } else {
- result.append("× ");
- }
- result.append(" ");
-
-
- result.append("");
- if (cursor.getInt(cursor.getColumnIndex("isLotW_import")) == 1) {
- result.append(GeneralVariables.getStringFromResource(R.string.html_qsl_import_data_from_external));
- } else {
- result.append(GeneralVariables.getStringFromResource(R.string.html_qsl_native_data));
- }
- result.append(" ");
-
- result.append(String.format("%s \n"
- , cursor.getString(cursor.getColumnIndex("ID"))
- , GeneralVariables.getStringFromResource(R.string.html_delete)));
- result.append(" \n");
+ HtmlContext.tableRowBegin(result, true, order % 2 != 0);
+ HtmlContext.tableCell(result
+ , cursor.getString(cursor.getColumnIndex("startTime"))
+ , cursor.getString(cursor.getColumnIndex("finishTime"))
+ , cursor.getString(cursor.getColumnIndex("callsign"))
+ , cursor.getString(cursor.getColumnIndex("mode"))
+ , cursor.getString(cursor.getColumnIndex("grid"))
+ , cursor.getString(cursor.getColumnIndex("band"))
+ , cursor.getString(cursor.getColumnIndex("band_i")) + "Hz"
+ , (cursor.getInt(cursor.getColumnIndex("isQSL")) == 1)
+ ? "√ " : "× "
+ , (cursor.getInt(cursor.getColumnIndex("isLotW_QSL")) == 1)
+ ? "√ " : "× "
+ , (cursor.getInt(cursor.getColumnIndex("isLotW_import")) == 1)
+ ? GeneralVariables.getStringFromResource(R.string.html_qsl_import_data_from_external)
+ : GeneralVariables.getStringFromResource(R.string.html_qsl_native_data)
+ , String.format("%s "
+ , cursor.getString(cursor.getColumnIndex("ID"))
+ , GeneralVariables.getStringFromResource(R.string.html_delete))).append("\n");
+ HtmlContext.tableRowEnd(result).append("\n");
order++;
}
- result.append("
");
+ HtmlContext.tableEnd(result).append("\n");
+ cursor.close();
return result.toString();
}
@@ -458,14 +441,14 @@ public class LogHttpServer extends NanoHTTPD {
"");
result.append(String.format(GeneralVariables.getStringFromResource(R.string.html_callsign_grid_total)
, GeneralVariables.callsignAndGrids.size()));
- //result.append("\n");
- result.append("\n");
- result.append("");
+ HtmlContext.tableBegin(result, true, 1, false);
+ HtmlContext.tableRowBegin(result);
result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_callsign)));
result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_qsl_grid)));
- result.append(" \n");
+ HtmlContext.tableRowEnd(result).append("\n");
+
result.append(GeneralVariables.getCallsignAndGridToHTML());
- result.append("
\n");
+ HtmlContext.tableEnd(result).append(" \n");
return result.toString();
}
@@ -482,266 +465,160 @@ public class LogHttpServer extends NanoHTTPD {
"function myrefresh(){\n" +
"window.location.reload();\n" +
"}\n" +
- "setTimeout('myrefresh()',5000); //指定5秒刷新一次,5000处可自定义设置,1000为1秒\n" +
+ "setTimeout('myrefresh()',5000);\n " +////指定5秒刷新一次,5000处可自定义设置,1000为1秒\n" +
"");
- result.append("\n");
- //result.append("\n");
- result.append("");
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_variable)));
- result.append(String.format("%s ", GeneralVariables.getStringFromResource(R.string.html_value)));
- result.append(" \n");
- result.append("");
- result.append("");
- result.append("UTC");
- result.append(" \n");
- result.append("");
- result.append(String.format("%s \n", UtcTimer.getTimeStr(mainViewModel.timerSec.getValue())));
- result.append(" \n");
+ HtmlContext.tableBegin(result, true, 5, false).append("\n");
+
+ HtmlContext.tableRowBegin(result);
+ HtmlContext.tableCellHeader(result
+ , GeneralVariables.getStringFromResource(R.string.html_variable)
+ , GeneralVariables.getStringFromResource(R.string.html_value)).append("\n");
+
+ HtmlContext.tableKeyRow(result, false, "UTC"
+ , UtcTimer.getTimeStr(mainViewModel.timerSec.getValue()));
+
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_my_callsign)
+ , GeneralVariables.myCallsign);
+
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.html_my_grid)
+ , GeneralVariables.getMyMaidenheadGrid());
+
+ HtmlContext.tableKeyRow(result, true//消息最大缓存条数
+ , GeneralVariables.getStringFromResource(R.string.html_max_message_cache)
+ , String.format("%d", GeneralVariables.MESSAGE_COUNT));
+
+ HtmlContext.tableKeyRow(result, false//音量大小
+ , GeneralVariables.getStringFromResource(R.string.signal_strength)
+ , String.format("%.0f%%\n", GeneralVariables.volumePercent * 100f));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_my_callsign));
- result.append(" \n");
- result.append("");
- result.append(String.format("%s \n", GeneralVariables.myCallsign));
- result.append(" \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_audio_bits)
+ , GeneralVariables.audioOutput32Bit ?
+ GeneralVariables.getStringFromResource(R.string.audio32_bit)
+ : GeneralVariables.getStringFromResource(R.string.audio16_bit));
+
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.html_audio_rate)
+ , String.format("%dHz", GeneralVariables.audioSampleRate));
+
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_decodes_in_this_cycle)
+ , String.format("%d", mainViewModel.currentDecodeCount));
+
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.decode_mode_text)
+ , GeneralVariables.deepDecodeMode
+ ? GeneralVariables.getStringFromResource(R.string.deep_mode)
+ : GeneralVariables.getStringFromResource(R.string.fast_mode));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_my_grid));
- result.append(" \n");
- result.append("");
- result.append(String.format("%s \n", GeneralVariables.getMyMaidenheadGrid()));
- result.append(" \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_total_number_of_decodes)
+ , String.format("%d", mainViewModel.ft8Messages.size()));
-
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_max_message_cache));
- result.append(" \n");
- result.append("");
- result.append(String.format("%d \n", GeneralVariables.MESSAGE_COUNT));//消息最大缓存条数
- result.append(" \n");
-
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.signal_strength));
- result.append(" \n");
- result.append("");
- result.append(String.format("%.0f%% \n", GeneralVariables.volumePercent * 100f));//音量大小
- result.append(" \n");
-
-
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_decodes_in_this_cycle));
- result.append(" \n");
- result.append("");
- result.append(String.format("%d \n", mainViewModel.currentDecodeCount));
- result.append(" \n");
-
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_total_number_of_decodes));
- result.append(" \n");
- result.append("");
- result.append(String.format("%d \n", mainViewModel.ft8Messages.size()));
- result.append(" \n");
-
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_in_recording_state));
- result.append(" \n");
- result.append("");
- result.append(String.format("%s \n",
- Boolean.TRUE.equals(mainViewModel.mutableIsRecording.getValue())
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.html_in_recording_state)
+ , Boolean.TRUE.equals(mainViewModel.mutableIsRecording.getValue())
? GeneralVariables.getStringFromResource(R.string.html_recording)
- : GeneralVariables.getStringFromResource(R.string.html_no_recording)));
- result.append(" \n");
+ : GeneralVariables.getStringFromResource(R.string.html_no_recording));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_time_consuming_for_this_decoding));
- result.append(" \n");
- result.append("");
- result.append(String.format(GeneralVariables.getStringFromResource(R.string.html_milliseconds)
- , mainViewModel.ft8SignalListener.decodeTimeSec.getValue()));
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_time_consuming_for_this_decoding)
+ , String.format(GeneralVariables.getStringFromResource(R.string.html_milliseconds)
+ , mainViewModel.ft8SignalListener.decodeTimeSec.getValue()));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_average_delay_time_of_this_cycle));
- result.append(" \n");
- result.append("");
- result.append(String.format(GeneralVariables.getStringFromResource(R.string.html_seconds)
- , mainViewModel.mutableTimerOffset.getValue()));
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.html_average_delay_time_of_this_cycle)
+ , String.format(GeneralVariables.getStringFromResource(R.string.html_seconds)
+ , mainViewModel.mutableTimerOffset.getValue()));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_sound_frequency));
- result.append(" \n");
- result.append("");
- result.append(String.format("%.0fHz", GeneralVariables.getBaseFrequency()));
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_sound_frequency)
+ , String.format("%.0fHz", GeneralVariables.getBaseFrequency()));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_transmission_delay_time));
- result.append(" \n");
- result.append("");
- result.append(String.format(GeneralVariables.getStringFromResource(R.string.html_milliseconds)
- , GeneralVariables.transmitDelay));
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.html_transmission_delay_time)
+ , String.format(GeneralVariables.getStringFromResource(R.string.html_milliseconds)
+ , GeneralVariables.transmitDelay));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_launch_supervision));
- result.append(" \n");
- result.append("");
- result.append(String.format(GeneralVariables.getStringFromResource(R.string.html_milliseconds)
- , GeneralVariables.launchSupervision));
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_launch_supervision)
+ , String.format(GeneralVariables.getStringFromResource(R.string.html_milliseconds)
+ , GeneralVariables.launchSupervision));
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.html_automatic_program_run_time)
+ , String.format(GeneralVariables.getStringFromResource(R.string.html_milliseconds)
+ , GeneralVariables.launchSupervisionCount()));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_automatic_program_run_time));
- result.append(" \n");
- result.append("");
- result.append(String.format(GeneralVariables.getStringFromResource(R.string.html_milliseconds)
- , GeneralVariables.launchSupervisionCount()));
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_no_reply_limit)
+ , GeneralVariables.noReplyLimit);
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_no_reply_limit));
- result.append(" \n");
- result.append("");
- result.append(GeneralVariables.noReplyLimit);
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.html_no_reply_count)
+ , GeneralVariables.noReplyCount);
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_no_reply_count));
- result.append(" \n");
- result.append("");
- result.append(GeneralVariables.noReplyCount);
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.follow_cq)
+ , String.valueOf(GeneralVariables.autoFollowCQ));
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.auto_call_follow)
+ , String.valueOf(GeneralVariables.autoCallFollow));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.follow_cq));
- result.append(" \n");
- result.append("");
- result.append(GeneralVariables.autoFollowCQ);
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_target_callsign)
+ , (mainViewModel.ft8TransmitSignal.mutableToCallsign.getValue() != null)
+ ? mainViewModel.ft8TransmitSignal.mutableToCallsign.getValue().callsign : "");
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.auto_call_follow));
- result.append(" \n");
- result.append("");
- result.append(GeneralVariables.autoCallFollow);
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.html_sequential)
+ , mainViewModel.ft8TransmitSignal.sequential);
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.synFrequency)
+ , String.valueOf(GeneralVariables.synFrequency));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_target_callsign));
- result.append(" \n");
- result.append("");
- if (mainViewModel.ft8TransmitSignal.mutableToCallsign.getValue() != null) {
- result.append(mainViewModel.ft8TransmitSignal.mutableToCallsign.getValue().callsign);
- } else {
- result.append("-");
- }
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.tran_delay)
+ , GeneralVariables.transmitDelay);
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_sequential));
- result.append(" \n");
- result.append("");
- result.append(mainViewModel.ft8TransmitSignal.sequential);
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.ptt_delay)
+ , GeneralVariables.pttDelay);
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.rig_name)
+ , RigNameList.getInstance(
+ GeneralVariables.getMainContext()).getRigNameByIndex(GeneralVariables.modelNo).modelName);
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.synFrequency));
- result.append(" \n");
- result.append("");
- result.append(GeneralVariables.synFrequency);
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_mark_message)
+ , String.format("%s", mainViewModel.markMessage
+ ? GeneralVariables.getStringFromResource(R.string.html_marking_message)
+ : GeneralVariables.getStringFromResource(R.string.html_do_not_mark_message)));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.tran_delay));
- result.append(" \n");
- result.append("");
- result.append(GeneralVariables.transmitDelay);
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.html_operation_mode)
+ , ControlMode.getControlModeStr(GeneralVariables.controlMode));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.ptt_delay));
- result.append(" \n");
- result.append("");
- result.append(GeneralVariables.pttDelay);
- result.append(" \n \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_civ_address)
+ , String.format("0x%2X", GeneralVariables.civAddress));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.rig_name));
- result.append(" \n");
- result.append("");
- result.append(RigNameList.getInstance(
- GeneralVariables.getMainContext()).getRigNameByIndex(GeneralVariables.modelNo).modelName);
- result.append(" \n \n");
-
-
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_mark_message));
- result.append(" \n");
- result.append("");
- result.append(String.format("%s", mainViewModel.markMessage
- ? GeneralVariables.getStringFromResource(R.string.html_marking_message)
- : GeneralVariables.getStringFromResource(R.string.html_do_not_mark_message)));
- result.append(" \n \n");
-
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_operation_mode));
- result.append(" \n");
- result.append(String.format("%s "
- , ControlMode.getControlModeStr(GeneralVariables.controlMode)));
- result.append(" \n");
-
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_civ_address));
- result.append(" \n");
- result.append(String.format("0x%2X ", GeneralVariables.civAddress));
- result.append(" \n");
-
- result.append(">");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_baud_rate));
- result.append(" \n");
- result.append(String.format("%d ", GeneralVariables.baudRate));
- result.append(" \n");
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.html_baud_rate)
+ , GeneralVariables.baudRate);
result.append("");
result.append("");
result.append(GeneralVariables.getStringFromResource(R.string.html_available_serial_ports));
result.append(" \n");
-
result.append("");
if (mainViewModel.mutableSerialPorts != null) {
if (mainViewModel.mutableSerialPorts.getValue().size() == 0) {
@@ -749,123 +626,57 @@ public class LogHttpServer extends NanoHTTPD {
}
}
for (CableSerialPort.SerialPort serialPort : Objects.requireNonNull(mainViewModel.mutableSerialPorts.getValue())) {
- result.append(serialPort.information() + " \n");
- }
- result.append(" ");
- result.append(" \n");
-
- result.append(">");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_instruction_set));
- result.append(" \n");
-
- result.append("");
- if (mainViewModel.baseRig != null) {
- result.append(mainViewModel.baseRig.getName());
- } else {
- result.append("-");
- }
- result.append(" ");
- result.append(" \n");
-
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_connect_mode));
- result.append(" \n");
- if (GeneralVariables.controlMode == ControlMode.VOX) {
- result.append("- ");
- } else {
- result.append(String.format("%s "
- , ConnectMode.getModeStr(GeneralVariables.connectMode)));
- }
- result.append(" \n");
-
-
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_baud_rate));
- result.append(" \n");
- result.append(String.format("%d ", GeneralVariables.baudRate));
- result.append(" \n");
-
-
- result.append(">");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_carrier_frequency_band));
- result.append(" \n");
- result.append("");
- result.append(GeneralVariables.getBandString());
- result.append(" ");
- result.append(" \n");
-
-
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_radio_frequency));
- result.append(" \n");
- result.append("");
- if (mainViewModel.baseRig != null) {
- result.append(BaseRigOperation.getFrequencyStr(mainViewModel.baseRig.getFreq()));
- } else {
- result.append("-");
+ result.append(serialPort.information()).append(" \n");
}
result.append(" ");
result.append(" \n");
- result.append(">");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_flex_max_rf_power));
- result.append(" \n");
- result.append("");
- result.append(String.format("%d W", GeneralVariables.flexMaxRfPower));
- result.append(" ");
- result.append(" \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_instruction_set)
+ , (mainViewModel.baseRig != null) ? mainViewModel.baseRig.getName() : "-");
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.html_connect_mode)
+ , (GeneralVariables.controlMode == ControlMode.VOX) ? "-"
+ : ConnectMode.getModeStr(GeneralVariables.connectMode));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.html_atu_tune_power));
- result.append(" \n");
- result.append("");
- result.append(String.format("%d W", GeneralVariables.flexMaxTunePower));
- result.append(" ");
- result.append(" \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_baud_rate)
+ , GeneralVariables.baudRate);
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.html_carrier_frequency_band)
+ , GeneralVariables.getBandString());
- result.append(">");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.be_excluded_callsigns));
- result.append(" \n");
- result.append("");
- result.append(GeneralVariables.getExcludeCallsigns());
- result.append(" ");
- result.append(" \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_radio_frequency)
+ , (mainViewModel.baseRig != null)
+ ? BaseRigOperation.getFrequencyStr(mainViewModel.baseRig.getFreq()) : "-");
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.html_flex_max_rf_power)
+ , String.format("%d W", GeneralVariables.flexMaxRfPower));
- result.append("");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.config_save_swl));
- result.append(" \n");
- result.append("");
- result.append(GeneralVariables.saveSWLMessage);
- result.append(" ");
- result.append(" \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.html_atu_tune_power)
+ , String.format("%d W", GeneralVariables.flexMaxTunePower));
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.be_excluded_callsigns)
+ , GeneralVariables.getExcludeCallsigns());
- result.append(">");
- result.append("");
- result.append(GeneralVariables.getStringFromResource(R.string.config_save_swl_qso));
- result.append(" \n");
- result.append("");
- result.append(GeneralVariables.saveSWL_QSO);
- result.append(" ");
- result.append(" \n");
+ HtmlContext.tableKeyRow(result, true
+ , GeneralVariables.getStringFromResource(R.string.config_save_swl)
+ , String.valueOf(GeneralVariables.saveSWLMessage));
- result.append("
");
+ HtmlContext.tableKeyRow(result, false
+ , GeneralVariables.getStringFromResource(R.string.config_save_swl_qso)
+ , String.valueOf(GeneralVariables.saveSWL_QSO));
+ HtmlContext.tableEnd(result).append(" \n");
- result.append("\n");
+ HtmlContext.tableBegin(result, false, 0, true).append("\n");
result.append(String.format("%s \n"
, String.format(GeneralVariables.getStringFromResource(R.string.html_successful_callsign)
, GeneralVariables.getBandString())));
@@ -875,13 +686,13 @@ public class LogHttpServer extends NanoHTTPD {
result.append(GeneralVariables.QSL_Callsign_list.get(i));
result.append(", ");
if (((i + 1) % 10) == 0) {
- result.append("");
+ result.append(" \n");
}
}
- result.append("
\n");
+ result.append("\n");
+ HtmlContext.tableEnd(result).append(" \n");
-
- result.append("\n");
+ HtmlContext.tableBegin(result, false, 0, true).append("\n");
result.append(String.format("%s \n"
, GeneralVariables.getStringFromResource(R.string.html_tracking_callsign)));
@@ -890,19 +701,20 @@ public class LogHttpServer extends NanoHTTPD {
result.append(GeneralVariables.followCallsign.get(i));
result.append(", ");
if (((i + 1) % 10) == 0) {
- result.append("");
+ result.append(" \n");
}
}
- result.append("
\n");
+ result.append("\n");
+ HtmlContext.tableEnd(result).append("\n");
- result.append("\n");
+ HtmlContext.tableBegin(result, false, 0, true).append("\n");
result.append(String.format("%s \n"
, GeneralVariables.getStringFromResource(R.string.html_tracking_qso_information)));
result.append("");
result.append(GeneralVariables.qslRecordList.toHTML());
- result.append("
\n");
- // result.append("
\n");
+ result.append("\n");
+ HtmlContext.tableEnd(result).append("\n");
return result.toString();
}
@@ -920,40 +732,26 @@ public class LogHttpServer extends NanoHTTPD {
"}\n" +
"setTimeout('myrefresh()',5000); //指定5秒刷新一次,5000处可自定义设置,1000为1秒\n" +
"");
- //result.append("\n");
- result.append("\n");
- result.append("");
- result.append(String.format("%s "
- , GeneralVariables.getStringFromResource(R.string.html_callsign)));
- result.append(String.format("%s "
- , GeneralVariables.getStringFromResource(R.string.html_hash_value)));
- result.append(" \n");
+ HtmlContext.tableBegin(result, true, 3, false).append("\n");
+ HtmlContext.tableRowBegin(result);
+ //表头
+ HtmlContext.tableCellHeader(result, GeneralVariables.getStringFromResource(R.string.html_callsign)
+ , GeneralVariables.getStringFromResource(R.string.html_hash_value));
+
+ HtmlContext.tableRowEnd(result).append("\n");
+
int order = 0;
for (Map.Entry entry : Ft8Message.hashList.entrySet()) {
- if ((order / 3) % 2 == 0) {
- result.append("");
- } else {
- result.append(" ");
- }
- result.append(String.format(" %s ", entry.getValue()));
- result.append(String.format(" 0x%x ", entry.getKey()));
- result.append(" \n");
+ HtmlContext.tableRowBegin(result, false, (order / 3) % 2 != 0);
+
+ HtmlContext.tableCell(result, entry.getValue());
+ HtmlContext.tableCell(result, String.format(" 0x%x ", entry.getKey()));
+ HtmlContext.tableRowEnd(result).append("\n");
+
order++;
}
-// for (HashStruct hash : Ft8Message.hashList) {
-// if ((order / 3) % 2 == 0) {
-// result.append("");
-// } else {
-// result.append(" ");
-// }
-// result.append(String.format("%s ", hash.callsign));
-// result.append(String.format("0x%x ", hash.hashCode));
-// result.append(" \n");
-// order++;
-// }
-
- result.append("
");
+ HtmlContext.tableEnd(result).append("\n");
return result.toString();
}
@@ -977,11 +775,11 @@ public class LogHttpServer extends NanoHTTPD {
.replace(".", "_")
.replace(">", "_"));
}
- if (start_date.length()>0){
- fileName.append(String.format("_%s",start_date));
+ if (start_date.length() > 0) {
+ fileName.append(String.format("_%s", start_date));
}
- if (end_date.length()>0){
- fileName.append(String.format("_%s",end_date));
+ if (end_date.length() > 0) {
+ fileName.append(String.format("_%s", end_date));
}
fileName.append(".").append(exportFile);
@@ -998,18 +796,18 @@ public class LogHttpServer extends NanoHTTPD {
}
String whereStr = String.format("%%%s%%", callsign);
cursor = mainViewModel.databaseOpr.getDb().rawQuery(
- "select * from SWLMessages where ((CALL_TO LIKE ?)OR(CALL_FROM LIKE ?)) " +
- dateSql.toString() +
- " order by ID "
- , new String[]{whereStr, whereStr});
+ "select * from SWLMessages where ((CALL_TO LIKE ?)OR(CALL_FROM LIKE ?)) " +
+ dateSql +
+ " order by ID "
+ , new String[]{whereStr, whereStr});
- StringBuilder result=new StringBuilder();
+ StringBuilder result = new StringBuilder();
String formatStr;
- if (exportFile.equalsIgnoreCase("CSV")){
- formatStr="%s,%.3f,Rx,%s,%d,%.1f,%d,%s\n";
- }else {
- formatStr="%s %12.3f Rx %s %6d %4.1f %4d %s\n";
+ if (exportFile.equalsIgnoreCase("CSV")) {
+ formatStr = "%s,%.3f,Rx,%s,%d,%.1f,%d,%s\n";
+ } else {
+ formatStr = "%s %12.3f Rx %s %6d %4.1f %4d %s\n";
}
while (cursor.moveToNext()) {
@@ -1024,15 +822,14 @@ public class LogHttpServer extends NanoHTTPD {
long band = cursor.getLong(cursor.getColumnIndex("BAND"));
result.append(String.format(formatStr
- ,utcTime,(band/1000f/1000f),protocol,dB,dt,freq,String.format("%s %s %s",callTo,callFrom,extra)));
+ , utcTime, (band / 1000f / 1000f), protocol, dB, dt, freq, String.format("%s %s %s", callTo, callFrom, extra)));
}
cursor.close();
-
response = newFixedLengthResponse(NanoHTTPD.Response.Status.OK, "text/plain", result.toString());
response.addHeader("Content-Disposition"
- , String.format("attachment;filename=%s",fileName.toString()));
+ , String.format("attachment;filename=%s", fileName));
return response;
}
@@ -1083,10 +880,14 @@ public class LogHttpServer extends NanoHTTPD {
return exportSWLMessage(exportFile, callsign, startDate, endDate);
}
+ HtmlContext.tableBegin(result, false, 0, true).append("\n");
+ HtmlContext.tableRowBegin(result).append("");
+
+
result.append(String.format("%s " +
" , %s "
- , callsign, startDate, endDate,GeneralVariables.getStringFromResource(R.string.html_export_csv)
- , callsign, startDate, endDate,GeneralVariables.getStringFromResource(R.string.html_export_text)));
+ , callsign, startDate, endDate, GeneralVariables.getStringFromResource(R.string.html_export_csv)
+ , callsign, startDate, endDate, GeneralVariables.getStringFromResource(R.string.html_export_text)));
Cursor cursor;
StringBuilder dateSql = new StringBuilder();
@@ -1101,7 +902,7 @@ public class LogHttpServer extends NanoHTTPD {
//计算总的记录数
cursor = mainViewModel.databaseOpr.getDb().rawQuery(
"select count(*) as rc from SWLMessages " +
- "where ((CALL_TO LIKE ?)OR(CALL_FROM LIKE ?))" + dateSql.toString()
+ "where ((CALL_TO LIKE ?)OR(CALL_FROM LIKE ?))" + dateSql
, new String[]{whereStr, whereStr});
cursor.moveToFirst();
int pageCount = Math.round(((float) cursor.getInt(cursor.getColumnIndex("rc")) / pageSize) + 0.5f);
@@ -1112,19 +913,20 @@ public class LogHttpServer extends NanoHTTPD {
result.append(String.format(" ");
+ HtmlContext.tableRowEnd(result);
+ HtmlContext.tableEnd(result).append("\n");
cursor = mainViewModel.databaseOpr.getDb().rawQuery(
String.format(
"select * from SWLMessages where ((CALL_TO LIKE ?)OR(CALL_FROM LIKE ?)) " +
- dateSql.toString() +
+ dateSql +
" order by ID LIMIT(%d),%d "
, (pageIndex - 1) * pageSize, pageSize), new String[]{whereStr, whereStr});
- result.append("\n");
- result.append("");
- result.append("No. ");
- result.append("Protocol ");
- result.append("i3.n3 ");
- result.append("UTC ");
- result.append("dB ");
- result.append("Dt ");
- result.append(String.format("%s "
- , GeneralVariables.getStringFromResource(R.string.html_qsl_freq)));
- result.append(String.format("%s "
- , GeneralVariables.getStringFromResource(R.string.message)));
- result.append(String.format("%s "
- , GeneralVariables.getStringFromResource(R.string.html_carrier_frequency_band)));
- result.append(" \n");
+ //result.append("\n");
+ HtmlContext.tableBegin(result, false, true).append("\n");
+ HtmlContext.tableRowBegin(result).append("\n");
+ HtmlContext.tableCellHeader(result, "No.");
+ HtmlContext.tableCellHeader(result, GeneralVariables.getStringFromResource(R.string.html_protocol));
+ HtmlContext.tableCellHeader(result, "i3.n3", "UTC", "dB", "Δt");
+ HtmlContext.tableCellHeader(result, GeneralVariables.getStringFromResource(R.string.html_qsl_freq));
+ HtmlContext.tableCellHeader(result, GeneralVariables.getStringFromResource(R.string.message));
+ HtmlContext.tableCellHeader(result, GeneralVariables.getStringFromResource(R.string.html_carrier_frequency_band)).append("\n");
+ HtmlContext.tableRowEnd(result).append("\n");
+
+
int order = 0;
while (cursor.moveToNext()) {
- if (order % 2 == 0) {
- result.append("");
- } else {
- result.append(" ");
- }
+ HtmlContext.tableRowBegin(result, true, order % 2 != 0);
- //int id = cursor.getInt(cursor.getColumnIndex("ID"));
int i3 = cursor.getInt(cursor.getColumnIndex("I3"));
int n3 = cursor.getInt(cursor.getColumnIndex("N3"));
String utcTime = cursor.getString(cursor.getColumnIndex("UTC"));
@@ -1184,35 +980,30 @@ public class LogHttpServer extends NanoHTTPD {
String extra = cursor.getString(cursor.getColumnIndex("EXTRAL"));
long band = cursor.getLong(cursor.getColumnIndex("BAND"));
- //UtcTimer.getDatetimeStr(utcTime)
- result.append(String.format("%d " +
- "%s " +//protocol
- "%s " +
- "%s %d %.1f %dHz %s " +
- "%s "
- , order + 1 + pageSize * (pageIndex - 1)
- , protocol
- , Ft8Message.getCommandInfoByI3N3(i3, n3)
- , utcTime
- , dB, dt, freq, String.format("" +
- "%s " +
- "%s %s"
- , pageSize, callTo.replace("<", "")
- .replace(">", "")
- , callTo.replace("<", "<")
- .replace(">", ">")
- , pageSize, callFrom.replace("<", "")
- .replace(">", "")
- , callFrom.replace("<", "<")
- .replace(">", ">"), extra)
+ HtmlContext.tableCell(result, String.format("%d", order + 1 + pageSize * (pageIndex - 1)));
+ HtmlContext.tableCell(result, protocol, Ft8Message.getCommandInfoByI3N3(i3, n3));
+ HtmlContext.tableCell(result, utcTime);
+ HtmlContext.tableCell(result, String.format("%d", dB));
+ HtmlContext.tableCell(result, String.format("%.1f", dt));
+ HtmlContext.tableCell(result, String.format("%dHz", freq));
+ HtmlContext.tableCell(result, String.format("" +
+ "%s " +
+ "%s %s ", pageSize, callTo.replace("<", "")
+ .replace(">", "")
+ , callTo.replace("<", "<")
+ .replace(">", ">")
+ , pageSize, callFrom.replace("<", "")
+ .replace(">", "")
+ , callFrom.replace("<", "<")
+ .replace(">", ">"), extra));
+ HtmlContext.tableCell(result, BaseRigOperation.getFrequencyStr(band)).append("\n");
+ HtmlContext.tableRowEnd(result).append("\n");
- , BaseRigOperation.getFrequencyStr(band)));
- //result.append(UtcTimer.getDatetimeStr(utcTime));
- result.append(" \n");
order++;
}
cursor.close();
- result.append("
");
+ HtmlContext.tableEnd(result).append(" \n");
+ //result.append("
");
return newFixedLengthResponse(HtmlContext.HTML_STRING(result.toString()));
@@ -1221,7 +1012,14 @@ public class LogHttpServer extends NanoHTTPD {
}
-
+ /**
+ * 把swo的QSO日志导出到文件
+ * @param exportFile 文件名
+ * @param callsign 呼号
+ * @param start_date 起始日期
+ * @param end_date 结束日期
+ * @return 数据
+ */
@SuppressLint("Range")
private Response exportSWLQSOMessage(String exportFile, String callsign, String start_date, String end_date) {
Response response;
@@ -1241,11 +1039,11 @@ public class LogHttpServer extends NanoHTTPD {
.replace(".", "_")
.replace(">", "_"));
}
- if (start_date.length()>0){
- fileName.append(String.format("_%s",start_date));
+ if (start_date.length() > 0) {
+ fileName.append(String.format("_%s", start_date));
}
- if (end_date.length()>0){
- fileName.append(String.format("_%s",end_date));
+ if (end_date.length() > 0) {
+ fileName.append(String.format("_%s", end_date));
}
fileName.append(".").append(exportFile);
@@ -1265,21 +1063,24 @@ public class LogHttpServer extends NanoHTTPD {
cursor = mainViewModel.databaseOpr.getDb().rawQuery(
String.format(
"select * from SWLQSOTable where (([call] LIKE ?)OR(station_callsign LIKE ?)) " +
- dateSql.toString() +
- " order by ID "),new String[]{whereStr, whereStr});
+ dateSql +
+ " order by qso_date desc,time_on desc "), new String[]{whereStr, whereStr});
response = newFixedLengthResponse(NanoHTTPD.Response.Status.OK, "text/plain"
- , downQSLTable(cursor,true));
+ , downQSLTable(cursor, true));
response.addHeader("Content-Disposition"
- , String.format("attachment;filename=%s",fileName.toString()));
+ , String.format("attachment;filename=%s", fileName));
return response;
}
-
-
+ /**
+ * 查询SWL日志
+ * @param session 会话
+ * @return html
+ */
@SuppressLint({"DefaultLocale", "Range"})
private Response getSWLQsoMessages(IHTTPSession session) {
int pageSize = 100;
@@ -1320,8 +1121,12 @@ public class LogHttpServer extends NanoHTTPD {
return exportSWLQSOMessage(exportFile, callsign, startDate, endDate);
}
+ HtmlContext.tableBegin(result, false, 0, true).append("\n");
+ HtmlContext.tableRowBegin(result).append("\n");
+ result.append("");
+
result.append(String.format("%s "
- , callsign, startDate, endDate,GeneralVariables.getStringFromResource(R.string.html_export_adi)));
+ , callsign, startDate, endDate, GeneralVariables.getStringFromResource(R.string.html_export_adi)));
Cursor cursor;
StringBuilder dateSql = new StringBuilder();
@@ -1336,7 +1141,7 @@ public class LogHttpServer extends NanoHTTPD {
//计算总的记录数
cursor = mainViewModel.databaseOpr.getDb().rawQuery(
"select count(*) as rc from SWLQSOTable " +
- "where (([call] LIKE ?)OR(station_callsign LIKE ?))" + dateSql.toString()
+ "where (([call] LIKE ?)OR(station_callsign LIKE ?))" + dateSql
, new String[]{whereStr, whereStr});
cursor.moveToFirst();
int pageCount = Math.round(((float) cursor.getInt(cursor.getColumnIndex("rc")) / pageSize) + 0.5f);
@@ -1347,19 +1152,20 @@ public class LogHttpServer extends NanoHTTPD {
result.append(String.format("\n"
+
, 1, pageSize, callsign, startDate, endDate
, pageIndex - 1 == 0 ? 1 : pageIndex - 1, pageSize, callsign, startDate, endDate
, pageIndex
, pageIndex == pageCount ? pageCount : pageIndex + 1, pageSize, callsign, startDate, endDate
, pageCount, pageSize, callsign, startDate, endDate));
-
+ result.append(" ");
+ HtmlContext.tableRowEnd(result);
+ HtmlContext.tableEnd(result).append("\n");
cursor = mainViewModel.databaseOpr.getDb().rawQuery(
String.format(
"select * from SWLQSOTable where (([call] LIKE ?)OR(station_callsign LIKE ?)) " +
- dateSql.toString() +
- " order by ID LIMIT(%d),%d "
+ dateSql +
+ " order by qso_date desc,time_on desc LIMIT(%d),%d "
, (pageIndex - 1) * pageSize, pageSize), new String[]{whereStr, whereStr});
- result.append("\n");
- result.append("");
- result.append("No. ");
- result.append("call ");
- result.append("gridsquare ");
- result.append("mode ");
- result.append("rst_sent ");
- result.append("rst_rcvd ");
- result.append("qso_date ");
- result.append("time_on ");
- result.append("qso_date_off ");
- result.append("time_off ");
- result.append("band ");
- result.append("freq ");
- result.append("station_callsign ");
- result.append("my_gridsquare ");
- result.append("comment ");
- result.append(" \n");
+ HtmlContext.tableBegin(result, false, true).append("\n");
+
+ HtmlContext.tableRowBegin(result);
+ HtmlContext.tableCellHeader(result, "No."
+ , GeneralVariables.getStringFromResource(R.string.html_callsign)//"call"
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_grid)//"gridsquare"
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_mode)//"mode"
+ , GeneralVariables.getStringFromResource(R.string.html_rst_sent)//"rst_sent"
+ , GeneralVariables.getStringFromResource(R.string.html_rst_rcvd)//"rst_rcvd"
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_start_day)//"qso date"
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_start_time)//"time_on"
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_end_date)//qso date off
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_end_time)//"time_off"
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_band)//"band"
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_freq)//"freq"
+ , GeneralVariables.getStringFromResource(R.string.html_callsign)//"station_callsign"
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_grid)//"my_gridsquare"
+ , GeneralVariables.getStringFromResource(R.string.html_comment))//"comment")
+ .append("\n");
+ HtmlContext.tableRowEnd(result).append("\n");
int order = 0;
while (cursor.moveToNext()) {
- if (order % 2 == 0) {
- result.append("");
- } else {
- result.append(" ");
- }
+ HtmlContext.tableRowBegin(result, true, order % 2 != 0).append("\n");
- //int id = cursor.getInt(cursor.getColumnIndex("ID"));
String call = cursor.getString(cursor.getColumnIndex("call"));
String gridsquare = cursor.getString(cursor.getColumnIndex("gridsquare"));
String mode = cursor.getString(cursor.getColumnIndex("mode"));
@@ -1426,58 +1232,350 @@ public class LogHttpServer extends NanoHTTPD {
String comment = cursor.getString(cursor.getColumnIndex("comment"));
- //UtcTimer.getDatetimeStr(utcTime)
- result.append(String.format("%d " +
- "%s "+//call
- "%s " +//gridsquare
- "%s " +//mode
- "%s " +//rst_sent
- "%s " +//rst_rcvd
- "%s " +//qso_date
- "%s " +//time_on
- "%s " +//qso_date_off
- "%s " +//time_off
- "%s " +//band
- "%s " +//freq
- "%s "+//station_callsign
- "%s " +//my_gridsquare
- "%s " //comment
-
- , order + 1 + pageSize * (pageIndex - 1)
-
- ,pageSize
- ,call.replace("<", "")
+ //生成数据表的一行
+ HtmlContext.tableCell(result, String.format("%d", order + 1 + pageSize * (pageIndex - 1)));
+ HtmlContext.tableCell(result, String.format("%s "
+ , pageSize, call.replace("<", "")
.replace(">", "")
, call.replace("<", "<")
- .replace(">", ">")
-
- ,gridsquare==null?"":gridsquare
- ,mode,rst_sent,rst_rcvd
- ,qso_date,time_on,qso_date_off,time_off
- ,band,freq
-
- ,pageSize
- ,station_callsign.replace("<", "")
+ .replace(">", ">")));
+ HtmlContext.tableCell(result, gridsquare == null ? "" : gridsquare);
+ HtmlContext.tableCell(result, mode, rst_sent, rst_rcvd, qso_date, time_on, qso_date_off, time_off);
+ HtmlContext.tableCell(result, band, freq);
+ HtmlContext.tableCell(result, String.format("%s "
+ , pageSize
+ , station_callsign.replace("<", "")
.replace(">", "")
, station_callsign.replace("<", "<")
- .replace(">", ">")
+ .replace(">", ">")));
+ HtmlContext.tableCell(result, my_gridsquare == null ? "" : my_gridsquare
+ , comment).append("\n");
- ,my_gridsquare==null?"":my_gridsquare
- ,comment ));
- //result.append(UtcTimer.getDatetimeStr(utcTime));
- result.append(" \n");
+ HtmlContext.tableRowEnd(result).append("\n");
order++;
}
cursor.close();
- result.append("
");
-
+ HtmlContext.tableEnd(result).append(" \n");
return newFixedLengthResponse(HtmlContext.HTML_STRING(result.toString()));
- //return result.toString();
-
}
+ /**
+ * 把QSO日志导出到文件
+ * @param exportFile 文件名
+ * @param callsign 呼号
+ * @param start_date 起始日期
+ * @param end_date 结束日期
+ * @return 数据
+ */
+ @SuppressLint("Range")
+ private Response exportQSOLogs(String exportFile, String callsign, String start_date, String end_date, String extWhere) {
+ Response response;
+ StringBuilder fileName = new StringBuilder();
+ fileName.append("qso_log");
+ if (callsign.length() > 0) {
+ fileName.append("_");
+ fileName.append(callsign.replace("/", "_")
+ .replace("\\", "_")
+ .replace(":", "_")
+ .replace("?", "_")
+ .replace("*", "_")
+ .replace("|", "_")
+ .replace("\"", "_")
+ .replace("'", "_")
+ .replace("<", "_")
+ .replace(".", "_")
+ .replace(">", "_"));
+ }
+ if (start_date.length() > 0) {
+ fileName.append(String.format("_%s", start_date));
+ }
+ if (end_date.length() > 0) {
+ fileName.append(String.format("_%s", end_date));
+ }
+ fileName.append(".").append(exportFile);
+
+
+ Cursor cursor;
+ String whereStr = String.format("%%%s%%", callsign);
+
+ cursor = mainViewModel.databaseOpr.getDb().rawQuery(
+ String.format(
+ "select * from QSLTable where (([call] LIKE ?)OR(station_callsign LIKE ?)) " +
+ extWhere +
+ " order by qso_date desc,time_on desc "), new String[]{whereStr, whereStr});
+
+
+ response = newFixedLengthResponse(NanoHTTPD.Response.Status.OK, "text/plain"
+ , downQSLTable(cursor, false));
+ response.addHeader("Content-Disposition"
+ , String.format("attachment;filename=%s", fileName));
+
+ return response;
+ }
+
+ /**
+ * 查询QSO日志
+ * @param session 会话
+ * @return html
+ */
+ @SuppressLint({"DefaultLocale", "Range"})
+ private Response getQsoLogs(IHTTPSession session) {
+ int pageSize = 100;
+ String callsign = "";
+
+
+ StringBuilder result = new StringBuilder();
+ String startDate = "";
+ String endDate = "";
+ String exportFile = "";
+ String qIsQSL = "";
+ String qIsImported = "";
+
+ //读取查询的参数
+ Map pars = session.getParms();
+ int pageIndex = 1;
+ if (pars.get("page") != null) {
+ pageIndex = Integer.parseInt(Objects.requireNonNull(pars.get("page")));
+ }
+ if (pars.get("pageSize") != null) {
+ pageSize = Integer.parseInt(Objects.requireNonNull(pars.get("pageSize")));
+ }
+ if (pars.get("callsign") != null) {
+ callsign = Objects.requireNonNull(pars.get("callsign"));
+ }
+ if (pars.get("start_date") != null) {
+ startDate = Objects.requireNonNull(pars.get("start_date"));
+ }
+ if (pars.get("end_date") != null) {
+ endDate = Objects.requireNonNull(pars.get("end_date"));
+ }
+ String whereStr = String.format("%%%s%%", callsign);
+
+ if (pars.get("exportFile") != null) {
+ exportFile = Objects.requireNonNull(pars.get("exportFile"));
+ }
+ if (pars.get("QSL") != null) {
+ qIsQSL = Objects.requireNonNull(pars.get("QSL"));
+ }
+ if (pars.get("Imported") != null) {
+ qIsImported = Objects.requireNonNull(pars.get("Imported"));
+ }
+
+ result.append("
\n" +
+ " " +
+ "\n" +
+ " "
+
+
+ , String.format(GeneralVariables.getStringFromResource(R.string.html_message_page_count), pageCount)
+ , GeneralVariables.getStringFromResource(R.string.html_message_page_size)
+ , pageSize
+ , GeneralVariables.getStringFromResource(R.string.html_message_query)
+
+ , GeneralVariables.getStringFromResource(R.string.html_callsign)
+ , callsign
+ , GeneralVariables.getStringFromResource(R.string.html_start_date_swl_message)
+ , startDate
+ , GeneralVariables.getStringFromResource(R.string.html_end_date_swl_message)
+ , endDate
+
+ , qIsQSL.equals("") ? "checked=\"true\"" : ""
+ , qIsQSL.equals("0") ? "checked=\"true\"" : ""
+ , qIsQSL.equals("1") ? "checked=\"true\"" : ""
+
+
+ , qIsImported.equals("") ? "checked=\"true\"" : ""
+ , qIsImported.equals("0") ? "checked=\"true\"" : ""
+ , qIsImported.equals("1") ? "checked=\"true\"" : ""
+ ));
+
+
+ //定位页,第一页、上一页、下一页,最后一页
+ result.append(String.format("|< " +
+ " << " +
+ " " +
+ ">> \n" +
+ " >| \n"
+
+ , 1, pageSize, callsign, startDate, endDate, qIsQSL, qIsImported
+ , pageIndex - 1 == 0 ? 1 : pageIndex - 1, pageSize, callsign, startDate, endDate, qIsQSL, qIsImported
+ , pageIndex
+ , pageIndex == pageCount ? pageCount : pageIndex + 1, pageSize, callsign, startDate, endDate, qIsQSL, qIsImported
+ , pageCount, pageSize, callsign, startDate, endDate, qIsQSL, qIsImported));
+ result.append(" ");
+ HtmlContext.tableRowEnd(result);
+ HtmlContext.tableEnd(result);
+ result.append("");
+ //"
\n"
+
+
+ cursor = mainViewModel.databaseOpr.getDb().rawQuery(
+ String.format(
+ "select * from QSLTable where (([call] LIKE ?)OR(station_callsign LIKE ?)) " +
+ dateSql +
+ " order by qso_date desc,time_on desc LIMIT(%d),%d "
+ , (pageIndex - 1) * pageSize, pageSize), new String[]{whereStr, whereStr});
+
+ HtmlContext.tableBegin(result, false, true).append("\n");
+
+ //表头
+ HtmlContext.tableRowBegin(result).append("\n");
+ HtmlContext.tableCellHeader(result, "No.", "QSL"
+ , GeneralVariables.getStringFromResource(R.string.html_qso_source)
+ , GeneralVariables.getStringFromResource(R.string.html_callsign)
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_grid)
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_mode)
+ , GeneralVariables.getStringFromResource(R.string.html_rst_sent)
+ , GeneralVariables.getStringFromResource(R.string.html_rst_rcvd)
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_start_day)//"qso date"
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_start_time)//"time_on"
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_end_date)//qso date off
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_end_time)//"time_off"
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_band)
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_freq)
+ , GeneralVariables.getStringFromResource(R.string.html_callsign)//"station_callsign"
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_grid)//"my_gridsquare"
+ , GeneralVariables.getStringFromResource(R.string.html_comment))//"comment")
+ .append("\n");
+ HtmlContext.tableRowEnd(result).append("\n");
+
+ //表内容
+ int order = 0;
+ while (cursor.moveToNext()) {
+ HtmlContext.tableRowBegin(result, true, order % 2 != 0).append("\n");
+
+ String call = cursor.getString(cursor.getColumnIndex("call"));
+ boolean isQSL = cursor.getInt(cursor.getColumnIndex("isQSL")) == 1;
+ boolean isLotW_Import = cursor.getInt(cursor.getColumnIndex("isLotW_import")) == 1;
+ boolean isLotW_QSL = cursor.getInt(cursor.getColumnIndex("isLotW_QSL")) == 1;
+ String gridsquare = cursor.getString(cursor.getColumnIndex("gridsquare"));
+ String mode = cursor.getString(cursor.getColumnIndex("mode"));
+ String rst_sent = cursor.getString(cursor.getColumnIndex("rst_sent"));
+ String rst_rcvd = cursor.getString(cursor.getColumnIndex("rst_rcvd"));
+ String qso_date = cursor.getString(cursor.getColumnIndex("qso_date"));
+ String time_on = cursor.getString(cursor.getColumnIndex("time_on"));
+ String qso_date_off = cursor.getString(cursor.getColumnIndex("qso_date_off"));
+ String time_off = cursor.getString(cursor.getColumnIndex("time_off"));
+ String band = cursor.getString(cursor.getColumnIndex("band"));
+ String freq = cursor.getString(cursor.getColumnIndex("freq"));
+ String station_callsign = cursor.getString(cursor.getColumnIndex("station_callsign"));
+ String my_gridsquare = cursor.getString(cursor.getColumnIndex("my_gridsquare"));
+ String comment = cursor.getString(cursor.getColumnIndex("comment"));
+
+
+ HtmlContext.tableCell(result, String.format("%d", order + 1 + pageSize * (pageIndex - 1)));
+ HtmlContext.tableCell(result, (isQSL || isLotW_QSL) ? "√ " : "✗ ");
+ HtmlContext.tableCell(result, isLotW_Import ?
+ String.format("%s "
+ , GeneralVariables.getStringFromResource(R.string.html_qso_external))
+ : String.format("%s "
+ , GeneralVariables.getStringFromResource(R.string.html_qso_raw)));//是否是导入的
+ HtmlContext.tableCell(result, String.format("%s "
+ , pageSize
+ , call.replace("<", "")
+ .replace(">", "")
+ , call.replace("<", "<")
+ .replace(">", ">")));
+ HtmlContext.tableCell(result, gridsquare == null ? "" : gridsquare, mode, rst_sent, rst_rcvd
+ , qso_date, time_on, qso_date_off, time_off, band, freq);
+ HtmlContext.tableCell(result, String.format("%s "
+ , pageSize
+ , station_callsign.replace("<", "")
+ .replace(">", "")
+ , station_callsign.replace("<", "<")
+ .replace(">", ">")));
+ HtmlContext.tableCell(result, my_gridsquare == null ? "" : my_gridsquare
+ , comment).append("\n");
+
+ HtmlContext.tableRowEnd(result).append("\n");
+
+ order++;
+ }
+ cursor.close();
+ HtmlContext.tableEnd(result).append(" \n");
+
+ return newFixedLengthResponse(HtmlContext.HTML_STRING(result.toString()));
+ }
/**
* 获取全部通联日志
@@ -1487,7 +1585,7 @@ public class LogHttpServer extends NanoHTTPD {
private String showAllQSL() {
Cursor cursor = mainViewModel.databaseOpr.getDb().rawQuery(
"select * from QSLTable order by ID DESC ", null);
- return HtmlContext.ListTableContext(cursor);
+ return HtmlContext.ListTableContext(cursor, true);
}
/**
@@ -1500,7 +1598,7 @@ public class LogHttpServer extends NanoHTTPD {
Cursor cursor = mainViewModel.databaseOpr.getDb().rawQuery(
"select * from QSLTable WHERE SUBSTR(qso_date,1,?)=? \n" +
"order by ID DESC ", new String[]{String.valueOf(month.length()), month});
- return HtmlContext.ListTableContext(cursor);
+ return HtmlContext.ListTableContext(cursor, true);
}
/**
@@ -1516,32 +1614,25 @@ public class LogHttpServer extends NanoHTTPD {
"}\n" +
"setTimeout('myrefresh()',5000); //指定5秒刷新一次,5000处可自定义设置,1000为1秒\n" +
"");
- result.append("\n");
- result.append("");
- result.append("UTC ");
- result.append("dB ");
- result.append("Dt ");
- result.append(String.format("%s "
- , GeneralVariables.getStringFromResource(R.string.html_qsl_freq)));
- result.append(String.format("%s "
- , GeneralVariables.getStringFromResource(R.string.message)));
- result.append(String.format("%s "
- , GeneralVariables.getStringFromResource(R.string.html_carrier_frequency_band)));
- result.append(" \n");
+ HtmlContext.tableBegin(result, false, true).append("\n");
+
+ HtmlContext.tableRowBegin(result);
+ HtmlContext.tableCellHeader(result, "UTC", "dB", "Δt"
+ , GeneralVariables.getStringFromResource(R.string.html_qsl_freq)
+ , GeneralVariables.getStringFromResource(R.string.message)
+ , GeneralVariables.getStringFromResource(R.string.html_carrier_frequency_band));
+ HtmlContext.tableRowEnd(result).append("\n");
+
int order = 0;
if (mainViewModel.currentMessages != null) {
for (Ft8Message message : mainViewModel.currentMessages) {
- if (order % 2 == 0) {
- result.append("");
- } else {
- result.append(" ");
- }
- result.append(message.toHtml());
- result.append(" ");
+ HtmlContext.tableRowBegin(result, true, order % 2 != 0)
+ .append("\n").append(message.toHtml());
+ HtmlContext.tableRowEnd(result).append("\n");
order++;
}
}
- result.append("
");
+ HtmlContext.tableEnd(result).append(" \n");
return result.toString();
}
@@ -1552,18 +1643,23 @@ public class LogHttpServer extends NanoHTTPD {
*/
private String showImportLog() {
StringBuilder result = new StringBuilder();
- result.append("\n");
- result.append(String.format("" +
- "%s%s %s "
+ HtmlContext.tableBegin(result, false, 0, true).append("\n");
+ HtmlContext.tableRowBegin(result, false, true);
+ HtmlContext.tableCell(result, String.format("%s%s %s"
, GeneralVariables.getStringFromResource(R.string.html_please_select)
, GeneralVariables.getStringFromResource(R.string.html_adi_format)
, GeneralVariables.getStringFromResource(R.string.html_file_in_other_format)));
- result.append(" ");
- result.append("
");
+ " ");
+ HtmlContext.tableRowEnd(result).append("\n");
+ HtmlContext.tableEnd(result).append("\n");
return result.toString();
}
@@ -1571,21 +1667,23 @@ public class LogHttpServer extends NanoHTTPD {
private String showQSLTable() {
StringBuilder result = new StringBuilder();
- result.append("\n");
- result.append(String.format("%s "
- , GeneralVariables.getStringFromResource(R.string.html_time)));
- result.append(String.format("%s "
- , GeneralVariables.getStringFromResource(R.string.html_total)));
- result.append(String.format("%s "
- , GeneralVariables.getStringFromResource(R.string.html_operation)));
- result.append(String.format("%s "
- , GeneralVariables.getStringFromResource(R.string.html_operation)));
- result.append(String.format("%s "
- , GeneralVariables.getStringFromResource(R.string.html_operation)));
+ HtmlContext.tableBegin(result, false, 1, true).append("\n");
+ HtmlContext.tableRowBegin(result).append("\n");
+
+ HtmlContext.tableCellHeader(result
+ , GeneralVariables.getStringFromResource(R.string.html_time)
+ , GeneralVariables.getStringFromResource(R.string.html_total)
+ , GeneralVariables.getStringFromResource(R.string.html_operation)
+ , GeneralVariables.getStringFromResource(R.string.html_operation)
+ , GeneralVariables.getStringFromResource(R.string.html_operation)
+ ).append("\n");
+
+ HtmlContext.tableRowEnd(result).append("\n");
Cursor cursor = mainViewModel.databaseOpr.getDb().rawQuery("select count(*) as b from QSLTable"
, null);
cursor.moveToFirst();
+
result.append(String.format("%s "
, GeneralVariables.getStringFromResource(R.string.html_all_logs)));
result.append(String.format("%s ", cursor.getString(cursor.getColumnIndex("b"))));
@@ -1598,53 +1696,52 @@ public class LogHttpServer extends NanoHTTPD {
"WHERE SUBSTR(qso_date,1,8)=?", new String[]{UtcTimer.getYYYYMMDD(UtcTimer.getSystemTime())});
cursor.moveToFirst();
- result.append("");
-
- result.append(String.format("%s "
+ HtmlContext.tableRowBegin(result, true, true).append("\n");
+ HtmlContext.tableCell(result, String.format("%s "
, UtcTimer.getYYYYMMDD(UtcTimer.getSystemTime())
, GeneralVariables.getStringFromResource(R.string.html_today_log)));
- result.append(String.format("%s "
- , cursor.getString(cursor.getColumnIndex("b"))));
- result.append(String.format("%s "
+ HtmlContext.tableCell(result, cursor.getString(cursor.getColumnIndex("b")));
+ HtmlContext.tableCell(result, String.format("%s "
, UtcTimer.getYYYYMMDD(UtcTimer.getSystemTime())
, GeneralVariables.getStringFromResource(R.string.html_download_all)));
- result.append(String.format("%s "
+ HtmlContext.tableCell(result, String.format("%s "
, UtcTimer.getYYYYMMDD(UtcTimer.getSystemTime())
, GeneralVariables.getStringFromResource(R.string.html_download_unconfirmed)));
-
- result.append(String.format("%s "
+ HtmlContext.tableCell(result, String.format("%s "
, UtcTimer.getYYYYMMDD(UtcTimer.getSystemTime())
- , GeneralVariables.getStringFromResource(R.string.html_delete)));
+ , GeneralVariables.getStringFromResource(R.string.html_delete))).append("\n");
+
+ HtmlContext.tableRowEnd(result).append("\n");
+
cursor.close();
int order = 1;
- cursor = mainViewModel.databaseOpr.getDb().rawQuery("select SUBSTR(qso_date,1,6) as a,count(*) as b from QSLTable\n" +
- "group by SUBSTR(qso_date,1,6)", null);
+ cursor = mainViewModel.databaseOpr.getDb()
+ .rawQuery("select SUBSTR(qso_date,1,6) as a,count(*) as b from QSLTable\n" +
+ "group by SUBSTR(qso_date,1,6)", null);
while (cursor.moveToNext()) {
- if (order % 2 == 0) {
- result.append("");
- } else {
- result.append(" ");
- }
- result.append(String.format("%s "
+ HtmlContext.tableRowBegin(result, true, order % 2 == 0);
+
+ HtmlContext.tableCell(result, String.format("%s "
, cursor.getString(cursor.getColumnIndex("a"))
, cursor.getString(cursor.getColumnIndex("a"))));
- result.append(String.format("%s "
- , cursor.getString(cursor.getColumnIndex("b"))));
- result.append(String.format("%s "
+
+ HtmlContext.tableCell(result, cursor.getString(cursor.getColumnIndex("b")));
+ HtmlContext.tableCell(result, String.format("%s "
, cursor.getString(cursor.getColumnIndex("a"))
, GeneralVariables.getStringFromResource(R.string.html_download_all)));
- result.append(String.format("%s "
+ HtmlContext.tableCell(result, String.format("%s "
, cursor.getString(cursor.getColumnIndex("a"))
, GeneralVariables.getStringFromResource(R.string.html_download_unconfirmed)));
-
- result.append(String.format("%s "
+ HtmlContext.tableCell(result, String.format("%s "
, cursor.getString(cursor.getColumnIndex("a"))
- , GeneralVariables.getStringFromResource(R.string.html_delete)));
+ , GeneralVariables.getStringFromResource(R.string.html_delete))).append("\n");
+
+ HtmlContext.tableRowEnd(result).append("\n");
order++;
}
- result.append("
");
- //cursor.close();
+ HtmlContext.tableEnd(result).append("\n");
+ cursor.close();
return result.toString();
}
@@ -1660,17 +1757,17 @@ public class LogHttpServer extends NanoHTTPD {
, new String[]{String.valueOf(month.length()), month});
}
- return downQSLTable(cursor,false);
+ return downQSLTable(cursor, false);
}
/**
* 下载全部日志
*
- * @return
+ * @return String
*/
private String downAllQSl() {
Cursor cursor = mainViewModel.databaseOpr.getDb().rawQuery("select * from QSLTable", null);
- return downQSLTable(cursor,false);
+ return downQSLTable(cursor, false);
}
/**
@@ -1679,8 +1776,7 @@ public class LogHttpServer extends NanoHTTPD {
* @return 日志内容
*/
@SuppressLint({"Range", "DefaultLocale"})
- private String downQSLTable(Cursor cursor,boolean isSWL) {
- //Cursor cursor = mainViewModel.databaseOpr.getDb().rawQuery("select * from QSLTable", null);
+ private String downQSLTable(Cursor cursor, boolean isSWL) {
StringBuilder logStr = new StringBuilder();
logStr.append("FT8CN ADIF Export\n");
@@ -1699,76 +1795,76 @@ public class LogHttpServer extends NanoHTTPD {
} else {
logStr.append("N ");
}
- }else {
+ } else {
logStr.append("Y ");
}
- if (cursor.getString(cursor.getColumnIndex("gridsquare"))!=null) {
+ if (cursor.getString(cursor.getColumnIndex("gridsquare")) != null) {
logStr.append(String.format("%s "
, cursor.getString(cursor.getColumnIndex("gridsquare")).length()
, cursor.getString(cursor.getColumnIndex("gridsquare"))));
}
- if (cursor.getString(cursor.getColumnIndex("mode"))!=null) {
+ if (cursor.getString(cursor.getColumnIndex("mode")) != null) {
logStr.append(String.format("%s "
, cursor.getString(cursor.getColumnIndex("mode")).length()
, cursor.getString(cursor.getColumnIndex("mode"))));
}
- if (cursor.getString(cursor.getColumnIndex("rst_sent"))!=null) {
+ if (cursor.getString(cursor.getColumnIndex("rst_sent")) != null) {
logStr.append(String.format("%s "
, cursor.getString(cursor.getColumnIndex("rst_sent")).length()
, cursor.getString(cursor.getColumnIndex("rst_sent"))));
}
- if (cursor.getString(cursor.getColumnIndex("rst_rcvd"))!=null) {
+ if (cursor.getString(cursor.getColumnIndex("rst_rcvd")) != null) {
logStr.append(String.format("%s "
, cursor.getString(cursor.getColumnIndex("rst_rcvd")).length()
, cursor.getString(cursor.getColumnIndex("rst_rcvd"))));
}
- if (cursor.getString(cursor.getColumnIndex("qso_date"))!=null) {
+ if (cursor.getString(cursor.getColumnIndex("qso_date")) != null) {
logStr.append(String.format("%s "
, cursor.getString(cursor.getColumnIndex("qso_date")).length()
, cursor.getString(cursor.getColumnIndex("qso_date"))));
}
- if (cursor.getString(cursor.getColumnIndex("time_on"))!=null) {
+ if (cursor.getString(cursor.getColumnIndex("time_on")) != null) {
logStr.append(String.format("%s "
, cursor.getString(cursor.getColumnIndex("time_on")).length()
, cursor.getString(cursor.getColumnIndex("time_on"))));
}
- if (cursor.getString(cursor.getColumnIndex("qso_date_off"))!=null) {
+ if (cursor.getString(cursor.getColumnIndex("qso_date_off")) != null) {
logStr.append(String.format("%s "
, cursor.getString(cursor.getColumnIndex("qso_date_off")).length()
, cursor.getString(cursor.getColumnIndex("qso_date_off"))));
}
- if (cursor.getString(cursor.getColumnIndex("time_off"))!=null) {
+ if (cursor.getString(cursor.getColumnIndex("time_off")) != null) {
logStr.append(String.format("%s "
, cursor.getString(cursor.getColumnIndex("time_off")).length()
, cursor.getString(cursor.getColumnIndex("time_off"))));
}
- if (cursor.getString(cursor.getColumnIndex("band"))!=null) {
+ if (cursor.getString(cursor.getColumnIndex("band")) != null) {
logStr.append(String.format("%s "
, cursor.getString(cursor.getColumnIndex("band")).length()
, cursor.getString(cursor.getColumnIndex("band"))));
}
- if (cursor.getString(cursor.getColumnIndex("freq"))!=null) {
+ if (cursor.getString(cursor.getColumnIndex("freq")) != null) {
logStr.append(String.format("%s "
, cursor.getString(cursor.getColumnIndex("freq")).length()
, cursor.getString(cursor.getColumnIndex("freq"))));
}
- if (cursor.getString(cursor.getColumnIndex("station_callsign"))!=null) {
+ if (cursor.getString(cursor.getColumnIndex("station_callsign")) != null) {
logStr.append(String.format("%s "
, cursor.getString(cursor.getColumnIndex("station_callsign")).length()
, cursor.getString(cursor.getColumnIndex("station_callsign"))));
}
- if (cursor.getString(cursor.getColumnIndex("my_gridsquare"))!=null) {
+ if (cursor.getString(cursor.getColumnIndex("my_gridsquare")) != null) {
logStr.append(String.format("%s "
, cursor.getString(cursor.getColumnIndex("my_gridsquare")).length()
, cursor.getString(cursor.getColumnIndex("my_gridsquare"))));
@@ -1783,7 +1879,6 @@ public class LogHttpServer extends NanoHTTPD {
, comment));
}
- //Log.e(TAG, "getQSLTable: " + logStr.toString());
cursor.close();
return logStr.toString();
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IComPacketTypes.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IComPacketTypes.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IComWifiRig.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IComWifiRig.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomAudioUdp.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomAudioUdp.java
old mode 100755
new mode 100644
index cbd022b..d0f1242
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomAudioUdp.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomAudioUdp.java
@@ -47,48 +47,10 @@ public class IcomAudioUdp extends IcomUdpBase {
x = 1.0f;
else if (x < -1.0)
x = -1.0f;
- temp[i] = (short) (0.5 + (x * 32767.0));
+ temp[i] = (short) (x * 32767.0);
}
doTXAudioRunnable.audioData=temp;
doTXThreadPool.execute(doTXAudioRunnable);
-// new Thread(new Runnable() {
-// @Override
-// public void run() {
-// final int partialLen = IComPacketTypes.TX_BUFFER_SIZE * 2;//数据包的长度
-// //要转换一下到BYTE,小端模式
-//
-// //byte[] data = new byte[audioData.length * 2 + partialLen * 4];//多出一点空声音放在前后各20ms*2共80ms
-// //先播放,是给出空的声音,for i 循环,做了一个判断,是给前面的空声音,for j循环,做得判断,是让后面发送空声音
-// byte[] audioPacket = new byte[partialLen];
-// for (int i = 0; i < (audioData.length / IComPacketTypes.TX_BUFFER_SIZE) + 8; i++) {//多出6个周期,前面3个,后面3个多
-// if (!isPttOn) break;
-// long now = System.currentTimeMillis() - 1;//获取当前时间
-//
-// sendTrackedPacket(IComPacketTypes.AudioPacket.getTxAudioPacket(audioPacket
-// , (short) 0, localId, remoteId, innerSeq));
-// innerSeq++;
-//
-// Arrays.fill(audioPacket,(byte)0x00);
-// if (i>=3) {//让前两个空数据发送出去
-// for (int j = 0; j < IComPacketTypes.TX_BUFFER_SIZE; j++) {
-// if ((i-3) * IComPacketTypes.TX_BUFFER_SIZE + j < audioData.length) {
-// System.arraycopy(IComPacketTypes.shortToBigEndian((short)
-// (audioData[(i-3) * IComPacketTypes.TX_BUFFER_SIZE + j]
-// * GeneralVariables.volumePercent))
-// , 0, audioPacket, j * 2, 2);
-// }
-// }
-// }
-// while (isPttOn) {
-// if (System.currentTimeMillis() - now >= 21) {//20毫秒一个周期
-// break;
-// }
-// }
-// }
-// Log.e(TAG, "run: 音频发送完毕!!" );
-// Thread.currentThread().interrupt();
-// }
-// }).start();
}
private static class DoTXAudioRunnable implements Runnable{
IcomAudioUdp icomAudioUdp;
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomCivUdp.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomCivUdp.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomControlUdp.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomControlUdp.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomSeqBuffer.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomSeqBuffer.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomUdpBase.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomUdpBase.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomUdpClient.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/icom/IcomUdpClient.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/HashTable.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/HashTable.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/LogCallsignAdapter.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/LogCallsignAdapter.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/LogFileImport.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/LogFileImport.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/LogQSLAdapter.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/LogQSLAdapter.java
old mode 100755
new mode 100644
index 7e39539..8e170f8
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/LogQSLAdapter.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/LogQSLAdapter.java
@@ -111,10 +111,15 @@ public class LogQSLAdapter extends RecyclerView.Adapter1000) clearBufferData();
+ //return;//说明数据还没接收完。
+ }else {
+ if (s.indexOf("\r")>0){//说明接到结束的数据了,并且不是第一个字符是;
+ buffer.append(s.substring(0,s.indexOf("\r")));
+ }
+ //开始分析数据
+ Yaesu3Command yaesu3Command = Yaesu3Command.getCommand(buffer.toString());
+ clearBufferData();//清一下缓存
+ //要把剩下的数据放到缓存里
+ buffer.append(s.substring(s.indexOf("\r")+1));
+
+ if (yaesu3Command == null) {
+ return;
+ }
+ String cmd=yaesu3Command.getCommandID();
+ if (cmd.equalsIgnoreCase("FA")) {//频率
+ long tempFreq=Yaesu3Command.getFrequency(yaesu3Command);
+ if (tempFreq!=0) {//如果tempFreq==0,说明频率不正常
+ setFreq(Yaesu3Command.getFrequency(yaesu3Command));
+ }
+ }else if (cmd.equalsIgnoreCase("RM")){//meter
+ if (Yaesu3Command.is590MeterSWR(yaesu3Command)) {
+ swr = Yaesu3Command.get590ALCOrSWR(yaesu3Command);
+ }
+ if (Yaesu3Command.is590MeterALC(yaesu3Command)) {
+ alc = Yaesu3Command.get590ALCOrSWR(yaesu3Command);
+ }
+ showAlert();
+ }
+
+ }
+
+ }
+ private void showAlert() {
+ if (swr >= KenwoodTK90RigConstant.ts_590_swr_alert_max) {
+ if (!swrAlert) {
+ swrAlert = true;
+ ToastMessage.show(GeneralVariables.getStringFromResource(R.string.swr_high_alert));
+ }
+ } else {
+ swrAlert = false;
+ }
+ if (alc > KenwoodTK90RigConstant.ts_590_alc_alert_max) {//网络模式下不警告ALC
+ if (!alcMaxAlert) {
+ alcMaxAlert = true;
+ ToastMessage.show(GeneralVariables.getStringFromResource(R.string.alc_high_alert));
+ }
+ } else {
+ alcMaxAlert = false;
+ }
+
+ }
+ @Override
+ public void readFreqFromRig() {
+ if (getConnector() != null) {
+ clearBufferData();//清空一下缓存
+ getConnector().sendData(KenwoodTK90RigConstant.setTS590ReadOperationFreq());
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "KENWOOD TS-2000";
+ }
+
+ public KenwoodTS2000Rig() {
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (getConnector()!=null){
+ getConnector().sendData(KenwoodTK90RigConstant.setTS590VFOMode());
+ }
+ }
+ },START_QUERY_FREQ_DELAY-500);
+ readFreqTimer.schedule(readTask(), START_QUERY_FREQ_DELAY,QUERY_FREQ_TIMEOUT);
+ }
+}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/KenwoodTS590Rig.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/KenwoodTS590Rig.java
old mode 100755
new mode 100644
index 6905c42..fb59880
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/KenwoodTS590Rig.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/KenwoodTS590Rig.java
@@ -176,7 +176,7 @@ public class KenwoodTS590Rig extends BaseRig {
@Override
public String getName() {
- return "KENWOOD TS-480/590/2000";
+ return "KENWOOD TS-480/590";
}
public KenwoodTS590Rig() {
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/OnConnectReceiveData.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/OnConnectReceiveData.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/OnRigStateChanged.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/OnRigStateChanged.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/XieGu6100Rig.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/XieGu6100Rig.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/XieGuRig.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/XieGuRig.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu2Command.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu2Command.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu2Rig.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu2Rig.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu2RigConstant.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu2RigConstant.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu38Rig.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu38Rig.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu38_450Rig.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu38_450Rig.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu39Rig.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu39Rig.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu3Command.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu3Command.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu3RigConstant.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/Yaesu3RigConstant.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/YaesuDX10Rig.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/YaesuDX10Rig.java
old mode 100755
new mode 100644
index 338b384..1f7244e
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/YaesuDX10Rig.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/YaesuDX10Rig.java
@@ -17,7 +17,7 @@ import java.util.TimerTask;
* 3代的指令,不同电台还有不同,频率长度981,991是9位,其它的长度是8位
*/
public class YaesuDX10Rig extends BaseRig {
- private static final String TAG = "Yaesu3Rig";
+ private static final String TAG = "YaesuDX10Rig";
private final StringBuilder buffer = new StringBuilder();
private int swr = 0;
private int alc = 0;
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/CdcAcmSerialDriver.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/CdcAcmSerialDriver.java
old mode 100755
new mode 100644
index 8574a44..be22673
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/CdcAcmSerialDriver.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/CdcAcmSerialDriver.java
@@ -305,6 +305,8 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
new int[] {
UsbId.VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL,
});
+
+
supportedDevices.put(UsbId.VENDOR_ATMEL,
new int[] {
UsbId.ATMEL_LUFA_CDC_DEMO_APP,
@@ -321,7 +323,8 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
new int[] {
UsbId.ST_CDC,
UsbId.ST_CDC2,
- UsbId.ST_CDC3
+ UsbId.ST_CDC3,
+ UsbId.CDC_WOLF_PID
});
supportedDevices.put(UsbId.VENDOR_RASPBERRY_PI,
new int[] {
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/Ch34xSerialDriver.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/Ch34xSerialDriver.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/CommonUsbSerialPort.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/CommonUsbSerialPort.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/Cp21xxSerialDriver.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/Cp21xxSerialDriver.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/FtdiSerialDriver.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/FtdiSerialDriver.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/ProbeTable.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/ProbeTable.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/ProlificSerialDriver.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/ProlificSerialDriver.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/SerialTimeoutException.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/SerialTimeoutException.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/UsbId.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/UsbId.java
old mode 100755
new mode 100644
index b9db984..73ab2a2
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/UsbId.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/UsbId.java
@@ -42,6 +42,8 @@ public final class UsbId {
public static final int VENDOR_VAN_OOIJEN_TECH = 0x16c0;
public static final int VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL = 0x0483;
+ public static final int CDC_WOLF_PID= 0xF001;// ST CDC WOLF RIG
+
public static final int VENDOR_LEAFLABS = 0x1eaf;
public static final int LEAFLABS_MAPLE = 0x0004;
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/UsbSerialDriver.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/UsbSerialDriver.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/UsbSerialPort.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/UsbSerialPort.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/UsbSerialProber.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/UsbSerialProber.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/util/MonotonicClock.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/util/MonotonicClock.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/util/SerialInputOutputManager.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/serialport/util/SerialInputOutputManager.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/spectrum/SpectrumListener.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/spectrum/SpectrumListener.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/timer/OnUtcTimer.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/timer/OnUtcTimer.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/timer/UtcTimer.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/timer/UtcTimer.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/BandsSpinnerAdapter.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/BandsSpinnerAdapter.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/BauRateSpinnerAdapter.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/BauRateSpinnerAdapter.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/CallingListAdapter.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/CallingListAdapter.java
old mode 100755
new mode 100644
index b2c7223..ddfb0f9
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/CallingListAdapter.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/CallingListAdapter.java
@@ -9,6 +9,7 @@ package com.bg7yoz.ft8cn.ui;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Paint;
+import android.opengl.Visibility;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.View;
@@ -169,6 +170,7 @@ public class CallingListAdapter extends RecyclerView.Adapter>() {
+ @Override
+ public void onChanged(ArrayList messages) {
callingListAdapter.notifyDataSetChanged();
//当列表下部稍微多出一些,自动上移
if (callListRecyclerView.computeVerticalScrollRange()
@@ -115,7 +124,6 @@ public class CallingListFragment extends Fragment {
}
});
-
//观察UTC时间
mainViewModel.timerSec.observe(getViewLifecycleOwner(), new Observer() {
@Override
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/ClearCacheDataDialog.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/ClearCacheDataDialog.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/ColumnarView.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/ColumnarView.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/ConfigFragment.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/ConfigFragment.java
old mode 100755
new mode 100644
index 0e7d215..9e69fa1
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/ConfigFragment.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/ConfigFragment.java
@@ -17,6 +17,10 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.CompoundButton;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.Observer;
+
import com.bg7yoz.ft8cn.FAQActivity;
import com.bg7yoz.ft8cn.Ft8Message;
import com.bg7yoz.ft8cn.GeneralVariables;
@@ -34,10 +38,6 @@ import com.bg7yoz.ft8cn.timer.UtcTimer;
import java.io.IOException;
-import androidx.annotation.NonNull;
-import androidx.fragment.app.Fragment;
-import androidx.lifecycle.Observer;
-
/**
* A simple {@link Fragment} subclass.
* create an instance of this fragment.
@@ -280,6 +280,15 @@ public class ConfigFragment extends Fragment {
//设置电台名称,参数列表
setRigNameSpinner();
+ //设置解码模式
+ setDecodeMode();
+
+ //设置音频输出的位数
+ setAudioOutputBitsMode();
+
+ //设置音频输出采样率
+ setAudioOutputRateMode();
+
//设置控制模式 VOX CAT
setControlMode();
@@ -762,6 +771,80 @@ public class ConfigFragment extends Fragment {
}
+ private void setDecodeMode() {
+ binding.decodeModeRadioGroup.clearCheck();
+ binding.fastDecodeRadioButton.setChecked(!GeneralVariables.deepDecodeMode);
+ binding.deepDecodeRadioButton.setChecked(GeneralVariables.deepDecodeMode);
+
+ View.OnClickListener listener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ int buttonId = binding.decodeModeRadioGroup.getCheckedRadioButtonId();
+ GeneralVariables.deepDecodeMode= buttonId ==binding.deepDecodeRadioButton.getId();
+ writeConfig("deepMode", GeneralVariables.deepDecodeMode? "1" : "0");
+ }
+ };
+
+ binding.fastDecodeRadioButton.setOnClickListener(listener);
+ binding.deepDecodeRadioButton.setOnClickListener(listener);
+
+ }
+
+
+ /**
+ * 设置音频输出的位数
+ */
+ private void setAudioOutputBitsMode() {
+ //binding.controlModeRadioGroup.setOnCheckedChangeListener(null);
+ binding.audioBitsRadioGroup.clearCheck();
+ binding.audio32BitsRadioButton.setChecked(GeneralVariables.audioOutput32Bit);
+ binding.audio16BitsRadioButton.setChecked(!GeneralVariables.audioOutput32Bit);
+
+
+
+ View.OnClickListener listener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ int buttonId = binding.audioBitsRadioGroup.getCheckedRadioButtonId();
+ GeneralVariables.audioOutput32Bit= buttonId ==binding.audio32BitsRadioButton.getId();
+ writeConfig("audioBits", GeneralVariables.audioOutput32Bit? "1" : "0");
+ }
+ };
+
+ binding.audio32BitsRadioButton.setOnClickListener(listener);
+ binding.audio16BitsRadioButton.setOnClickListener(listener);
+
+ }
+
+ /**
+ * 输出音频的采样率设置
+ */
+ private void setAudioOutputRateMode() {
+ binding.audioRateRadioGroup.clearCheck();
+ binding.audio12kRadioButton.setChecked(GeneralVariables.audioSampleRate==12000);
+ binding.audio24kRadioButton.setChecked(GeneralVariables.audioSampleRate==24000);
+ binding.audio48kRadioButton.setChecked(GeneralVariables.audioSampleRate==48000);
+
+
+
+ View.OnClickListener listener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (binding.audio12kRadioButton.isChecked()) GeneralVariables.audioSampleRate=12000;
+ if (binding.audio24kRadioButton.isChecked()) GeneralVariables.audioSampleRate=24000;
+ if (binding.audio48kRadioButton.isChecked()) GeneralVariables.audioSampleRate=48000;
+ writeConfig("audioRate", String.valueOf(GeneralVariables.audioSampleRate));
+ }
+ };
+
+ binding.audio12kRadioButton.setOnClickListener(listener);
+ binding.audio24kRadioButton.setOnClickListener(listener);
+ binding.audio48kRadioButton.setOnClickListener(listener);
+
+ }
+
+
+
/**
* 设置控制模式VOX CAT
*/
@@ -897,66 +980,54 @@ public class ConfigFragment extends Fragment {
binding.callsignHelpImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "callsign.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "callsign_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.callsign_help)
+ , true).show();
}
});
//梅登海德网格的帮助
binding.maidenGridImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "maidenhead.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "maidenhead_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.maidenhead_help)
+ , true).show();
}
});
//发射频率的帮助
binding.frequencyImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "frequency.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "frequency_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.frequency_help)
+ , true).show();
}
});
//延迟发射帮助
binding.transDelayImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "transDelay.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "transDelay_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.transDelay_help)
+ , true).show();
}
});
//时间偏移帮助
binding.timeOffsetImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "timeoffset.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "timeoffset_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.timeoffset_help)
+ , true).show();
}
});
//PTT延时帮助
binding.pttDelayImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "pttdelay.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "pttdelay_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.pttdelay_help)
+ , true).show();
}
});
//设置ABOUT
@@ -970,110 +1041,110 @@ public class ConfigFragment extends Fragment {
binding.operationHelpImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "operationBand.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "operationBand_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.operationBand_help)
+ , true).show();
}
});
//设置操作模式
binding.controlModeHelpImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "controlMode.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "controlMode_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.controlMode_help)
+ , true).show();
}
});
//设置CI-V地址和波特率帮助
binding.baudRateHelpImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "civ_help.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "civ_help_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.civ_help)
+ , true).show();
}
});
//电台型号列表
binding.rigNameHelpImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "rig_model_help.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "rig_model_help_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.rig_model_help)
+ , true).show();
}
});
//发射监管
binding.launchSupervisionImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "launch_supervision_help.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "launch_supervision_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.launch_supervision_help)
+ , true).show();
}
});
//无回应次数
binding.noResponseCountButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "no_response_help.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "no_response_help_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.no_response_help)
+ , true).show();
}
});
//自动呼叫
binding.autoFollowCountButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "auto_follow_help.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "auto_follow_help_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.auto_follow_help)
+ , true).show();
}
});
//连接模式
binding.connectModeHelpImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "connectMode.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "connectMode_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.connectMode_help)
+ , true).show();
}
});
//排除选项
binding.excludedHelpButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "excludeCallsign.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "excludeCallsign_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.excludeCallsign_help)
+ , true).show();
}
});
binding.swlHelpButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "swlMode.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "swlMode_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.swlMode_help)
+ , true).show();
+ }
+ });
+
+ //解码模式
+ binding.decodeModeHelpButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ new HelpDialog(requireContext(),requireActivity()
+ ,GeneralVariables.getStringFromResource(R.string.deep_mode_help)
+ ,true).show();
+ }
+ });
+
+ //音频输出帮助
+ binding.audioOutputImageButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.audio_output_help)
+ , true).show();
}
});
@@ -1081,11 +1152,9 @@ public class ConfigFragment extends Fragment {
binding.clearCacheHelpButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- if (GeneralVariables.isChina) {
- new HelpDialog(requireContext(), requireActivity(), "clear_cache_data.txt", true).show();
- } else {
- new HelpDialog(requireContext(), requireActivity(), "clear_cache_data_en.txt", true).show();
- }
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.clear_cache_data_help)
+ , true).show();
}
});
binding.clearFollowButton.setOnClickListener(new View.OnClickListener() {
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/FilterDialog.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/FilterDialog.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/FlexMeterRulerView.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/FlexMeterRulerView.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/FlexRadioInfoFragment.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/FlexRadioInfoFragment.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/FreqDialog.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/FreqDialog.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/FunctionOrderSpinnerAdapter.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/FunctionOrderSpinnerAdapter.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/HelpDialog.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/HelpDialog.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/LaunchSupervisionSpinnerAdapter.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/LaunchSupervisionSpinnerAdapter.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/LogFragment.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/LogFragment.java
old mode 100755
new mode 100644
index e27c71e..ad1ab12
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/LogFragment.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/LogFragment.java
@@ -1,6 +1,7 @@
package com.bg7yoz.ft8cn.ui;
/**
* 通联纪录的主界面。
+ *
* @author BGY70Z
* @date 2023-03-20
*/
@@ -65,7 +66,7 @@ public class LogFragment extends Fragment {
private LogCallsignAdapter logCallsignAdapter;
private LogQSLAdapter logQSLAdapter;
- private boolean loading=false;//防止滑动触发多次查询
+ private boolean loading = false;//防止滑动触发多次查询
private int lastItemPosition;
@@ -113,12 +114,12 @@ public class LogFragment extends Fragment {
});
binding.inputMycallEdit.setText(mainViewModel.queryKey);
- queryByCallsign(mainViewModel.queryKey,0);
+ queryByCallsign(mainViewModel.queryKey, 0);
mainViewModel.mutableQueryFilter.observe(getViewLifecycleOwner(), new Observer() {
@Override
public void onChanged(Integer integer) {
- queryByCallsign(mainViewModel.queryKey,0);
+ queryByCallsign(mainViewModel.queryKey, 0);
}
});
@@ -137,7 +138,7 @@ public class LogFragment extends Fragment {
@Override
public void afterTextChanged(Editable editable) {
mainViewModel.queryKey = editable.toString();
- queryByCallsign(mainViewModel.queryKey,0);
+ queryByCallsign(mainViewModel.queryKey, 0);
}
});
@@ -153,10 +154,16 @@ public class LogFragment extends Fragment {
binding.exportImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- new HelpDialog(requireContext(), requireActivity()
- , String.format(GeneralVariables.getStringFromResource(R.string.export_info)
- , getLocalIp(), LogHttpServer.DEFAULT_PORT)
- , false).show();
+ if (getLocalIp()==null) {
+ new HelpDialog(requireContext(), requireActivity()
+ , GeneralVariables.getStringFromResource(R.string.export_null)
+ ,false).show();
+ }else {
+ new HelpDialog(requireContext(), requireActivity()
+ , String.format(GeneralVariables.getStringFromResource(R.string.export_info)
+ , getLocalIp(), LogHttpServer.DEFAULT_PORT)
+ , false).show();
+ }
}
});
@@ -166,7 +173,7 @@ public class LogFragment extends Fragment {
public void onClick(View view) {
mainViewModel.logListShowCallsign = !mainViewModel.logListShowCallsign;
setShowStyle();
- queryByCallsign(binding.inputMycallEdit.getText().toString(),0);//偏移量0,就是重新查询
+ queryByCallsign(binding.inputMycallEdit.getText().toString(), 0);//偏移量0,就是重新查询
}
});
@@ -176,7 +183,7 @@ public class LogFragment extends Fragment {
public void onClick(View view) {
Intent intent = new Intent(requireContext(), GridTrackerMainActivity.class);
intent.putExtra("qslAll", mainViewModel.queryKey);
- intent.putExtra("queryFilter",mainViewModel.queryFilter);
+ intent.putExtra("queryFilter", mainViewModel.queryFilter);
startActivity(intent);
}
});
@@ -209,6 +216,8 @@ public class LogFragment extends Fragment {
break;
case 3:
Intent intent = new Intent(requireContext(), GridTrackerMainActivity.class);
+ //ArrayList records=new ArrayList<>();
+ //records.add(logQSLAdapter.getRecord(position));
intent.putExtra("qslList", logQSLAdapter.getRecord(position));
startActivity(intent);
break;
@@ -222,6 +231,7 @@ public class LogFragment extends Fragment {
return super.onContextItemSelected(item);
}
+
private boolean itemIsOnScreen(View view) {
if (view != null) {
int width = view.getWidth();
@@ -232,15 +242,20 @@ public class LogFragment extends Fragment {
return false;
}
- private void loadQueryData(RecyclerView recyclerView){
- if ((!loading)) {
- if (mainViewModel.logListShowCallsign){
- queryByCallsign(mainViewModel.queryKey, logCallsignAdapter.getItemCount());
- }else {
- queryByCallsign(mainViewModel.queryKey, logQSLAdapter.getItemCount());
+ private void loadQueryData(RecyclerView recyclerView) {
+// if ((!loading)&&(recyclerView.computeVerticalScrollRange()
+// - recyclerView.computeVerticalScrollExtent()
+// - recyclerView.computeVerticalScrollOffset() < 100)) {
+ if ((!loading)) {
+ //ToastMessage.show("查询");
+ if (mainViewModel.logListShowCallsign) {
+ queryByCallsign(mainViewModel.queryKey, logCallsignAdapter.getItemCount());
+ } else {
+ queryByCallsign(mainViewModel.queryKey, logQSLAdapter.getItemCount());
}
}
}
+
/**
* 设置列表滑动动作
*/
@@ -252,13 +267,13 @@ public class LogFragment extends Fragment {
super.onScrollStateChanged(recyclerView, newState);
int itemCount;
- if (mainViewModel.logListShowCallsign){
- itemCount=logCallsignAdapter.getItemCount();
- }else {
- itemCount=logQSLAdapter.getItemCount();
+ if (mainViewModel.logListShowCallsign) {
+ itemCount = logCallsignAdapter.getItemCount();
+ } else {
+ itemCount = logQSLAdapter.getItemCount();
}
if (newState == SCROLL_STATE_IDLE &&
- lastItemPosition == itemCount){
+ lastItemPosition == itemCount) {
loadQueryData(recyclerView);
}
@@ -268,14 +283,28 @@ public class LogFragment extends Fragment {
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
- if (layoutManager instanceof LinearLayoutManager){
+ if (layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager manager = (LinearLayoutManager) layoutManager;
int firstVisibleItem = manager.findFirstVisibleItemPosition();
int l = manager.findLastCompletelyVisibleItemPosition();
- lastItemPosition = firstVisibleItem+(l-firstVisibleItem)+1;
+ lastItemPosition = firstVisibleItem + (l - firstVisibleItem) + 1;
}
+// if (dy>0){//列表向上移动
+// loadQueryData(recyclerView);
+// }
+ //当列表上滑,接近底部时,开始查询
+// if ((!loading)&&(dy>0)&&(recyclerView.computeVerticalScrollRange()
+// - recyclerView.computeVerticalScrollExtent()
+// - recyclerView.computeVerticalScrollOffset() < 100)) {
+// ToastMessage.show("查询");
+// if (mainViewModel.logListShowCallsign){
+// queryByCallsign(mainViewModel.queryKey, logCallsignAdapter.getItemCount());
+// }else {
+// queryByCallsign(mainViewModel.queryKey, logQSLAdapter.getItemCount());
+// }
+// }
}
});
@@ -425,15 +454,15 @@ public class LogFragment extends Fragment {
*
* @param callsign 呼号
*/
- private void queryByCallsign(String callsign,int offset) {
- loading=true;//开始读数据
+ private void queryByCallsign(String callsign, int offset) {
+ loading = true;//开始读数据
//分两种查询
if (mainViewModel.logListShowCallsign) {
- if (offset==0) {//说明是新增记录
+ if (offset == 0) {//说明是新增记录
logCallsignAdapter.clearRecords();//清空记录
}
- mainViewModel.databaseOpr.getQSLCallsignsByCallsign(false,offset,callsign, mainViewModel.queryFilter
+ mainViewModel.databaseOpr.getQSLCallsignsByCallsign(false, offset, callsign, mainViewModel.queryFilter
, new OnQueryQSLCallsign() {
@Override
public void afterQuery(ArrayList records) {
@@ -441,16 +470,16 @@ public class LogFragment extends Fragment {
@Override
public void run() {
logCallsignAdapter.setQSLCallsignList(records);
- loading=false;
+ loading = false;
}
});
}
});
} else {
- if (offset==0){//说明是新增记录
+ if (offset == 0) {//说明是新增记录
logQSLAdapter.clearRecords();
}
- mainViewModel.databaseOpr.getQSLRecordByCallsign(false,offset,callsign, mainViewModel.queryFilter
+ mainViewModel.databaseOpr.getQSLRecordByCallsign(false, offset, callsign, mainViewModel.queryFilter
, new OnQueryQSLRecordCallsign() {
@Override
public void afterQuery(ArrayList records) {
@@ -458,7 +487,7 @@ public class LogFragment extends Fragment {
@Override
public void run() {
logQSLAdapter.setQSLList(records);
- loading=false;
+ loading = false;
}
});
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/LoginIcomRadioDialog.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/LoginIcomRadioDialog.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/MyCallingFragment.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/MyCallingFragment.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/NoReplyLimitSpinnerAdapter.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/NoReplyLimitSpinnerAdapter.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/PttDelaySpinnerAdapter.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/PttDelaySpinnerAdapter.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/QRZ_Fragment.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/QRZ_Fragment.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/RigNameSpinnerAdapter.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/RigNameSpinnerAdapter.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/RulerFrequencyView.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/RulerFrequencyView.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/SelectBluetoothDialog.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/SelectBluetoothDialog.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/SelectFlexRadioDialog.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/SelectFlexRadioDialog.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/SetVolumeDialog.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/SetVolumeDialog.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/SpectrumFragment.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/SpectrumFragment.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/SpectrumView.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/SpectrumView.java
old mode 100755
new mode 100644
index 84ea2b3..6dbd23c
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/SpectrumView.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/SpectrumView.java
@@ -72,8 +72,6 @@ public class SpectrumView extends ConstraintLayout {
mainViewModel.currentMessages=null;
-
-
//原始频谱开关
controlDeNoiseSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
@@ -105,7 +103,7 @@ public class SpectrumView extends ConstraintLayout {
mainViewModel.mutableIsDecoding.observe(fragment.getViewLifecycleOwner(), new Observer() {
@Override
public void onChanged(Boolean aBoolean) {
- waterfallView.setDrawMessage(!aBoolean);//false说明解码完毕
+ waterfallView.setDrawMessage(!aBoolean);//aBoolean==false说明解码完毕
}
});
@@ -121,7 +119,6 @@ public class SpectrumView extends ConstraintLayout {
columnarView.setTouch_x(Math.round(motionEvent.getX()));
-
if (!mainViewModel.ft8TransmitSignal.isSynFrequency()
&& (waterfallView.getFreq_hz() > 0)
&& (motionEvent.getAction() == ACTION_UP)
@@ -132,7 +129,6 @@ public class SpectrumView extends ConstraintLayout {
mainViewModel.ft8TransmitSignal.setBaseFrequency(
(float) waterfallView.getFreq_hz());
-
rulerFrequencyView.setFreq(waterfallView.getFreq_hz());
fragment.requireActivity().runOnUiThread(new Runnable() {
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/ToastMessage.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/ToastMessage.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/UtcOffsetSpinnerAdapter.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/UtcOffsetSpinnerAdapter.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/VolumeProgress.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/VolumeProgress.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/WaterfallView.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/WaterfallView.java
old mode 100755
new mode 100644
index 93b243d..a0fc9ed
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/WaterfallView.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ui/WaterfallView.java
@@ -1,6 +1,7 @@
package com.bg7yoz.ft8cn.ui;
/**
* 瀑布图自定义控件。
+ *
* @author BGY70Z
* @date 2023-03-20
*/
@@ -19,12 +20,17 @@ import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.bg7yoz.ft8cn.Ft8Message;
import com.bg7yoz.ft8cn.timer.UtcTimer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
import java.util.List;
+import java.util.ListIterator;
public class WaterfallView extends View {
private int blockHeight = 2;//色块高度
@@ -60,6 +66,8 @@ public class WaterfallView extends View {
public WaterfallView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
+ ArrayList messages= new ArrayList<>();
+
/**
* 把dp值转换为像素点
@@ -97,7 +105,7 @@ public class WaterfallView extends View {
fontPaint.setDither(true);
fontPaint.setTextAlign(Paint.Align.LEFT);
- // messagePaint = new Paint();
+ // messagePaint = new Paint();
messagePaint.setTextSize(dpToPixel(11));
messagePaint.setColor(0xff00ffff);
messagePaint.setAntiAlias(true);
@@ -176,7 +184,10 @@ public class WaterfallView extends View {
invalidate();
}
- public void setWaveData(int[] data, int sequential, List messages) {
+ public void setWaveData(int[] data, int sequential, List msgs) {
+ if (drawMessage&& msgs!=null){//把需要画的消息复制出来防止多线程访问冲突
+ messages=new ArrayList<>(msgs);
+ }
if (data == null) {
return;
@@ -232,19 +243,7 @@ public class WaterfallView extends View {
fontPaint.setTextAlign(Paint.Align.LEFT);
for (Ft8Message msg : messages) {
-// if (GeneralVariables.checkQSLCallsign(msg.getCallsignFrom())) {//如果在数据库中,划线
-// messagePaint.setStrikeThruText(true);
-// messagePaint.setUnderlineText(true);
-// //messagePaint.setFlags(messagePaint.getFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
-// } else {//如果不在数据库中,去掉划线
-// messagePaint.setStrikeThruText(false);
-// messagePaint.setUnderlineText(false);
-// //messagePaint.setFlags(messagePaint.getFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
-// }
-
-
if (msg.inMyCall()) {//与我有关
- //messagePaint.setColor(0xffFF0202);
messagePaint.setColor(0xffffb2b2);
} else if (msg.checkIsCQ()) {//CQ
messagePaint.setColor(0xffeeee00);
@@ -252,18 +251,20 @@ public class WaterfallView extends View {
messagePaint.setColor(0xff00ffff);
}
Path path = new Path();
+
path.moveTo(msg.freq_hz * freq_width, pathStart);
path.lineTo(msg.freq_hz * freq_width, pathEnd);
- _canvas.drawTextOnPath(msg.getMessageText(), path
+ _canvas.drawTextOnPath(msg.getMessageText(true), path
, 0, 0, messagePaintBack);//消息背景
- _canvas.drawTextOnPath(msg.getMessageText(), path
+ _canvas.drawTextOnPath(msg.getMessageText(true), path
, 0, 0, messagePaint);//消息
}
}
+
}
public void setTouch_x(int touch_x) {
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/HamRecorder.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/HamRecorder.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/MicRecorder.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/MicRecorder.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/OnAudioRecorded.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/OnAudioRecorded.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/OnGetVoiceDataDone.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/OnGetVoiceDataDone.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/OnHamRecord.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/OnHamRecord.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/OnVoiceMonitorChanged.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/OnVoiceMonitorChanged.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/WaveAccess.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/WaveAccess.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/WaveConstants.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/WaveConstants.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/WaveFileReader.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/WaveFileReader.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/WaveFileWriter.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/WaveFileWriter.java
old mode 100755
new mode 100644
index ade3750..dcc46bd
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/WaveFileWriter.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/WaveFileWriter.java
@@ -87,7 +87,7 @@ public class WaveFileWriter {
}
public void close() {
- // TODO document why this method is empty
+
}
private void writeString(String str, int len) {
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/WriteWavHeader.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/wave/WriteWavHeader.java
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/anim/click_button.xml b/ft8CN/app/src/main/res/anim/click_button.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/anim/view_blink.xml b/ft8CN/app/src/main/res/anim/view_blink.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/color/imagebutton_click_selector.xml b/ft8CN/app/src/main/res/color/imagebutton_click_selector.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/color/switch_color_style.xml b/ft8CN/app/src/main/res/color/switch_color_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/baseline_location_on_24.xml b/ft8CN/app/src/main/res/drawable-night/baseline_location_on_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/clear_calling_list_image_48 2.xml b/ft8CN/app/src/main/res/drawable-night/clear_calling_list_image_48 2.xml
deleted file mode 100755
index 46b3d83..0000000
--- a/ft8CN/app/src/main/res/drawable-night/clear_calling_list_image_48 2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/clear_calling_list_image_48.xml b/ft8CN/app/src/main/res/drawable-night/clear_calling_list_image_48.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/config_ic_baseline_keyboard_arrow_down_24 2.xml b/ft8CN/app/src/main/res/drawable-night/config_ic_baseline_keyboard_arrow_down_24 2.xml
deleted file mode 100755
index 32ebe76..0000000
--- a/ft8CN/app/src/main/res/drawable-night/config_ic_baseline_keyboard_arrow_down_24 2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/config_ic_baseline_keyboard_arrow_down_24.xml b/ft8CN/app/src/main/res/drawable-night/config_ic_baseline_keyboard_arrow_down_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/config_ic_baseline_keyboard_arrow_up_24 2.xml b/ft8CN/app/src/main/res/drawable-night/config_ic_baseline_keyboard_arrow_up_24 2.xml
deleted file mode 100755
index a893e20..0000000
--- a/ft8CN/app/src/main/res/drawable-night/config_ic_baseline_keyboard_arrow_up_24 2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/config_ic_baseline_keyboard_arrow_up_24.xml b/ft8CN/app/src/main/res/drawable-night/config_ic_baseline_keyboard_arrow_up_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/cqzone.png b/ft8CN/app/src/main/res/drawable-night/cqzone.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/cqzone_b 2.png b/ft8CN/app/src/main/res/drawable-night/cqzone_b 2.png
deleted file mode 100755
index b4a4dfe..0000000
Binary files a/ft8CN/app/src/main/res/drawable-night/cqzone_b 2.png and /dev/null differ
diff --git a/ft8CN/app/src/main/res/drawable-night/cqzone_b.png b/ft8CN/app/src/main/res/drawable-night/cqzone_b.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/dxcc 2.png b/ft8CN/app/src/main/res/drawable-night/dxcc 2.png
deleted file mode 100755
index 6aa14e8..0000000
Binary files a/ft8CN/app/src/main/res/drawable-night/dxcc 2.png and /dev/null differ
diff --git a/ft8CN/app/src/main/res/drawable-night/dxcc.png b/ft8CN/app/src/main/res/drawable-night/dxcc.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/dxcc_b 2.png b/ft8CN/app/src/main/res/drawable-night/dxcc_b 2.png
deleted file mode 100755
index f436b64..0000000
Binary files a/ft8CN/app/src/main/res/drawable-night/dxcc_b 2.png and /dev/null differ
diff --git a/ft8CN/app/src/main/res/drawable-night/dxcc_b.png b/ft8CN/app/src/main/res/drawable-night/dxcc_b.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/guohe_logo 2.png b/ft8CN/app/src/main/res/drawable-night/guohe_logo 2.png
deleted file mode 100755
index a80382f..0000000
Binary files a/ft8CN/app/src/main/res/drawable-night/guohe_logo 2.png and /dev/null differ
diff --git a/ft8CN/app/src/main/res/drawable-night/guohe_logo.png b/ft8CN/app/src/main/res/drawable-night/guohe_logo.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_assignment_24 2.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_assignment_24 2.xml
deleted file mode 100755
index 84527a7..0000000
--- a/ft8CN/app/src/main/res/drawable-night/ic_baseline_assignment_24 2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_assignment_24.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_assignment_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_assignment_ind_24 2.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_assignment_ind_24 2.xml
deleted file mode 100755
index f753515..0000000
--- a/ft8CN/app/src/main/res/drawable-night/ic_baseline_assignment_ind_24 2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_assignment_ind_24.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_assignment_ind_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_cancel_schedule_send_off 2.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_cancel_schedule_send_off 2.xml
deleted file mode 100755
index f926e9a..0000000
--- a/ft8CN/app/src/main/res/drawable-night/ic_baseline_cancel_schedule_send_off 2.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_cancel_schedule_send_off.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_cancel_schedule_send_off.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_check_48 2.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_check_48 2.xml
deleted file mode 100755
index 76faa78..0000000
--- a/ft8CN/app/src/main/res/drawable-night/ic_baseline_check_48 2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_check_48.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_check_48.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_close_32 2.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_close_32 2.xml
deleted file mode 100755
index a880088..0000000
--- a/ft8CN/app/src/main/res/drawable-night/ic_baseline_close_32 2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_close_32.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_close_32.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_headset_mic_24 2.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_headset_mic_24 2.xml
deleted file mode 100755
index a99f4b1..0000000
--- a/ft8CN/app/src/main/res/drawable-night/ic_baseline_headset_mic_24 2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_headset_mic_24.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_headset_mic_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_info_32.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_info_32.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_insert_chart_outlined_24.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_insert_chart_outlined_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_link_24 2.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_link_24 2.xml
deleted file mode 100755
index 57f63f7..0000000
--- a/ft8CN/app/src/main/res/drawable-night/ic_baseline_link_24 2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_link_24.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_link_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_list_alt_24 2.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_list_alt_24 2.xml
deleted file mode 100755
index a8087d4..0000000
--- a/ft8CN/app/src/main/res/drawable-night/ic_baseline_list_alt_24 2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_list_alt_24.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_list_alt_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_mic_off_48.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_mic_off_48.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_not_listed_location_32 2.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_not_listed_location_32 2.xml
deleted file mode 100755
index 0241208..0000000
--- a/ft8CN/app/src/main/res/drawable-night/ic_baseline_not_listed_location_32 2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_not_listed_location_32.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_not_listed_location_32.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_pause_circle_outline_24.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_pause_circle_outline_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_save_alt_24 2.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_save_alt_24 2.xml
deleted file mode 100755
index a534888..0000000
--- a/ft8CN/app/src/main/res/drawable-night/ic_baseline_save_alt_24 2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_save_alt_24.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_save_alt_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_send_white_48 2.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_send_white_48 2.xml
deleted file mode 100755
index 461a25c..0000000
--- a/ft8CN/app/src/main/res/drawable-night/ic_baseline_send_white_48 2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_send_white_48.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_send_white_48.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/ic_baseline_settings_bluetooth_24.xml b/ft8CN/app/src/main/res/drawable-night/ic_baseline_settings_bluetooth_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/itu 2.png b/ft8CN/app/src/main/res/drawable-night/itu 2.png
deleted file mode 100755
index d76a030..0000000
Binary files a/ft8CN/app/src/main/res/drawable-night/itu 2.png and /dev/null differ
diff --git a/ft8CN/app/src/main/res/drawable-night/itu.png b/ft8CN/app/src/main/res/drawable-night/itu.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/itu_b 2.png b/ft8CN/app/src/main/res/drawable-night/itu_b 2.png
deleted file mode 100755
index 1bce90b..0000000
Binary files a/ft8CN/app/src/main/res/drawable-night/itu_b 2.png and /dev/null differ
diff --git a/ft8CN/app/src/main/res/drawable-night/itu_b.png b/ft8CN/app/src/main/res/drawable-night/itu_b.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable-night/spinner_style 2.xml b/ft8CN/app/src/main/res/drawable-night/spinner_style 2.xml
deleted file mode 100755
index f3e99f0..0000000
--- a/ft8CN/app/src/main/res/drawable-night/spinner_style 2.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/drawable-night/spinner_style.xml b/ft8CN/app/src/main/res/drawable-night/spinner_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/baseline_location_on_24.xml b/ft8CN/app/src/main/res/drawable/baseline_location_on_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/calling_list_cell_0_style.xml b/ft8CN/app/src/main/res/drawable/calling_list_cell_0_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/calling_list_cell_1_style.xml b/ft8CN/app/src/main/res/drawable/calling_list_cell_1_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/calling_list_cell_2_style.xml b/ft8CN/app/src/main/res/drawable/calling_list_cell_2_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/calling_list_cell_3_style.xml b/ft8CN/app/src/main/res/drawable/calling_list_cell_3_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/calling_list_cell_style.xml b/ft8CN/app/src/main/res/drawable/calling_list_cell_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/clear_calling_list_image_48.xml b/ft8CN/app/src/main/res/drawable/clear_calling_list_image_48.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/config_ic_baseline_keyboard_arrow_down_24.xml b/ft8CN/app/src/main/res/drawable/config_ic_baseline_keyboard_arrow_down_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/config_ic_baseline_keyboard_arrow_up_24.xml b/ft8CN/app/src/main/res/drawable/config_ic_baseline_keyboard_arrow_up_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/count_detail_item_0_style.xml b/ft8CN/app/src/main/res/drawable/count_detail_item_0_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/count_detail_item_1_style.xml b/ft8CN/app/src/main/res/drawable/count_detail_item_1_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/cq.png b/ft8CN/app/src/main/res/drawable/cq.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/cqzone.png b/ft8CN/app/src/main/res/drawable/cqzone.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/cqzone_b.png b/ft8CN/app/src/main/res/drawable/cqzone_b.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/debug_message_style.xml b/ft8CN/app/src/main/res/drawable/debug_message_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/dxcc.png b/ft8CN/app/src/main/res/drawable/dxcc.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/dxcc_b.png b/ft8CN/app/src/main/res/drawable/dxcc_b.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/editor_layout_background_style.xml b/ft8CN/app/src/main/res/drawable/editor_layout_background_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/editor_layout_style.xml b/ft8CN/app/src/main/res/drawable/editor_layout_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/editor_style.xml b/ft8CN/app/src/main/res/drawable/editor_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/error_editor_style.xml b/ft8CN/app/src/main/res/drawable/error_editor_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/flex_config_style.xml b/ft8CN/app/src/main/res/drawable/flex_config_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/flex_icon.png b/ft8CN/app/src/main/res/drawable/flex_icon.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/flex_meter_ruler_style.xml b/ft8CN/app/src/main/res/drawable/flex_meter_ruler_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/flex_meter_style.xml b/ft8CN/app/src/main/res/drawable/flex_meter_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/flexradio.png b/ft8CN/app/src/main/res/drawable/flexradio.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/float_button_style.xml b/ft8CN/app/src/main/res/drawable/float_button_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ft8cn_icon.png b/ft8CN/app/src/main/res/drawable/ft8cn_icon.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/guohe_logo.png b/ft8CN/app/src/main/res/drawable/guohe_logo.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/help_dialog_style.xml b/ft8CN/app/src/main/res/drawable/help_dialog_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_arrow_forward_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_arrow_forward_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_assignment_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_assignment_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_assignment_ind_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_assignment_ind_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_campaign_transmitting_red_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_campaign_transmitting_red_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_cancel_schedule_send_off.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_cancel_schedule_send_off.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_check_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_check_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_check_48.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_check_48.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_chevron_left_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_chevron_left_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_chevron_right_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_chevron_right_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_close_32.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_close_32.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_cq_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_cq_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_cq_qso_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_cq_qso_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_freq_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_freq_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_fullscreen_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_fullscreen_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_fullscreen_exit_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_fullscreen_exit_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_grid_tracker_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_grid_tracker_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_headset_mic_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_headset_mic_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_help_outline_32.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_help_outline_32.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_history_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_history_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_info_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_info_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_info_32.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_info_32.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_info_white_32.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_info_white_32.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_insert_chart_outlined_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_insert_chart_outlined_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_library_add_check_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_library_add_check_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_link_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_link_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_list_alt_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_list_alt_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_mic_48.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_mic_48.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_mic_off_48.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_mic_off_48.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_mic_red_48.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_mic_red_48.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_nav_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_nav_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_not_listed_location_32.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_not_listed_location_32.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_pause_circle_outline_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_pause_circle_outline_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_pause_disable_circle_outline_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_pause_disable_circle_outline_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_save_alt_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_save_alt_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_send_48.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_send_48.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_send_red_48.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_send_red_48.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_send_white_48.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_send_white_48.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_settings_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_settings_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_settings_bluetooth_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_settings_bluetooth_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_signal_cellular_4_bar_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_signal_cellular_4_bar_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_tracker_close_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_tracker_close_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_tracker_settings_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_tracker_settings_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_visibility_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_visibility_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_volume_up_24.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_volume_up_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_baseline_wifi_tethering_16.xml b/ft8CN/app/src/main/res/drawable/ic_baseline_wifi_tethering_16.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_launcher_background.xml b/ft8CN/app/src/main/res/drawable/ic_launcher_background.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_launcher_foreground.xml b/ft8CN/app/src/main/res/drawable/ic_launcher_foreground.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/ic_nav_spectrum_24.xml b/ft8CN/app/src/main/res/drawable/ic_nav_spectrum_24.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/icon_background_style.xml b/ft8CN/app/src/main/res/drawable/icon_background_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/imagebutton_selected_style.xml b/ft8CN/app/src/main/res/drawable/imagebutton_selected_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/imagebutton_style.xml b/ft8CN/app/src/main/res/drawable/imagebutton_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/imagebutton_transparent_style.xml b/ft8CN/app/src/main/res/drawable/imagebutton_transparent_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/is_weak_signal_24.xml b/ft8CN/app/src/main/res/drawable/is_weak_signal_24.xml
new file mode 100644
index 0000000..1872318
--- /dev/null
+++ b/ft8CN/app/src/main/res/drawable/is_weak_signal_24.xml
@@ -0,0 +1,6 @@
+
+
+
diff --git a/ft8CN/app/src/main/res/drawable/itu.png b/ft8CN/app/src/main/res/drawable/itu.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/itu_b.png b/ft8CN/app/src/main/res/drawable/itu_b.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/log_callsign_editor_style.xml b/ft8CN/app/src/main/res/drawable/log_callsign_editor_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/log_item_delete_icon.xml b/ft8CN/app/src/main/res/drawable/log_item_delete_icon.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/logo_background_style.xml b/ft8CN/app/src/main/res/drawable/logo_background_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/nav_calling_list_image.xml b/ft8CN/app/src/main/res/drawable/nav_calling_list_image.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/nav_my_calling_image.xml b/ft8CN/app/src/main/res/drawable/nav_my_calling_image.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/newyear.png b/ft8CN/app/src/main/res/drawable/newyear.png
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/run_decode_image_48.xml b/ft8CN/app/src/main/res/drawable/run_decode_image_48.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/select_bluetooth_dialog_style.xml b/ft8CN/app/src/main/res/drawable/select_bluetooth_dialog_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/select_serial_port_item_style.xml b/ft8CN/app/src/main/res/drawable/select_serial_port_item_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/select_serial_port_layout_style.xml b/ft8CN/app/src/main/res/drawable/select_serial_port_layout_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/speaker.xml b/ft8CN/app/src/main/res/drawable/speaker.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/spectrum_text_back_ground.xml b/ft8CN/app/src/main/res/drawable/spectrum_text_back_ground.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/spinner_style.xml b/ft8CN/app/src/main/res/drawable/spinner_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/tracker_calling_list_background.xml b/ft8CN/app/src/main/res/drawable/tracker_calling_list_background.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/tracker_config_bar_style.xml b/ft8CN/app/src/main/res/drawable/tracker_config_bar_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/tracker_grid_info_win_style.xml b/ft8CN/app/src/main/res/drawable/tracker_grid_info_win_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/tracker_new_cq_info_win_style.xml b/ft8CN/app/src/main/res/drawable/tracker_new_cq_info_win_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/tracker_qsl_sample_style.xml b/ft8CN/app/src/main/res/drawable/tracker_qsl_sample_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/tracker_qso_sample_style.xml b/ft8CN/app/src/main/res/drawable/tracker_qso_sample_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/tracker_qsx_sample_style.xml b/ft8CN/app/src/main/res/drawable/tracker_qsx_sample_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/tracker_sample_cq_bar_style.xml b/ft8CN/app/src/main/res/drawable/tracker_sample_cq_bar_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/tracker_tips_radio_style.xml b/ft8CN/app/src/main/res/drawable/tracker_tips_radio_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/drawable/welcom_layout_style.xml b/ft8CN/app/src/main/res/drawable/welcom_layout_style.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout-land/fragment_calling_list.xml b/ft8CN/app/src/main/res/layout-land/fragment_calling_list.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout-land/fragment_flex_radio_info.xml b/ft8CN/app/src/main/res/layout-land/fragment_flex_radio_info.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout-land/fragment_my_calling.xml b/ft8CN/app/src/main/res/layout-land/fragment_my_calling.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout-land/log_callsign_holder_item.xml b/ft8CN/app/src/main/res/layout-land/log_callsign_holder_item.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout-land/log_qsl_holder_item.xml b/ft8CN/app/src/main/res/layout-land/log_qsl_holder_item.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout-land/main_activity.xml b/ft8CN/app/src/main/res/layout-land/main_activity.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout/activity_faqactivity.xml b/ft8CN/app/src/main/res/layout/activity_faqactivity.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout/activity_grid_tracker_main.xml b/ft8CN/app/src/main/res/layout/activity_grid_tracker_main.xml
old mode 100755
new mode 100644
index d0d2789..1cf90e9
--- a/ft8CN/app/src/main/res/layout/activity_grid_tracker_main.xml
+++ b/ft8CN/app/src/main/res/layout/activity_grid_tracker_main.xml
@@ -69,6 +69,29 @@
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/layout/clear_cache_dialog_layout.xml b/ft8CN/app/src/main/res/layout/clear_cache_dialog_layout.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout/count_detail_item.xml b/ft8CN/app/src/main/res/layout/count_detail_item.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout/count_info_bar_item.xml b/ft8CN/app/src/main/res/layout/count_info_bar_item.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout/count_info_none_item.xml b/ft8CN/app/src/main/res/layout/count_info_none_item.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout/count_info_pie_item.xml b/ft8CN/app/src/main/res/layout/count_info_pie_item.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout/filter_dialog_layout.xml b/ft8CN/app/src/main/res/layout/filter_dialog_layout.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout/flex_device_list_item.xml b/ft8CN/app/src/main/res/layout/flex_device_list_item.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout/fragment_calling_list.xml b/ft8CN/app/src/main/res/layout/fragment_calling_list.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/layout/fragment_config.xml b/ft8CN/app/src/main/res/layout/fragment_config.xml
old mode 100755
new mode 100644
index 4c9d64c..46c4210
--- a/ft8CN/app/src/main/res/layout/fragment_config.xml
+++ b/ft8CN/app/src/main/res/layout/fragment_config.xml
@@ -345,6 +345,168 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -880,6 +1044,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@+id/decodeModeLayout">
-
+ app:layout_constraintTop_toBottomOf="@+id/audioOutputLayout">
Κλήση
Αρχείο επαφών
Ρυθμίσεις
-
-
-
\'Εναρξη αποκωδικοποίησης
dB
Δt
@@ -173,7 +170,9 @@
Χρόνος έναρξης : %s
Χρόνος τερματισμού : %s
Rst που ελήφθη : %s
+ Rst που ελήφθη
Rst που στάλθηξε : %s
+ Rst που στάλθηξε
Μπάντα : %s
Συχν : %s
Mode : %s
@@ -212,7 +211,9 @@
Λειτουργία
Διαγραφή
Χρόνος έναρξης QSO
+ Ημερομηνία έναρξης QSO
Χρόνος τερματισμού QSO
+ Ημερομηνία λήξης QSO
Mode
Grid
Μπάντα
@@ -427,7 +428,51 @@
Export to ADIF file
Total number of QSO decoded
QSOs decoded
-
+ Audio output
+ 16-bit int
+ 32-bit float
+ The number of bits in audio output
+ 12kHz
+ 24kHz
+ 48kHz
+ Sample rate
+ The sample rate of the audio output
+ Bit depth
+ callsign_en.txt
+ maidenhead_en.txt
+ frequency_en.txt
+ transDelay_en.txt
+ timeoffset_en.txt
+ pttdelay_en.txt
+ operationBand_en.txt
+ controlMode_en.txt
+ civ_help_en.txt
+ rig_model_help_en.txt
+ launch_supervision_en.txt
+ no_response_help_en.txt
+ auto_follow_help_en.txt
+ connectMode_en.txt
+ excludeCallsign_en.txt
+ swlMode_en.txt
+ audio_output_help_en.txt
+ clear_cache_data_en.txt
+ Query the logs
+ All
+ Data sources
+ Unconfirmed
+ Confirmed
+ Local logs
+ External logs
+ External
+ Local log
+ Πρωτόκολλο
+ Comment
+ Λειτουργία αποκωδικοποίησης
+ Γρήγορη λειτουργία
+ Λειτουργία πολλαπλής αποκωδικοποίησης
+ Παρακαλώ χρησιμοποιήστε τον web browser των άλλων συσκευών για εξαγωγή στο παρασκήνιο στο ίδιο LAN που είναι αυτή η συσκευή.\nPlease connect to a valid Wi-Fi
+ decode_help_en.txt
+
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values-es/strings.xml b/ft8CN/app/src/main/res/values-es/strings.xml
old mode 100755
new mode 100644
index 80aac66..9b6ec62
--- a/ft8CN/app/src/main/res/values-es/strings.xml
+++ b/ft8CN/app/src/main/res/values-es/strings.xml
@@ -5,9 +5,6 @@
Calling
QSO Logs
Ajustes
-
-
-
Empezar a decodificar
dB
Δt
@@ -173,7 +170,9 @@
Hora de inicio : %s
Hora de finalización: %s
Rst recibido : %s
+ Rst recibido
Rst enviado: %s
+ Rst enviado
Banda : %s
Freq : %s
Modo : %s
@@ -212,7 +211,9 @@
Operación
Borrar
QSO hora de inicio
+ Fecha de inicio del qso
QSO hora de finalización
+ Fecha de finalización del qso
Modo
Grid
Banda
@@ -427,7 +428,50 @@
Export to ADIF file
Total number of QSO decoded
QSOs decoded
-
+ Audio output
+ 16-bit int
+ 32-bit float
+ The number of bits in audio output
+ 12kHz
+ 24kHz
+ 48kHz
+ Sample rate
+ The sample rate of the audio output
+ Bit depth
+ callsign_en.txt
+ maidenhead_en.txt
+ frequency_en.txt
+ transDelay_en.txt
+ timeoffset_en.txt
+ pttdelay_en.txt
+ operationBand_en.txt
+ controlMode_en.txt
+ civ_help_en.txt
+ rig_model_help_en.txt
+ launch_supervision_en.txt
+ no_response_help_en.txt
+ auto_follow_help_en.txt
+ connectMode_en.txt
+ excludeCallsign_en.txt
+ swlMode_en.txt
+ audio_output_help_en.txt
+ clear_cache_data_en.txt
+ Query the logs
+ All
+ Data sources
+ Unconfirmed
+ Confirmed
+ Local logs
+ External logs
+ External
+ Local log
+ Acuerdo
+ Comment
+ Modo de decodificación
+ Modo rápido
+ Modo de decodificación múltiple
+ Utilice el navegador web de otros dispositivos para exportar desde el fondo en la misma LAN que este dispositivo.\nPlease connect to a valid Wi-Fi
+ decode_help_en.txt
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values-ja/strings.xml b/ft8CN/app/src/main/res/values-ja/strings.xml
old mode 100755
new mode 100644
index e88d0f3..0600511
--- a/ft8CN/app/src/main/res/values-ja/strings.xml
+++ b/ft8CN/app/src/main/res/values-ja/strings.xml
@@ -13,12 +13,6 @@
設定
-
-
-
-
-
-
デコ—ドを開始
dB
@@ -288,7 +282,7 @@
- %.0fキロメートル
+ %.0f km
@@ -323,8 +317,10 @@
終了時間:%s
自局RST:%s
+ 自局RST
相手局RST:%s
+ 相手局RST
バンド:%s
@@ -391,8 +387,10 @@
削除
交信開始時間
+ 交信開始日
交信終了時間
+ 交信終了日
モード
@@ -713,6 +711,50 @@
ADIFにエクスポート
デコードされたQSO総件数
デコードされたQSO
+ 音声出力
+ 16-bit int
+ 32-bit float
+ 音声出力中のビット数
+ 12kHz
+ 24kHz
+ 48kHz
+ サンプルレート
+ 音声出力のサンプルレート
+ ビット深度
+ callsign_en.txt
+ maidenhead_en.txt
+ frequency_en.txt
+ transDelay_en.txt
+ timeoffset_en.txt
+ pttdelay_en.txt
+ operationBand_en.txt
+ controlMode_en.txt
+ civ_help_en.txt
+ rig_model_help_en.txt
+ launch_supervision_en.txt
+ no_response_help_en.txt
+ auto_follow_help_en.txt
+ connectMode_en.txt
+ excludeCallsign_en.txt
+ swlMode_en.txt
+ audio_output_help_en.txt
+ clear_cache_data_en.txt
+ ログを検索
+ 全部
+ データソース
+ 未確認
+ 確認済
+ ローカルログ
+ 外部ログ
+ 外部
+ ローカル
+ プロトコル
+ コメント
+ デコードモード
+ 高速
+ ディープ
+ このデバイスと同じLANに接続し、ほかのデバイスでウェブブラウザーを利用してエクスポートしてください。\nブラウザーのアドレスバーに次の内容を入力してください:\n有効なWiFiに接続してください。
+ decode_help_en.txt
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values-night/colors.xml b/ft8CN/app/src/main/res/values-night/colors.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/values-night/strings.xml b/ft8CN/app/src/main/res/values-night/strings.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/values-night/themes.xml b/ft8CN/app/src/main/res/values-night/themes.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/values-zh-rCN/strings.xml b/ft8CN/app/src/main/res/values-zh-rCN/strings.xml
old mode 100755
new mode 100644
index fd650ae..0c20174
--- a/ft8CN/app/src/main/res/values-zh-rCN/strings.xml
+++ b/ft8CN/app/src/main/res/values-zh-rCN/strings.xml
@@ -6,8 +6,6 @@
通联记录
设置
-
-
开始解码
dB
Δt
@@ -172,7 +170,9 @@
起始时间:%s
结束时间:%s
接收报告:%s
+ 接收报告
发送报告:%s
+ 发送报告
波长:%s
频率:%s
模式:%s
@@ -209,6 +209,7 @@
操作
删除
通联开始时间
+ 通联开始日期
通联结束时间
模式
网格
@@ -425,6 +426,50 @@
导出到ADIF文件
守听到的QSO数
SWL记录
+ 音频输出
+ 16位整型
+ 32位浮点
+ 音频输出位数
+ 12kHz
+ 24kHz
+ 48kHz
+ 采样频率
+ 音频输出采样率
+ 位深
+ callsign.txt
+ maidenhead.txt
+ frequency.txt
+ transDelay.txt
+ timeoffset.txt
+ pttdelay.txt
+ operationBand.txt
+ controlMode.txt
+ civ_help.txt
+ rig_model_help.txt
+ launch_supervision_help.txt
+ no_response_help.txt
+ auto_follow_help.txt
+ connectMode.txt
+ excludeCallsign.txt
+ swlMode.txt
+ audio_output_help.txt
+ clear_cache_data.txt
+ 查询日志
+ 全部
+ 数据来源
+ 未确认
+ 已确认
+ 本地日志
+ 外部日志
+ 外部日志
+ 本地日志
+ 协议
+ 备注
+ 解码模式
+ 快速解码
+ 多次解码
+ 请在与本机相同的局域网下,用其它设备的网页浏览器从后台导出。\n在浏览器的地址栏中输入如下内容:\n无法获取合适的IP地址,请连接到一个有效的Wifi。
+ decode_help.txt
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values-zh-rHK/strings.xml b/ft8CN/app/src/main/res/values-zh-rHK/strings.xml
old mode 100755
new mode 100644
index d8cebe0..d4a9220
--- a/ft8CN/app/src/main/res/values-zh-rHK/strings.xml
+++ b/ft8CN/app/src/main/res/values-zh-rHK/strings.xml
@@ -6,8 +6,6 @@
通聯記錄
設置
-
-
開始解碼
dB
Δt
@@ -172,7 +170,9 @@
起始時間:%s
結束時間:%s
接收報告:%s
+ 接收報告
發送報告:%s
+ 發送報告
頻段:%s
頻率:%s
模式:%s
@@ -209,7 +209,9 @@
操作
刪除
通聯開始時間
+ 通聯開始日期
通聯結束時間
+ 通聯結束日期
模式
網格
頻段
@@ -425,6 +427,50 @@
導出到ADIF文件
收聽到的QSO數
SWL記錄
+ 音頻輸出
+ 16位整型
+ 32位浮點
+ 音頻輸出位數
+ 12kHz
+ 24kHz
+ 48kHz
+ 采樣頻率
+ 音頻輸出采樣率
+ 位深
+ callsign.txt
+ maidenhead.txt
+ frequency.txt
+ transDelay.txt
+ timeoffset.txt
+ pttdelay.txt
+ operationBand.txt
+ controlMode.txt
+ civ_help.txt
+ rig_model_help.txt
+ launch_supervision_help.txt
+ no_response_help.txt
+ auto_follow_help.txt
+ connectMode.txt
+ excludeCallsign.txt
+ swlMode.txt
+ audio_output_help.txt
+ clear_cache_data.txt
+ 查詢日誌
+ 全部
+ 數據來源
+ 未確認
+ 已確認
+ 本地日誌
+ 外部日誌
+ 外部日誌
+ 本地日誌
+ 協定
+ 備註
+ 解碼模式
+ 快速解碼
+ 多次解碼
+ 請在與本機相同的局域網下,用其它設備的網頁瀏覽器從後台導出。\n在瀏覽器的地址欄中輸入如下內容:\n無法獲取合適的IP地址,請連接到一個有效的Wifi。
+ decode_help.txt
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values-zh-rMO/strings.xml b/ft8CN/app/src/main/res/values-zh-rMO/strings.xml
old mode 100755
new mode 100644
index 7f8e931..1d36dab
--- a/ft8CN/app/src/main/res/values-zh-rMO/strings.xml
+++ b/ft8CN/app/src/main/res/values-zh-rMO/strings.xml
@@ -6,8 +6,6 @@
通聯記錄
設置
-
-
開始解碼
dB
Δt
@@ -172,7 +170,9 @@
起始時間:%s
結束時間:%s
接收報告:%s
+ 接收報告
發送報告:%s
+ 發送報告
頻段:%s
頻率:%s
模式:%s
@@ -209,7 +209,9 @@
操作
刪除
通聯開始時間
+ 通聯開始日期
通聯結束時間
+ 通聯結束日期
模式
網格
頻段
@@ -425,5 +427,50 @@
導出到ADIF文件
收聽到的QSO數
SWL記錄
+ 音頻輸出
+ 16位整型
+ 32位浮點
+ 音頻輸出位數
+ 12kHz
+ 24kHz
+ 48kHz
+ 采樣頻率
+ 音頻輸出采樣率
+ 位深
+ callsign.txt
+ maidenhead.txt
+ frequency.txt
+ transDelay.txt
+ timeoffset.txt
+ pttdelay.txt
+ operationBand.txt
+ controlMode.txt
+ civ_help.txt
+ rig_model_help.txt
+ launch_supervision_help.txt
+ no_response_help.txt
+ auto_follow_help.txt
+ connectMode.txt
+ excludeCallsign.txt
+ swlMode.txt
+ audio_output_help.txt
+ clear_cache_data.txt
+ 查詢日誌
+ 全部
+ 數據來源
+ 未確認
+ 已確認
+ 本地日誌
+ 外部日誌
+ 外部日誌
+ 本地日誌
+ 協定
+ 備註
+ 解碼模式
+ 快速解碼
+ 多次解碼
+ 請在與本機相同的局域網下,用其它設備的網頁瀏覽器從後台導出。\n在瀏覽器的地址欄中輸入如下內容:\n無法獲取合適的IP地址,請連接到一個有效的Wifi。
+ decode_help.txt
+
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values-zh-rTW/strings.xml b/ft8CN/app/src/main/res/values-zh-rTW/strings.xml
old mode 100755
new mode 100644
index 114d409..b052a6b
--- a/ft8CN/app/src/main/res/values-zh-rTW/strings.xml
+++ b/ft8CN/app/src/main/res/values-zh-rTW/strings.xml
@@ -6,8 +6,6 @@
通聯記錄
設置
-
-
開始解碼
dB
Δt
@@ -172,7 +170,9 @@
起始時間:%s
結束時間:%s
接收報告:%s
+ 接收報告
發送報告:%s
+ 發送報告
頻段:%s
頻率:%s
模式:%s
@@ -209,7 +209,9 @@
操作
刪除
通聯開始時間
+ 通聯開始日期
通聯結束時間
+ 通聯結束日期
模式
網格
頻段
@@ -425,6 +427,50 @@
導出到ADIF文件
收聽到的QSO數
SWL記錄
+ 音頻輸出
+ 16位整型
+ 32位浮點
+ 音頻輸出位數
+ 12kHz
+ 24kHz
+ 48kHz
+ 采樣頻率
+ 音頻輸出采樣率
+ 位深
+ callsign.txt
+ maidenhead.txt
+ frequency.txt
+ transDelay.txt
+ timeoffset.txt
+ pttdelay.txt
+ operationBand.txt
+ controlMode.txt
+ civ_help.txt
+ rig_model_help.txt
+ launch_supervision_help.txt
+ no_response_help.txt
+ auto_follow_help.txt
+ connectMode.txt
+ excludeCallsign.txt
+ swlMode.txt
+ audio_output_help.txt
+ clear_cache_data.txt
+ 查詢日誌
+ 全部
+ 數據來源
+ 未確認
+ 已確認
+ 本地日誌
+ 外部日誌
+ 外部日誌
+ 本地日誌
+ 協定
+ 備註
+ 解碼模式
+ 快速解碼
+ 多次解碼
+ 請在與本機相同的局域網下,用其它設備的網頁瀏覽器從後台導出。\n在瀏覽器的地址欄中輸入如下內容:\n無法獲取合適的IP地址,請連接到一個有效的Wifi。
+ decode_help.txt
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values/colors.xml b/ft8CN/app/src/main/res/values/colors.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/values/float_button_ids.xml b/ft8CN/app/src/main/res/values/float_button_ids.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/values/strings.xml b/ft8CN/app/src/main/res/values/strings.xml
old mode 100755
new mode 100644
index dfa5a02..8671633
--- a/ft8CN/app/src/main/res/values/strings.xml
+++ b/ft8CN/app/src/main/res/values/strings.xml
@@ -173,7 +173,9 @@
Start time : %s
End time : %s
Rst received : %s
+ Rst received
Rst send : %s
+ Rst send
Band : %s
Freq : %s
Mode : %s
@@ -212,7 +214,9 @@
Operation
Delete
QSO start time
+ QSO start date
QSO end time
+ QSO end date
Mode
Grid
Band
@@ -357,7 +361,6 @@
Warning ! SWR is too high .
Signal output strength %.0f %%
Signal output strength
-
Distance
QTH of callsign
Update time
@@ -395,7 +398,6 @@
Band \t Total
CQ Modifier
Please input modifer
-
Start ATU
Maximum Tx power to %d W
ATU Tune power %d W
@@ -429,6 +431,50 @@
Export to ADIF file
Total number of QSO decoded
QSOs decoded
+ Audio output
+ 16-bit int
+ 32-bit float
+ Audio - bit depth
+ 12kHz
+ 24kHz
+ 48kHz
+ Sample rate
+ Audio - sample rate
+ Bit depth
+ callsign_en.txt
+ maidenhead_en.txt
+ frequency_en.txt
+ transDelay_en.txt
+ timeoffset_en.txt
+ pttdelay_en.txt
+ operationBand_en.txt
+ controlMode_en.txt
+ civ_help_en.txt
+ rig_model_help_en.txt
+ launch_supervision_en.txt
+ no_response_help_en.txt
+ auto_follow_help_en.txt
+ connectMode_en.txt
+ excludeCallsign_en.txt
+ swlMode_en.txt
+ audio_output_help_en.txt
+ decode_help_en.txt
+ clear_cache_data_en.txt
+ Query the logs
+ All
+ Data sources
+ Unconfirmed
+ Confirmed
+ Local logs
+ External logs
+ External
+ Local log
+ Protocol
+ Comment
+ Decode mode
+ Fast decode
+ Deep decode
+ Export from WebUI via web browser on another device under the same LAN \n Please connect to a valid Wi-Fi
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values/styles.xml b/ft8CN/app/src/main/res/values/styles.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/values/themes.xml b/ft8CN/app/src/main/res/values/themes.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/src/main/res/xml/device_filter.xml b/ft8CN/app/src/main/res/xml/device_filter.xml
old mode 100755
new mode 100644
index 265ed83..dd21225
--- a/ft8CN/app/src/main/res/xml/device_filter.xml
+++ b/ft8CN/app/src/main/res/xml/device_filter.xml
@@ -39,4 +39,5 @@
+
diff --git a/ft8CN/app/src/test/java/com/bg7yoz/ft8cn/ExampleUnitTest.java b/ft8CN/app/src/test/java/com/bg7yoz/ft8cn/ExampleUnitTest.java
deleted file mode 100755
index 1ec875a..0000000
--- a/ft8CN/app/src/test/java/com/bg7yoz/ft8cn/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.bg7yoz.ft8cn;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/ft8CN/build.gradle b/ft8CN/build.gradle
old mode 100755
new mode 100644
index a13cd5e..5908a23
--- a/ft8CN/build.gradle
+++ b/ft8CN/build.gradle
@@ -1,10 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
- id 'com.android.application' version '7.4.1' apply false
- id 'com.android.library' version '7.4.1' apply false
+ id 'com.android.application' version '7.1.2' apply false
+ id 'com.android.library' version '7.1.2' apply false
}
ext {
-
+ var3 = 'G:\\coding\\ft8CN\\ft8CN.jks'
+ var = var3
+ var1 = '/Users/mymac/Desktop/coding/ft8CN/ft8CN.jks'
+ var2 = var
}
task clean(type: Delete) {
delete rootProject.buildDir
diff --git a/ft8CN/ft8cn_icon.png b/ft8CN/ft8cn_icon.png
old mode 100755
new mode 100644
diff --git a/ft8CN/gradle.properties b/ft8CN/gradle.properties
old mode 100755
new mode 100644
diff --git a/ft8CN/gradle/wrapper/gradle-wrapper.jar b/ft8CN/gradle/wrapper/gradle-wrapper.jar
old mode 100755
new mode 100644
diff --git a/ft8CN/gradle/wrapper/gradle-wrapper.properties b/ft8CN/gradle/wrapper/gradle-wrapper.properties
old mode 100755
new mode 100644
index 515d43a..e0c50f2
--- a/ft8CN/gradle/wrapper/gradle-wrapper.properties
+++ b/ft8CN/gradle/wrapper/gradle-wrapper.properties
@@ -1,4 +1,4 @@
-#Mon Mar 20 22:46:09 CST 2023
+#Sat Jul 08 16:47:34 CST 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
distributionPath=wrapper/dists
diff --git a/ft8CN/gradlew b/ft8CN/gradlew
old mode 100755
new mode 100644
diff --git a/ft8CN/gradlew.bat b/ft8CN/gradlew.bat
old mode 100755
new mode 100644
diff --git a/ft8CN/settings.gradle b/ft8CN/settings.gradle
old mode 100755
new mode 100644