program jt9 ! Decoder for JT9. Can run stand-alone, reading data from *.wav files; ! or as the back end of wsjt-x, with data placed in a shared memory region. use options use prog_args use, intrinsic :: iso_c_binding use FFTW3 include 'constants.f90' integer(C_INT) iret integer*4 ihdr(11) real*4 s(NSMAX) integer*2 id2 character c character(len=500) optarg, infile character wisfile*80 integer :: arglen,stat,offset,remain,mode=0,flow=200,fsplit=2700,fhigh=4007,nrxfreq=1500,ntrperiod=1,ndepth=1 logical :: shmem = .false., read_files = .false., have_args = .false., tx9 = .false., display_help = .false. type (option) :: long_options(16) = [ & option ('help', .false., 'h', 'Display this help message', ''), & option ('shmem', .true., 's', 'Use shared memory for sample data', ''), & option ('tr-period', .true., 'p', 'Tx/Rx period, default=1', ''), & option ('executable-path', .true., 'e', 'Location of subordinate executables (KVASD) default="."', ''), & option ('data-path', .true., 'a', 'Location of writeable data files, detfault="."', ''), & option ('temp-path', .true., 't', 'Temporary files path, default="."', ''), & option ('lowest', .true., 'L', 'Lowest frequency decoded (JT65), default=200Hz', ''), & option ('highest', .true., 'H', 'Highest frequency decoded, default=4007Hz', ''), & option ('split', .true., 'S', 'Lowest JT9 frequency decoded, default=2700Hz', ''), & option ('rx-frequency', .true., 'f', 'Receive frequency offset, default=1500', ''), & option ('patience', .true., 'w', 'FFTW3 planing patience (0-4), default=1', ''), & option ('fft-threads', .true., 'm', 'Number of threads to process large FFTs, default=1', ''), & option ('jt65', .false., '6', 'JT65 mode', ''), & option ('jt9', .false., '9', 'JT9 mode', ''), & option ('depth', .true., 'd', 'JT9 decoding depth (1-3), default=1', ''), & option ('tx-jt9', .false., 'T', 'Tx mode is JT9, default=JT65', '') ] common/jt9com/ss(184,NSMAX),savg(NSMAX),id2(NMAX),nutc,ndiskdat,ntr, & mousefqso,newdat,nfa,nfsplit,nfb,ntol,kin,nzhsym,nsynced,ndecoded common/tracer/limtrace,lu common/patience/npatience,nthreads common/decstats/num65,numbm,numkv,num9,numfano data npatience/1/,nthreads/1/ do call getopt('hs:e:a:r:m:p:d:f:w:t:96TL:S:H:',long_options,c,optarg,arglen,stat, & offset,remain) if (stat .ne. 0) then exit end if have_args = .true. select case (c) case ('h') display_help = .true. case ('s') shmem = .true. shm_key = optarg(:arglen) case ('e') exe_dir = optarg(:arglen) case ('a') data_dir = optarg(:arglen) case ('t') temp_dir = optarg(:arglen) case ('m') read (optarg(:arglen), *) nthreads case ('p') read_files = .true. read (optarg(:arglen), *) ntrperiod case ('d') read_files = .true. read (optarg(:arglen), *) ndepth case ('f') read_files = .true. read (optarg(:arglen), *) nrxfreq case ('L') read_files = .true. read (optarg(:arglen), *) flow case ('S') read_files = .true. read (optarg(:arglen), *) fsplit case ('H') read_files = .true. read (optarg(:arglen), *) fhigh case ('6') read_files = .true. if (mode.lt.65) mode = mode + 65 case ('9') read_files = .true. if (mode.lt.9.or.mode.eq.65) mode = mode + 9 case ('T') read_files = .true. tx9 = .true. case ('w') read (optarg(:arglen), *) npatience end select end do if (display_help .or. .not. have_args .or. (stat .lt. 0 .or. (shmem .and. remain .gt. 0) & .or. (read_files .and. remain .eq. 0) .or. & (shmem .and. read_files))) then print*,'Usage: jt9 -p OPTIONS file1 [file2 ...]' print*,' Reads data from *.wav files.' print*,'' print*,' jt9 -s [-w n] [-m n] [-e path] [-a path] [-t path]' print*,' Gets data from shared memory region with key==' do i = 1, size (long_options) print*,'' call long_options(i) % print (6) end do go to 999 endif iret=fftwf_init_threads() !Initialize FFTW threading call fftwf_plan_with_nthreads(1) !Default to 1 thread but use nthreads for the big ones ! Import FFTW wisdom, if available wisfile=trim(data_dir)//'/jt9_wisdom.dat'// C_NULL_CHAR iret=fftwf_import_wisdom_from_filename(wisfile) num65=0 numbm=0 numkv=0 num9=0 numfano=0 if (shmem) then call jt9a() go to 999 endif limtrace=0 lu=12 nflatten=0 do iarg = offset + 1, offset + remain call get_command_argument (iarg, optarg, arglen) infile = optarg(:arglen) open(10,file=infile,access='stream',status='old',err=998) read(10) ihdr nutc0=ihdr(1) !Silence compiler warning i1=index(infile,'.wav') read(infile(i1-4:i1-1),*,err=1) nutc0 go to 2 1 nutc0=0 2 nsps=0 if(ntrperiod.eq.1) then nsps=6912 nzhsym=173 else if(ntrperiod.eq.2) then nsps=15360 nzhsym=178 else if(ntrperiod.eq.5) then nsps=40960 nzhsym=172 else if(ntrperiod.eq.10) then nsps=82944 nzhsym=171 else if(ntrperiod.eq.30) then nsps=252000 nzhsym=167 endif if(nsps.eq.0) stop 'Error: bad TRperiod' kstep=nsps/2 k=0 nhsym0=-999 npts=(60*ntrperiod-6)*12000 if(iarg .eq. offset + 1) then open(12,file=trim(data_dir)//'/timer.out',status='unknown') call timer('jt9 ',0) endif id2=0 !??? Why is this necessary ??? do iblk=1,npts/kstep k=iblk*kstep call timer('read_wav',0) read(10,end=10) id2(k-kstep+1:k) call timer('read_wav',1) nhsym=(k-2048)/kstep if(nhsym.ge.1 .and. nhsym.ne.nhsym0) then ! Emit signal readyForFFT ingain=0 call timer('symspec ',0) call symspec(k,ntrperiod,nsps,ingain,nflatten,pxdb,s,df3,ihsym,npts8) call timer('symspec ',1) nhsym0=nhsym if(ihsym.ge.173) go to 10 endif enddo 10 close(10) call fillcom(nutc0,ndepth,nrxfreq,mode,tx9,flow,fsplit,fhigh) call decoder(ss,id2) enddo call timer('jt9 ',1) call timer('jt9 ',101) go to 999 998 print*,'Cannot open file:' print*,infile 999 continue ! Output decoder statistics write(12,1100) numbm,numkv,num65 1100 format(/'JT65: ',i6,' BM and',i7,' KV in',i7,' attempts') write(12,1110) numfano,num9 1110 format( 'JT9: ',i6,' Fano in',i7,' attempts') ! Save wisdom and free memory iret=fftwf_export_wisdom_to_filename(wisfile) call four2a(a,-1,1,1,1) call filbig(a,-1,1,0.0,0,0,0,0,0) !used for FFT plans call fftwf_cleanup_threads() call fftwf_cleanup() end program jt9