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=4000,nrxfreq=1500,ntrperiod=1,ndepth=1 logical :: shmem = .false., read_files = .false., have_args = .false., & tx9 = .false., display_help = .false. type (option) :: long_options(17) = [ & 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 ('jt4', .false., '4', 'JT4 mode', ''), & option ('depth', .true., 'd', 'JT9 decoding depth (1-3), default=1', & ''), & option ('tx-jt9', .false., 'T', 'Tx mode is JT9, default=JT65', '') ] character datetime*20,mycall*12,mygrid*6,hiscall*12,hisgrid*6 common/jt9com/ss(184,NSMAX),savg(NSMAX),id2(NMAX),nutc,ndiskdat, & ntr,mousefqso,newdat,npts8a,nfa,nfsplit,nfb,ntol,kin,nzhsym, & nsubmode,nagain,ndepth,ntxmode,nmode,minw,nclearave,emedelay, & dttol,nlist,listutc(10),datetime,mycall,mygrid,hiscall,hisgrid common/tracer/limtrace,lu common/patience/npatience,nthreads common/decstats/num65,numbm,numkv,num9,numfano,infile data npatience/1/,nthreads/1/ do call getopt('hs:e:a:r:m:p:d:f:w:t:964TL: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 ('4') read_files = .true. mode = 4 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 ! Default to 1 thread, but use nthreads for the big ones call fftwf_plan_with_nthreads(1) ! 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() !We're running under control of WSJT-X go to 999 endif limtrace=0 !We're running jt9 in stand-alone mode 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 nfsample=ihdr(7) nutc0=ihdr(1) !Silence compiler warning i1=index(infile,'.wav') if(i1.lt.1) i1=index(infile,'.WAV') if(infile(i1-5:i1-5).eq.'_') then read(infile(i1-4:i1-1),*,err=1) nutc0 else read(infile(i1-6:i1-3),*,err=1) nutc0 endif go to 2 1 nutc0=0 2 nsps=0 if(ntrperiod.eq.1) then nsps=6912 nzhsym=181 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=3) id2(k-kstep+1:k) go to 4 3 call timer('read_wav',1) print*,'EOF on input file ',infile exit 4 call timer('read_wav',1) nhsym=(k-2048)/kstep if(nhsym.ge.1 .and. nhsym.ne.nhsym0) then if(mode.eq.9 .or. mode.eq.74) then ! Compute rough symbol spectra for the JT9 decoder ingain=0 call timer('symspec ',0) call symspec(k,ntrperiod,nsps,ingain,pxdb,s,df3, & ihsym,npts8) call timer('symspec ',1) endif nhsym0=nhsym if(nhsym.ge.181) exit endif enddo 10 close(10) call fillcom(nutc0,ndepth,nrxfreq,mode,tx9,flow,fsplit,fhigh) call decoder(ss,id2,nfsample) 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,numbm+numkv,num65,numfano,num9 1100 format(58('-')/' BM KV JT65 Tries JT9 Tries'/ & 58('-')/6i8) ! 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