diff --git a/commons.h b/commons.h index 52bc6086c..a3843004f 100644 --- a/commons.h +++ b/commons.h @@ -1,14 +1,14 @@ #ifndef COMMONS_H #define COMMONS_H -#define NFFT 32768 +#define NSMAX 15750 extern "C" { extern struct { short int d2[1800*12000]; //This is "common/jt8com/..." in fortran - float ss[184*4400]; - float savg[4400]; + float ss[184*NSMAX]; + float savg[NSMAX]; double fcenter; //USB dial freq (kHz) int nutc; //UTC as integer, HHMM int ntrperiod; //TR period (seconds) diff --git a/jt9.txt b/jt9.txt index a4e11239e..5aeb0dee1 100644 --- a/jt9.txt +++ b/jt9.txt @@ -5,17 +5,17 @@ rate r=1/2, and a zero tail, which leads to an encoded message length of (72+31)*2 = 206 bits. Modulation is 9-FSK: 8 tones for data, one for synchronization. Sixteen symbol intervals are used for synchronization, so a transmission requires a total of 207/3 + 16 = 85 -channel symbols. Symbol durations tsym are set to approximately +channel symbols. Symbol durations tsym are approximately (TRperiod-10)/85, where TRperiod is the T/R sequence length in -seconds. The exact symbol lengths are chosen so as to make nsps, the -number of samples at 12000 samples per second, a number rich in -factors less than 7. This choice makes for efficient FFTs. Tone -spacing of the 9-FSK modulation is df=1/tsym, the same as the keying -rate. The total occupied bandwidth is 9*df. +seconds. Exact symbol lengths are chosen so that nsps, the number of +samples per symbol (at 12000 samples per second) is a number with no +prime factor greater than 7. This choice makes for efficient FFTs. +Tone spacing of the 9-FSK modulation is df=1/tsym=12000/nsps, the same +as the keying rate. The total occupied bandwidth is 9*df. -Parameters of the five JT9 sub-modes are summarized in the following +Parameters of five JT9 sub-modes are summarized in the following table, along with S/N thresholds measured by simulation on an AWGN -(additive white Gaussian noise) channel. +(additive white Gaussian noise) channel. ------------------------------------------------------------------------- Mode nsps nsps2 df tsym BW S/N* Tdec Tfree Factors @@ -33,7 +33,7 @@ JT9-30 250880 15750 0.048 20.91 0.4 -42.3 1788.5 11.5 2^5 3^2 5^3 7 Transmitting ------------ 1. Source encode the structured message to 72 bits -2. Convolutionally encode (K=32, r=1/2) to (72+31)*2 = 206 bits +2. Apply convolutional ECC (K=32, r=1/2) to yield (72+31)*2 = 206 bits 3. Interleave to scramble the bit order 4. Assemble 3-bit groups to make (206+1)/3 = 69 symbols 5. Gray-code the symbol values @@ -43,21 +43,21 @@ Transmitting Receiving --------- 1. Apply noise blanking with the timf2 method -2. Filter to 500 Hz bandwidth, downsample (1/16) to 750 Hz complex - (FIR filter with 61 taps). Complex data to array c0 (max 1.35M) +2. Filter to 500 Hz bandwidth and downsample (1/16) to 750 Hz (FIR + filter with 61 taps). Complex data to array c0 (max 1.35M) 3. Compute symbol-length spectra at half-symbol steps. Use for - waterfalls s(15750) and detecting sync vectors; save in ss(184,15750) - and savg(15750) -4. At time Tdec, look for sync vectors in ss(); get estimates of DF, DT -5. Do full-length FFTs, NFFT1=96*nsps2 + waterfall display s(15750) and save in ss(184,15750) and + savg(15750) for detecting sync vectors. +4. At time Tdec, find sync vectors in ss(); get estimates of DF, DT +5. Do full-length FFT, NFFT1=96*nsps2, zero-pad as required. 6. For each candidate signal, do inverse FFT of length 1536. This - will provide 16 complex samples per symbol, and sync tone should be - close to f=0. + yields 16 complex samples per symbol, and sync tone should be + close to zero frequency. 7. Use afc65b method to get improved values of DF, DT. 8. Tweak freq and time offset to 0. -9. Compute 8-bin spectra of 69 data symbols: s2(8,69). Re-order the - bins by removing Gray code. -10. Compute soft symbols for 206 bits -11. Re-order the bits by removing interleaving. +9. Compute 8-bin spectra of 69 data symbols: s2(8,69). Re-order bins + by removing Gray code. +10. Compute soft symbols for 206 bits. +11. Remove interleaving 12. Pack bits into bytes, send to Fano decoder 13. If Fano succeeds, remove source encoding and display user message. diff --git a/libm65/genjt9.f90 b/libm65/genjt9.f90 index d8a109aae..8af75ba18 100644 --- a/libm65/genjt9.f90 +++ b/libm65/genjt9.f90 @@ -7,8 +7,6 @@ subroutine genjt9(message,minutes,msgsent,d6) character*22 message !Message to be generated character*22 msgsent !Message as it will be received - integer*4 mettab(0:255,0:1) - integer*4 d0(13) !72-bit message as 6-bit words integer*1 d1(13) !72 bits and zero tail as 8-bit bytes integer*1 d2(207) !Encoded information-carrying bits @@ -17,18 +15,6 @@ subroutine genjt9(message,minutes,msgsent,d6) integer*4 d5(69) !Gray-coded symbols, values 0-7 integer*4 d6(85) !Channel symbols including sync, values 0-8 - integer*4 t0(13) !72-bit message as 6-bit words - integer*1 t1(13) !72 bits and zero tail as 8-bit bytes - integer*1 t2(207) !Encoded information-carrying bits - integer*1 t3(207) !Bits from t4, after interleaving - integer*4 t4(69) !Symbols from t5, values 0-7 - integer*4 t5(69) !Gray-coded symbols, values 0-7 - integer*4 t6(85) !Channel symbols including sync, values 0-8 - - integer*1 tmp(72) - - integer*1 i1 - equivalence (i1,i4) integer isync(85) !Sync vector data isync/ & 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0, & @@ -58,60 +44,5 @@ subroutine genjt9(message,minutes,msgsent,d6) endif enddo -! We're done! Now reverse the process, as a test. - - t6=d6 - j=0 - do i=1,85 - if(isync(i).eq.1) cycle - j=j+1 - t5(j)=t6(i)-1 - enddo - - call graycode(t5,69,-1,t4) - call unpackbits(t4,69,3,t3) - call interleave9(t3,-1,t2) - -! Get the metric table - bias=0.37 - scale=10 !Optimize? - open(19,file='met8.21',status='old') - - do i=0,255 - read(19,*) x00,x0,x1 - mettab(i,0)=nint(scale*(x0-bias)) - mettab(i,1)=nint(scale*(x1-bias)) !### Check range, etc. ### - enddo - close(19) - - nbits=72 - ndelta=17 - limit=1000 - do i=1,206 - i4=-10 - if(t2(i).eq.1) i4=10 - i4=i4+128 - t2(i)=i1 - enddo - - call fano232(t2,nbits+31,mettab,ndelta,limit,t1,ncycles,metric,ierr, & - maxmetric,maxnp) - - nbytes=(nbits+7)/8 - do i=1,nbytes - n=t1(i) - t4(i)=iand(n,255) - enddo - call unpackbits(t4,nbytes,8,tmp) - call packbits(tmp,12,6,t4) - do i=1,12 - if(t4(i).lt.128) t1(i)=t4(i) - if(t4(i).ge.128) t1(i)=t4(i)-256 - enddo - do i=1,12 - t4(i)=t1(i) - enddo - print*,'z0',d0(1:12)-t4(1:12) - return end subroutine genjt9 diff --git a/libm65/jt9sim.f90 b/libm65/jt9sim.f90 index 88f2dd5c3..f5fcf85ee 100644 --- a/libm65/jt9sim.f90 +++ b/libm65/jt9sim.f90 @@ -6,8 +6,27 @@ program jt9sim integer ihdr(11) integer*2 iwave !Generated waveform (no noise) real*8 f0,f,dt,twopi,phi,dphi,baud,fspan - character msg0*22,message*22,msgsent*22,arg*8,fname*11 + character msg*22,msg0*22,message*22,msgsent*22,arg*8,fname*11 integer*4 d6(85) + + integer*4 mettab(0:255,0:1) + integer*4 t0(13) !72-bit message as 6-bit words + integer*1 t1(13) !72 bits and zero tail as 8-bit bytes + integer*1 t2(207) !Encoded information-carrying bits + integer*1 t3(207) !Bits from t4, after interleaving + integer*4 t4(69) !Symbols from t5, values 0-7 + integer*4 t5(69) !Gray-coded symbols, values 0-7 + integer*4 t6(85) !Channel symbols including sync, values 0-8 + integer*1 tmp(72) + integer*1 i1 + equivalence (i1,i4) + integer isync(85) !Sync vector + data isync/ & + 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0, & + 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0, & + 0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0, & + 0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0, & + 1,0,0,0,1/ common/acom/dat(NMAX),iwave(NMAX) nargs=iargc() @@ -51,6 +70,18 @@ program jt9sim open(12,file='msgs.txt',status='old') +! Get the metric table + bias=0.37 + scale=10 !Optimize? + open(19,file='met8.21',status='old') + + do i=0,255 + read(19,*) x00,x0,x1 + mettab(i,0)=nint(scale*(x0-bias)) + mettab(i,1)=nint(scale*(x1-bias)) !### Check range, etc. ### + enddo + close(19) + write(*,1000) 1000 format('File N freq S/N Message'/ & '---------------------------------------------------') @@ -113,6 +144,49 @@ program jt9sim write(10) ihdr,iwave(1:npts) close(10) + +! We're done! Now decode the data in d6, as a test. + j=0 + do i=1,85 + if(isync(i).eq.1) cycle + j=j+1 + t5(j)=d6(i)-1 + enddo + + call graycode(t5,69,-1,t4) + call unpackbits(t4,69,3,t3) + call interleave9(t3,-1,t2) + + nbits=72 + ndelta=17 + limit=1000 + do i=1,206 + i4=-10 + if(t2(i).eq.1) i4=10 + i4=i4+128 + t2(i)=i1 + enddo + + call fano232(t2,nbits+31,mettab,ndelta,limit,t1,ncycles,metric,ierr, & + maxmetric,maxnp) + + nbytes=(nbits+7)/8 + do i=1,nbytes + n=t1(i) + t4(i)=iand(n,255) + enddo + call unpackbits(t4,nbytes,8,tmp) + call packbits(tmp,12,6,t4) + do i=1,12 + if(t4(i).lt.128) t1(i)=t4(i) + if(t4(i).ge.128) t1(i)=t4(i)-256 + enddo + do i=1,12 + t4(i)=t1(i) + enddo + call unpackmsg(t4,msg) !Unpack decoded msg + if(msg.ne.msg0) print*,'Decode error: ',msg0,' ',msg + enddo 999 end program jt9sim diff --git a/libm65/symspecx.f90 b/libm65/symspecx.f90 index 0ac762a46..77ff5708c 100644 --- a/libm65/symspecx.f90 +++ b/libm65/symspecx.f90 @@ -12,7 +12,7 @@ subroutine symspecx(k,nsps,ndiskdat,nb,nbslider,pxdb,s,nkhz,ihsym, & ! nzap number of samples zero'ed by noise blanker parameter (NMAX=1800*12000) !Total sample intervals per 30 minutes - parameter (NSMAX=4400) !Max length of saved spectra + parameter (NSMAX=15750) !Max length of saved spectra parameter (MAXFFT=262144) !Max length of FFTs integer*2 id2 real*8 ts,hsym diff --git a/mainwindow.cpp b/mainwindow.cpp index ce9415bef..5d1975cb9 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1307,7 +1307,7 @@ void MainWindow::on_actionJT8_1_triggered() { m_mode="JT8-1"; m_TRperiod=60; - m_nsps=7168; + m_nsps=6912; soundInThread.setPeriod(m_TRperiod,m_nsps); soundOutThread.setPeriod(m_TRperiod,m_nsps); g_pWideGraph->setPeriod(m_TRperiod,m_nsps); @@ -1320,7 +1320,7 @@ void MainWindow::on_actionJT8_2_triggered() { m_mode="JT8-2"; m_TRperiod=120; - m_nsps=16000; + m_nsps=15360; soundInThread.setPeriod(m_TRperiod,m_nsps); soundOutThread.setPeriod(m_TRperiod,m_nsps); g_pWideGraph->setPeriod(m_TRperiod,m_nsps); @@ -1333,7 +1333,7 @@ void MainWindow::on_actionJT8_5_triggered() { m_mode="JT8-5"; m_TRperiod=300; - m_nsps=42336; + m_nsps=40960; soundInThread.setPeriod(m_TRperiod,m_nsps); soundOutThread.setPeriod(m_TRperiod,m_nsps); g_pWideGraph->setPeriod(m_TRperiod,m_nsps); @@ -1346,7 +1346,7 @@ void MainWindow::on_actionJT8_10_triggered() { m_mode="JT8-10"; m_TRperiod=600; - m_nsps=86400; + m_nsps=82944; soundInThread.setPeriod(m_TRperiod,m_nsps); soundOutThread.setPeriod(m_TRperiod,m_nsps); g_pWideGraph->setPeriod(m_TRperiod,m_nsps); @@ -1359,7 +1359,7 @@ void MainWindow::on_actionJT8_30_triggered() { m_mode="JT8-30"; m_TRperiod=1800; - m_nsps=262144; + m_nsps=250880; soundInThread.setPeriod(m_TRperiod,m_nsps); soundOutThread.setPeriod(m_TRperiod,m_nsps); g_pWideGraph->setPeriod(m_TRperiod,m_nsps); diff --git a/plotter.cpp b/plotter.cpp index 272f2b8ff..61f392431 100644 --- a/plotter.cpp +++ b/plotter.cpp @@ -32,7 +32,7 @@ CPlotter::CPlotter(QWidget *parent) : //CPlotter Constructor m_fQSO = 125; m_line = 0; m_fSample = 12000; - m_nsps=7168; + m_nsps=6912; m_paintAllZoom = false; } @@ -62,7 +62,7 @@ void CPlotter::resizeEvent(QResizeEvent* ) //resizeEvent() m_WaterfallPixmap.fill(Qt::black); m_ZoomWaterfallPixmap.fill(Qt::black); m_2DPixmap.fill(Qt::black); - memset(m_zwf,0,32768*h); + memset(m_zwf,0,NSMAX*h); m_ScalePixmap = QPixmap(w,30); m_ZoomScalePixmap = QPixmap(w,30); //(no change on resize...) m_ScalePixmap.fill(Qt::white); @@ -97,9 +97,11 @@ void CPlotter::paintEvent(QPaintEvent *) // paintEvent() QRect source(0,0,w,30); painter.drawPixmap(target,m_ZoomScalePixmap,source); - float df=m_fSample/32768.0; - int x0=16384 + (0.001*(m_ZoomStartFreq+m_fCal)+m_fQSO-m_nkhz+1.27046) * \ - 1000.0/df + 0.5; +// float df=m_fSample/32768.0; +// int x0=16384 + (0.001*(m_ZoomStartFreq+m_fCal)+m_fQSO-m_nkhz+1.27046) * \ +// 1000.0/df + 0.5; + float df=12000.0/m_nsps; + int x0=0; //### TEMP ### QPainter painter2(&m_ZoomWaterfallPixmap); for(int i=0; i254) y1=254; - if (s[i]>1.e29) y1=255; + if (swide[i]>1.e29) y1=255; m_hist1[y1]++; painter1.setPen(m_ColorTbl[y1]); painter1.drawPoint(i,0); @@ -185,7 +187,7 @@ void CPlotter::draw(float s[], int i0, float splot[]) //draw() int y2 = gain*(y + 34 -m_plotZero); if (y2<0) y2=0; if (y2>254) y2=254; - if (s[i]>1.e29) y2=255; + if (swide[i]>1.e29) y2=255; if(strong != strong0 or i==w-1) { painter2D.drawPolyline(LineBuf,j); j=0; @@ -199,9 +201,9 @@ void CPlotter::draw(float s[], int i0, float splot[]) //draw() } } - for(i=0; i<32768; i++) { + for(i=0; i254) y1=254; if (splot[i]>1.e29) y1=255; @@ -209,7 +211,7 @@ void CPlotter::draw(float s[], int i0, float splot[]) //draw() m_zwf[i]=y1; } - if(s[0]>1.0e29) m_line=0; + if(swide[0]>1.0e29) m_line=0; m_line++; if(m_line == 13) { UTCstr(); diff --git a/soundin.cpp b/soundin.cpp index 3544b0400..ba217b336 100644 --- a/soundin.cpp +++ b/soundin.cpp @@ -2,13 +2,14 @@ #include #define FRAMES_PER_BUFFER 1024 +#define NSMAX 15750 extern "C" { #include extern struct { short int d2[1800*12000]; //This is "common/jt8com/..." in fortran - float ss[184*4400]; - float savg[4400]; + float ss[184*NSMAX]; + float savg[NSMAX]; double fcenter; //USB dial freq (kHz) int nutc; //UTC as integer, HHMM int ntrperiod; //TR period (seconds) diff --git a/widegraph.cpp b/widegraph.cpp index c34d979ac..fceb7ccb3 100644 --- a/widegraph.cpp +++ b/widegraph.cpp @@ -1,7 +1,7 @@ #include "widegraph.h" #include "ui_widegraph.h" -#define NSMAX 4400 +#define NSMAX 15750 WideGraph::WideGraph(QWidget *parent) : QDialog(parent), @@ -89,6 +89,7 @@ void WideGraph::dataSink2(float s[], int nkhz, int ihsym, int ndiskdata, } //Average spectra over specified number, m_waterfallAvg +// qDebug() << "A" << ihsym << NSMAX << df << nbpp; if (n==0) { for (int i=0; iwidePlot->startFreq()) ui->widePlot->SetStartFreq(sf); // int i0=16384.0+(ui->widePlot->startFreq()-nkhz+1.27046+0.001*m_fCal) * // 1000.0/df + 0.5; + int i0=0; //### nbpp=1; //### + int i=i0; for (int j=0; j<2048; j++) { - smax=0; - for (int k=0; ksmax) smax=splot[i]; - } - swide[j]=smax; - if(lstrong[1 + i/32]!=0) swide[j]=-smax; //Tag strong signals + smax=0; + for (int k=0; ksmax) smax=splot[i]; + i++; + } + swide[j]=smax; +/* + float sum=0; + for (int k=0; k