Add files for command-line program ft2[.exe].

This commit is contained in:
Joe Taylor 2019-01-14 16:39:50 -05:00
parent 348e7c8ad5
commit 8a6cee7e26
16 changed files with 2549 additions and 0 deletions

154
lib/ft2/ft2.f90 Normal file
View File

@ -0,0 +1,154 @@
program ft2
use packjt77
include 'gcom1.f90'
integer ft2audio,ptt
logical allok
character*20 pttport
character*8 arg
integer*2 iwave2(30000)
allok=.true.
! Get home-station details
open(10,file='ft2.ini',status='old',err=1)
go to 2
1 print*,'Cannot open ft2.ini'
allok=.false.
2 read(10,*,err=3) mycall,mygrid,ndevin,ndevout,pttport,exch
go to 4
3 print*,'Error reading ft2.ini'
allok=.false.
4 if(index(pttport,'/').lt.1) read(pttport,*) nport
hiscall=' '
hiscall_next=' '
idevin=ndevin
idevout=ndevout
call padevsub(idevin,idevout)
if(idevin.ne.ndevin .or. idevout.ne.ndevout) allok=.false.
i1=ptt(nport,1,1,iptt)
if(i1.lt.0 .and. nport.ne.0) allok=.false.
if(.not.allok) then
write(*,"('Please fix setup error(s) and restart.')")
go to 999
endif
nright=1
iwrite=0
iwave=0
nwave=NTZ
nfsample=12000
ngo=1
npabuf=1280
ntxok=0
ntransmitting=0
tx_once=.false.
snrdb=99.0
txmsg='CQ K1JT FN20'
nargs=iargc()
if(nargs.eq.3) then
call getarg(1,txmsg)
call getarg(2,arg)
read(arg,*) f0
call getarg(3,arg)
read(arg,*) snrdb
nTxOK=1
tx_once=.true.
call ft2_iwave(txmsg,f0,snrdb,iwave)
endif
iwave2(1:23040)=iwave
iwave2(23041:30000)=0
nutc=0
nfqso=nint(f0)
call ft2_decode(nutc,nfqso,iwave2)
ierr=ft2audio(idevin,idevout,npabuf,nright,y1,y2,NRING,iwrite,itx, &
iwave,nwave,nfsample,nTxOK,nTransmitting,ngo)
if(ierr.ne.0) then
print*,'Error',ierr,' in JTaudio, you will only be able to work offline.'
else
write(*,1006)
1006 format('Audio streams terminated normally.')
endif
999 end program ft2
subroutine update(total_time,ic1,ic2)
real*8 total_time
integer ptt
logical transmitted
integer*2 id(30000),id2(30000)
include 'gcom1.f90'
data nt0/-1/,transmitted/.false./,snr/0.0/
save nt0,transmitted,snr
if(ic1.ne.0 .or. ic2.ne.0) then
if(ic1.eq.27 .and. ic2.eq.0) ngo=0 !ESC
if(nTxOK.eq.0 .and. ntransmitting.eq.0) then
nd=0
if(ic1.eq.0 .and. ic2.eq.59) nd=7 !F1
if(ic1.eq.0 .and. ic2.eq.60) nd=6 !F2
if(ic1.eq.0 .and. ic2.eq.61) nd=5 !F3
if(ic1.eq.0 .and. ic2.eq.62) nd=4 !F4
if(ic1.eq.0 .and. ic2.eq.63) nd=3 !F5
if(nd.gt.0) then
i1=ptt(nport,1,1,iptt)
ntxok=1
n=1000
nwave=NTZ
do i=1,nwave/nd
ib=i*nd
ia=ib-nd+1
iwave(ia:ib)=n
n=-n
enddo
endif
endif
if(ic1.eq.13 .and. ic2.eq.0) hiscall=hiscall_next
endif
if(ntransmitting.eq.1) transmitted=.true.
if(ntransmitting.eq.0) then
if(iptt.eq.1 .and. nport.gt.0) i1=ptt(nport,0,1,iptt)
if(tx_once .and. transmitted) stop
endif
nt=2*total_time
if(nt.gt.nt0 .or. ic1.ne.0 .or. ic2.ne.0) then
k=iwrite-6000
if(k.lt.1) k=k+NRING
sq=0.
do i=1,6000
k=k+1
if(k.gt.NRING) k=k-NRING
x=y1(k)
sq=sq + x*x
enddo
sigdb=0.
if(sq.gt.0.0) sigdb=db(sq/6000.0)
k=iwrite-30000
if(k.lt.1) k=k+NRING
do i=1,30000
k=k+1
if(k.gt.NRING) k=k-NRING
id(i)=y1(k)
enddo
nutc=0
nfqso=1500
call ft2_iwave(txmsg,1500.0,snr,id2) !###
snr=snr-1.0
call ft2_decode(nutc,nfqso,id2) !###
!### call ft2_decode(nutc,nfqso,id)
write(*,1010) nt,total_time,iwrite,itx,ntxok,ntransmitting,sigdb,snr
1010 format(i6,f9.3,4i6,f6.1,f6.0)
nt0=nt
max1=0
max2=0
endif
return
end subroutine update

2
lib/ft2/ft2.ini Normal file
View File

@ -0,0 +1,2 @@
K1JT FN20 1 5 0 NJ
MyCall MyGrid AudioIn AudioOut PTTport Exch

277
lib/ft2/ft2_decode.f90 Normal file
View File

@ -0,0 +1,277 @@
subroutine ft2_decode(nutc,nfqso,iwave)
use crc
use packjt77
include 'ft2_params.f90'
character message*37,c77*77
character*37 decodes(100)
character*120 data_dir
complex c2(0:NMAX/16-1) !Complex waveform
complex cb(0:NMAX/16-1)
complex cd(0:144*10-1) !Complex waveform
complex c1(0:9),c0(0:9)
complex ccor(0:1,144)
complex csum,cterm,cc0,cc1,csync1
real*8 fMHz
real a(5)
real rxdata(128),llr(128) !Soft symbols
real llr2(128)
real sbits(144),sbits1(144),sbits3(144)
real ps(0:8191),psbest(0:8191)
real candidate(3,100)
real savg(NH1)
integer*2 iwave(NMAX) !Generated full-length waveform
integer*1 message77(77),apmask(128),cw(128)
integer*1 hbits(144),hbits1(144),hbits3(144)
integer*1 s16(16)
logical unpk77_success
data s16/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0/
fs=12000.0/NDOWN !Sample rate
dt=1/fs !Sample interval after downsample (s)
tt=NSPS*dt !Duration of "itone" symbols (s)
baud=1.0/tt !Keying rate for "itone" symbols (baud)
txt=NZ*dt !Transmission length (s)
twopi=8.0*atan(1.0)
h=0.8 !h=0.8 seems to be optimum for AWGN sensitivity (not for fading)
dphi=twopi/2*baud*h*dt*16 ! dt*16 is samp interval after downsample
dphi0=-1*dphi
dphi1=+1*dphi
phi0=0.0
phi1=0.0
do i=0,9
c1(i)=cmplx(cos(phi1),sin(phi1))
c0(i)=cmplx(cos(phi0),sin(phi0))
phi1=mod(phi1+dphi1,twopi)
phi0=mod(phi0+dphi0,twopi)
enddo
the=twopi*h/2.0
cc1=cmplx(cos(the),-sin(the))
cc0=cmplx(cos(the),sin(the))
data_dir="."
fMHz=7.074
ncoh=1
candidate=0.0
ncand=0
fa=375.0
fb=3000.0
syncmin=0.2
maxcand=100
nfqso=-1
call getcandidates2(iwave,fa,fb,maxcand,savg,candidate,ncand)
ndecodes=0
do icand=1,ncand
f0=candidate(1,icand)
! print*,'A',ncand,f0
xsnr=1.0
if( f0.le.375.0 .or. f0.ge.(5000.0-375.0) ) cycle
call ft2_downsample(iwave,f0,c2) ! downsample from 160s/Symbol to 10s/Symbol
! 750 samples/second here
ibest=-1
sybest=-99.
dfbest=-1.
!### do if=-15,+15
do if=-30,30
df=if
a=0.
a(1)=-df
call twkfreq1(c2,NMAX/16,fs,a,cb)
do is=0,374
csync1=0.
cterm=1
do ib=1,16
i1=(ib-1)*10+is
i2=i1+136*10
if(s16(ib).eq.1) then
csync1=csync1+sum(cb(i1:i1+9)*conjg(c1(0:9)))*cterm
cterm=cterm*cc1
else
csync1=csync1+sum(cb(i1:i1+9)*conjg(c0(0:9)))*cterm
cterm=cterm*cc0
endif
enddo
! write(60,3001) if,is,abs(csync1)
!3001 format(2i6,f10.3)
if(abs(csync1).gt.sybest) then
ibest=is
sybest=abs(csync1)
dfbest=df
endif
enddo
enddo
!dfbest=0.0
!ibest=187
a=0.
a(1)=-dfbest
call twkfreq1(c2,NMAX/16,fs,a,cb)
ib=ibest
cd=cb(ib:ib+144*10-1)
s2=sum(real(cd*conjg(cd)))/(10*144)
cd=cd/sqrt(s2)
do nseq=1,5
if( nseq.eq.1 ) then ! noncoherent single-symbol detection
sbits1=0.0
do ibit=1,144
ib=(ibit-1)*10
ccor(1,ibit)=sum(cd(ib:ib+9)*conjg(c1(0:9)))
ccor(0,ibit)=sum(cd(ib:ib+9)*conjg(c0(0:9)))
sbits1(ibit)=abs(ccor(1,ibit))-abs(ccor(0,ibit))
hbits1(ibit)=0
if(sbits1(ibit).gt.0) hbits1(ibit)=1
enddo
sbits=sbits1
hbits=hbits1
sbits3=sbits1
hbits3=hbits1
elseif( nseq.ge.2 ) then
nbit=2*nseq-1
numseq=2**(nbit)
ps=0
do ibit=nbit/2+1,144-nbit/2
ps=0.0
pmax=0.0
do iseq=0,numseq-1
csum=0.0
cterm=1.0
k=1
do i=nbit-1,0,-1
ibb=iand(iseq/(2**i),1)
csum=csum+ccor(ibb,ibit-(nbit/2+1)+k)*cterm
if(ibb.eq.0) cterm=cterm*cc0
if(ibb.eq.1) cterm=cterm*cc1
k=k+1
enddo
ps(iseq)=abs(csum)
if( ps(iseq) .gt. pmax ) then
pmax=ps(iseq)
ibflag=1
endif
enddo
if( ibflag .eq. 1 ) then
psbest=ps
ibflag=0
endif
call getbitmetric(2**(nbit/2),psbest,numseq,sbits3(ibit))
hbits3(ibit)=0
if(sbits3(ibit).gt.0) hbits3(ibit)=1
enddo
sbits=sbits3
hbits=hbits3
endif
nsync_qual=count(hbits(1:16).eq.s16)
if(nsync_qual.lt.10) exit
rxdata=sbits(17:144)
rxav=sum(rxdata(1:128))/128.0
rx2av=sum(rxdata(1:128)*rxdata(1:128))/128.0
rxsig=sqrt(rx2av-rxav*rxav)
rxdata=rxdata/rxsig
sigma=0.80
llr(1:128)=2*rxdata/(sigma*sigma)
apmask=0
max_iterations=40
do ibias=0,0
llr2=llr
if(ibias.eq.1) llr2=llr+0.4
if(ibias.eq.2) llr2=llr-0.4
call bpdecode128_90(llr2,apmask,max_iterations,message77,cw,nharderror,niterations)
if(nharderror.ge.0) exit
enddo
nhardmin=-1
if(sum(message77).eq.0) cycle
if( nharderror.ge.0 ) then
write(c77,'(77i1)') message77(1:77)
call unpack77(c77,message,unpk77_success)
idupe=0
do i=1,ndecodes
if(decodes(i).eq.message) idupe=1
enddo
if(idupe.eq.1) goto 888
ndecodes=ndecodes+1
decodes(ndecodes)=message
nsnr=nint(xsnr)
freq=f0+dfbest
write(*,1212) nutc,nsnr,ibest/750.0,nint(freq),message, &
nseq,nharderror,nhardmin
1212 format(i4.4,i4,f6.2,i6,2x,a37,3i5)
goto 888
endif
enddo ! nseq
888 continue
enddo !candidate list
end subroutine ft2_decode
subroutine getbitmetric(ib,ps,ns,xmet)
real ps(0:ns-1)
xm1=0
xm0=0
do i=0,ns-1
if( iand(i/ib,1) .eq. 1 .and. ps(i) .gt. xm1 ) xm1=ps(i)
if( iand(i/ib,1) .eq. 0 .and. ps(i) .gt. xm0 ) xm0=ps(i)
enddo
xmet=xm1-xm0
return
end subroutine getbitmetric
subroutine downsample2(ci,f0,co)
parameter(NI=144*160,NH=NI/2,NO=NI/16) ! downsample from 200 samples per symbol to 10
complex ci(0:NI-1),ct(0:NI-1)
complex co(0:NO-1)
fs=12000.0
df=fs/NI
ct=ci
call four2a(ct,NI,1,-1,1) !c2c FFT to freq domain
i0=nint(f0/df)
ct=cshift(ct,i0)
co=0.0
co(0)=ct(0)
b=8.0
do i=1,NO/2
arg=(i*df/b)**2
filt=exp(-arg)
co(i)=ct(i)*filt
co(NO-i)=ct(NI-i)*filt
enddo
co=co/NO
call four2a(co,NO,1,1,1) !c2c FFT back to time domain
return
end subroutine downsample2
subroutine ft2_downsample(iwave,f0,c)
! Input: i*2 data in iwave() at sample rate 12000 Hz
! Output: Complex data in c(), sampled at 1200 Hz
include 'ft2_params.f90'
parameter (NFFT2=NMAX/16)
integer*2 iwave(NMAX)
complex c(0:NMAX/16-1)
complex c1(0:NFFT2-1)
complex cx(0:NMAX/2)
real x(NMAX)
equivalence (x,cx)
BW=4.0*75
df=12000.0/NMAX
x=iwave
call four2a(x,NMAX,1,-1,0) !r2c FFT to freq domain
ibw=nint(BW/df)
i0=nint(f0/df)
c1=0.
c1(0)=cx(i0)
do i=1,NFFT2/2
arg=(i-1)*df/bw
win=exp(-arg*arg)
c1(i)=cx(i0+i)*win
c1(NFFT2-i)=cx(i0-i)*win
enddo
c1=c1/NFFT2
call four2a(c1,NFFT2,1,1,1) !c2c FFT back to time domain
c=c1(0:NMAX/16-1)
return
end subroutine ft2_downsample

66
lib/ft2/ft2_iwave.f90 Normal file
View File

@ -0,0 +1,66 @@
subroutine ft2_iwave(msg37,f0,snrdb,iwave)
! Generate waveform for experimental "FT2" mode
use packjt77
include 'ft2_params.f90' !Set various constants
parameter (NWAVE=NN*NSPS)
character msg37*37,msgsent37*37
real wave(NMAX)
integer itone(NN)
integer*2 iwave(NMAX) !Generated full-length waveform
twopi=8.0*atan(1.0)
fs=12000.0 !Sample rate (Hz)
dt=1.0/fs !Sample interval (s)
hmod=0.8 !Modulation index (MSK=0.5, FSK=1.0)
tt=NSPS*dt !Duration of symbols (s)
baud=1.0/tt !Keying rate (baud)
bw=1.5*baud !Occupied bandwidth (Hz)
txt=NZ*dt !Transmission length (s)
bandwidth_ratio=2500.0/(fs/2.0)
sig=sqrt(2*bandwidth_ratio) * 10.0**(0.05*snrdb)
if(snrdb.gt.90.0) sig=1.0
txt=NN*NSPS/12000.0
! Source-encode, then get itone():
itype=1
call genft2(msg37,0,msgsent37,itone,itype)
k=0
phi=0.0
do j=1,NN !Generate real waveform
dphi=twopi*(f0*dt+(hmod/2.0)*(2*itone(j)-1)/real(NSPS))
do i=1,NSPS
k=k+1
wave(k)=sig*sin(phi)
phi=mod(phi+dphi,twopi)
enddo
enddo
kz=k
peak=maxval(abs(wave(1:kz)))
! nslots=1
! if(width.gt.0.0) call filt8(f0,nslots,width,wave)
if(snrdb.lt.90) then
do i=1,NMAX !Add gaussian noise at specified SNR
xnoise=gran()
wave(i)=wave(i) + xnoise
enddo
endif
gain=1.0
if(snrdb.lt.90.0) then
wave=gain*wave
else
datpk=maxval(abs(wave))
fac=32767.0/datpk
wave=fac*wave
endif
if(any(abs(wave).gt.32767.0)) print*,"Warning - data will be clipped."
iwave(1:kz)=nint(wave(1:kz))
return
end subroutine ft2_iwave

12
lib/ft2/ft2_params.f90 Normal file
View File

@ -0,0 +1,12 @@
! LDPC (128,90) code
parameter (KK=90) !Information bits (77 + CRC13)
parameter (ND=128) !Data symbols
parameter (NS=16) !Sync symbols (2x8)
parameter (NN=NS+ND) !Total channel symbols (144)
parameter (NSPS=160) !Samples per symbol at 12000 S/s
parameter (NZ=NSPS*NN) !Samples in full 1.92 s waveform (23040)
parameter (NMAX=30000) !Samples in iwave (2.5*12000)
parameter (NFFT1=400, NH1=NFFT1/2) !Length of FFTs for symbol spectra
parameter (NSTEP=NSPS/4) !Rough time-sync step size
parameter (NHSYM=NMAX/NSTEP-3) !Number of symbol spectra (1/4-sym steps)
parameter (NDOWN=16) !Downsample factor

330
lib/ft2/ft2audio.c Normal file
View File

@ -0,0 +1,330 @@
#include <stdio.h>
#include "portaudio.h"
#include <string.h>
int iaa;
int icc;
double total_time=0.0;
// Definition of structure pointing to the audio data
typedef struct
{
int *iwrite;
int *itx;
int *TxOK;
int *Transmitting;
int *nwave;
int *nright;
int nbuflen;
int nfs;
short *y1;
short *y2;
short *iwave;
} paTestData;
// Input callback routine:
static int
SoundIn( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
short *in = (short*)inputBuffer;
unsigned int i;
static int ia=0;
if(*data->Transmitting) return 0;
if(statusFlags!=0) printf("Status flags %d\n",(int)statusFlags);
if((statusFlags&1) == 0) {
//increment buffer pointers only if data available
ia=*data->iwrite;
if(*data->nright==0) { //Use left channel for input
for(i=0; i<framesPerBuffer; i++) {
data->y1[ia] = (*in++);
data->y2[ia] = (*in++);
ia++;
}
} else { //Use right channel
for(i=0; i<framesPerBuffer; i++) {
data->y2[ia] = (*in++);
data->y1[ia] = (*in++);
ia++;
}
}
}
if(ia >= data->nbuflen) ia=0; //Wrap buffer pointer if necessary
*data->iwrite = ia; //Save buffer pointer
iaa=ia;
total_time += (double)framesPerBuffer/12000.0;
return 0;
}
// Output callback routine:
static int
SoundOut( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
short *wptr = (short*)outputBuffer;
unsigned int i,n;
static short int n2;
static int ic=0;
static int TxOKz=0;
// printf("txOK: %d %d\n",TxOKz,*data->TxOK);
if(*data->TxOK && (!TxOKz)) ic=0;
TxOKz=*data->TxOK;
*data->Transmitting=*data->TxOK;
if(*data->TxOK) {
for(i=0 ; i < framesPerBuffer; i++ ) {
n2=data->iwave[ic];
*wptr++ = n2; //left
*wptr++ = n2; //right
ic++;
if(ic >= *data->nwave) {
*data->TxOK = 0;
*data->Transmitting = 0;
*data->iwrite = 0; //Reset Rx buffer pointer to 0
ic=0;
break;
}
}
} else {
memset((void*)outputBuffer, 0, 2*sizeof(short)*framesPerBuffer);
}
*data->itx = icc; //Save buffer pointer
icc=ic;
return 0;
}
/*******************************************************************/
int ft2audio_(int *ndevin, int *ndevout, int *npabuf, int *nright,
short y1[], short y2[], int *nbuflen, int *iwrite,
int *itx, short iwave[], int *nwave, int *nfsample,
int *TxOK, int *Transmitting, int *ngo)
{
paTestData data;
PaStream *instream, *outstream;
PaStreamParameters inputParameters, outputParameters;
// PaStreamInfo *streamInfo;
int nfpb = *npabuf;
int nSampleRate = *nfsample;
int ndevice_in = *ndevin;
int ndevice_out = *ndevout;
double dSampleRate = (double) *nfsample;
PaError err_init, err_open_in, err_open_out, err_start_in, err_start_out;
PaError err = 0;
data.iwrite = iwrite;
data.itx = itx;
data.TxOK = TxOK;
data.Transmitting = Transmitting;
data.y1 = y1;
data.y2 = y2;
data.nbuflen = *nbuflen;
data.nright = nright;
data.nwave = nwave;
data.iwave = iwave;
data.nfs = nSampleRate;
err_init = Pa_Initialize(); // Initialize PortAudio
if(err_init) {
printf("Error initializing PortAudio.\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_init),
err_init);
Pa_Terminate(); // I don't think we need this but...
return(-1);
}
// printf("Opening device %d for input, %d for output...\n",ndevice_in,ndevice_out);
inputParameters.device = ndevice_in;
inputParameters.channelCount = 2;
inputParameters.sampleFormat = paInt16;
inputParameters.suggestedLatency = 0.2;
inputParameters.hostApiSpecificStreamInfo = NULL;
// Test if this configuration actually works, so we do not run into an ugly assertion
err_open_in = Pa_IsFormatSupported(&inputParameters, NULL, dSampleRate);
if (err_open_in == 0) {
err_open_in = Pa_OpenStream(
&instream, //address of stream
&inputParameters,
NULL,
dSampleRate, //Sample rate
nfpb, //Frames per buffer
paNoFlag,
(PaStreamCallback *)SoundIn, //Callback routine
(void *)&data); //address of data structure
if(err_open_in) { // We should have no error here usually
printf("Error opening input audio stream:\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_in), err_open_in);
err = 1;
} else {
// printf("Successfully opened audio input.\n");
}
} else {
printf("Error opening input audio stream.\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_in), err_open_in);
err = 1;
}
outputParameters.device = ndevice_out;
outputParameters.channelCount = 2;
outputParameters.sampleFormat = paInt16;
outputParameters.suggestedLatency = 0.2;
outputParameters.hostApiSpecificStreamInfo = NULL;
// Test if this configuration actually works, so we do not run into an ugly assertion
err_open_out = Pa_IsFormatSupported(NULL, &outputParameters, dSampleRate);
if (err_open_out == 0) {
err_open_out = Pa_OpenStream(
&outstream, //address of stream
NULL,
&outputParameters,
dSampleRate, //Sample rate
nfpb, //Frames per buffer
paNoFlag,
(PaStreamCallback *)SoundOut, //Callback routine
(void *)&data); //address of data structure
if(err_open_out) { // We should have no error here usually
printf("Error opening output audio stream!\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_out), err_open_out);
err += 2;
} else {
// printf("Successfully opened audio output.\n");
}
} else {
printf("Error opening output audio stream.\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_out), err_open_out);
err += 2;
}
// if there was no error in opening both streams start them
if (err == 0) {
err_start_in = Pa_StartStream(instream); //Start input stream
if(err_start_in) {
printf("Error starting input audio stream!\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_start_in), err_start_in);
err += 4;
}
err_start_out = Pa_StartStream(outstream); //Start output stream
if(err_start_out) {
printf("Error starting output audio stream!\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_start_out), err_start_out);
err += 8;
}
}
if (err == 0) printf("Audio streams running normally.\n******************************************************************\n");
while( Pa_IsStreamActive(instream) && (*ngo != 0) && (err == 0) ) {
int ic1=0;
int ic2=0;
if(_kbhit()) ic1 = _getch();
if(_kbhit()) ic2 = _getch();
// if(ic1!=0 || ic2!=0) printf("%d %d %d\n",iaa,ic1,ic2);
update_(&total_time,&ic1,&ic2);
Pa_Sleep(100);
}
Pa_AbortStream(instream); // Abort stream
Pa_CloseStream(instream); // Close stream, we're done.
Pa_AbortStream(outstream); // Abort stream
Pa_CloseStream(outstream); // Close stream, we're done.
Pa_Terminate();
return(err);
}
int padevsub_(int *idevin, int *idevout)
{
int numdev,ndefin,ndefout;
int nchin[101], nchout[101];
int i, devIdx;
int numDevices;
const PaDeviceInfo *pdi;
PaError err;
Pa_Initialize();
numDevices = Pa_GetDeviceCount();
numdev = numDevices;
if( numDevices < 0 ) {
err = numDevices;
Pa_Terminate();
return err;
}
if ((devIdx = Pa_GetDefaultInputDevice()) > 0) {
ndefin = devIdx;
} else {
ndefin = 0;
}
if ((devIdx = Pa_GetDefaultOutputDevice()) > 0) {
ndefout = devIdx;
} else {
ndefout = 0;
}
printf("\nAudio Input Output Device Name\n");
printf("Device Channels Channels\n");
printf("------------------------------------------------------------------\n");
for( i=0; i < numDevices; i++ ) {
pdi = Pa_GetDeviceInfo(i);
// if(i == Pa_GetDefaultInputDevice()) ndefin = i;
// if(i == Pa_GetDefaultOutputDevice()) ndefout = i;
nchin[i]=pdi->maxInputChannels;
nchout[i]=pdi->maxOutputChannels;
printf(" %2d %2d %2d %s\n",i,nchin[i],nchout[i],pdi->name);
}
printf("\nUser requested devices: Input = %2d Output = %2d\n",
*idevin,*idevout);
printf("Default devices: Input = %2d Output = %2d\n",
ndefin,ndefout);
if((*idevin<0) || (*idevin>=numdev)) *idevin=ndefin;
if((*idevout<0) || (*idevout>=numdev)) *idevout=ndefout;
if((*idevin==0) && (*idevout==0)) {
*idevin=ndefin;
*idevout=ndefout;
}
printf("Will open devices: Input = %2d Output = %2d\n",
*idevin,*idevout);
Pa_Terminate();
return 0;
}

6
lib/ft2/g4.cmd Normal file
View File

@ -0,0 +1,6 @@
gcc -c ft2audio.c
gcc -c ptt.c
gfortran -c ../77bit/packjt77.f90
gfortran -c ../crc.f90
gfortran -o ft2 -fbounds-check -fno-second-underscore -Wall -Wno-conversion -Wno-character-truncation ft2.f90 ft2_iwave.f90 ft2_decode.f90 getcandidates2.f90 ft2audio.o ptt.o libwsjt_fort.a libwsjt_cxx.a libportaudio.a ../libfftw3f_win.a -lwinmm
rm *.o *.mod

30
lib/ft2/gcom1.f90 Normal file
View File

@ -0,0 +1,30 @@
! Variable Purpose
!---------------------------------------------------------------------------
integer NRING !Length of Rx ring buffer
integer NTZ !Length of Tx waveform in samples
parameter(NRING=32768) !About 2.7 s at 12000 sam/sec
parameter(NTZ=23040) !144*160
parameter(NMAX=30000) !2.5*12000
real snrdb
integer ndevin !Device# for audio input
integer ndevout !Device# for audio output
integer iwrite !Pointer to Rx ring buffer
integer itx !Pointer to Tx buffer
integer ngo !Set to 0 to terminate audio streams
integer nTransmitting !Actually transmitting?
integer nTxOK !OK to transmit?
integer nport !COM port for PTT
logical tx_once !Transmit one message, then exit
integer*2 y1 !Ring buffer for audio channel 0
integer*2 y2 !Ring buffer for audio channel 1
integer*2 iwave !Data for Tx audio
character*6 mycall
character*6 hiscall
character*6 hiscall_next
character*4 mygrid
character*3 exch
character*37 txmsg
common/gcom1/snrdb,ndevin,ndevout,iwrite,itx,ngo,nTransmitting,nTxOK,nport, &
tx_once, y1(NRING),y2(NRING),iwave(NTZ),mycall,hiscall, &
hiscall_next,mygrid,exch,txmsg

86
lib/ft2/genft2.f90 Normal file
View File

@ -0,0 +1,86 @@
subroutine genft2(msg0,ichk,msgsent,i4tone,itype)
! s8 + 48bits + s8 + 80 bits = 144 bits (72ms message duration)
!
! Encode an MSK144 message
! Input:
! - msg0 requested message to be transmitted
! - ichk if ichk=1, return only msgsent
! if ichk.ge.10000, set imsg=ichk-10000 for short msg
! - msgsent message as it will be decoded
! - i4tone array of audio tone values, 0 or 1
! - itype message type
! 1 = 77 bit message
! 7 = 16 bit message "<Call_1 Call2> Rpt"
use iso_c_binding, only: c_loc,c_size_t
use packjt77
character*37 msg0
character*37 message !Message to be generated
character*37 msgsent !Message as it will be received
character*77 c77
integer*4 i4tone(144)
integer*1 codeword(128)
integer*1 msgbits(77)
integer*1 bitseq(144) !Tone #s, data and sync (values 0-1)
integer*1 s16(16)
real*8 xi(864),xq(864),pi,twopi
data s16/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0/
equivalence (ihash,i1hash)
logical unpk77_success
nsym=128
pi=4.0*atan(1.0)
twopi=8.*atan(1.0)
message(1:37)=' '
itype=1
if(msg0(1:1).eq.'@') then !Generate a fixed tone
read(msg0(2:5),*,end=1,err=1) nfreq !at specified frequency
go to 2
1 nfreq=1000
2 i4tone(1)=nfreq
else
message=msg0
do i=1, 37
if(ichar(message(i:i)).eq.0) then
message(i:37)=' '
exit
endif
enddo
do i=1,37 !Strip leading blanks
if(message(1:1).ne.' ') exit
message=message(i+1:)
enddo
if(message(1:1).eq.'<') then
i2=index(message,'>')
i1=0
if(i2.gt.0) i1=index(message(1:i2),' ')
if(i1.gt.0) then
call genmsk40(message,msgsent,ichk,i4tone,itype)
if(itype.lt.0) go to 999
i4tone(41)=-40
go to 999
endif
endif
i3=-1
n3=-1
call pack77(message,i3,n3,c77)
call unpack77(c77,msgsent,unpk77_success) !Unpack to get msgsent
if(ichk.eq.1) go to 999
read(c77,"(77i1)") msgbits
call encode_128_90(msgbits,codeword)
!Create 144-bit channel vector:
bitseq=0
bitseq(1:16)=s16
bitseq(17:144)=codeword
i4tone=bitseq
endif
999 return
end subroutine genft2

View File

@ -0,0 +1,64 @@
subroutine getcandidates2(id,fa,fb,maxcand,savg,candidate,ncand)
! For now, hardwired to find the largest peak in the average spectrum
include 'ft2_params.f90'
real s(NH1,NHSYM)
real savg(NH1),savsm(NH1)
real x(NFFT1)
complex cx(0:NH1)
real candidate(3,100)
integer*2 id(NMAX)
integer*1 s8(8)
integer indx(NH1)
data s8/0,1,1,1,0,0,1,0/
equivalence (x,cx)
! Compute symbol spectra, stepping by NSTEP steps.
savg=0.
tstep=NSTEP/12000.0
df=12000.0/NFFT1 !3.125 Hz
fac=1.0/300.0
do j=1,NHSYM
ia=(j-1)*NSTEP + 1
ib=ia+NSPS-1
x(1:NSPS)=fac*id(ia:ib)
x(NSPS+1:)=0.
call four2a(x,NFFT1,1,-1,0) !r2c FFT
do i=1,NH1
s(i,j)=real(cx(i))**2 + aimag(cx(i))**2
enddo
savg=savg + s(1:NH1,j) !Average spectrum
enddo
savsm=0.
do i=2,NH1-1
savsm(i)=sum(savg(i-1:i+1))/3.
enddo
savsm(1)=savg(1)
savsm(NH1)=savg(NH1)
nfa=nint(fa/df)
nfb=nint(fb/df)
np=nfb-nfa+1
indx=0
call indexx(savsm(nfa:nfb),np,indx)
xn=savsm(nfa+indx(nint(0.3*np)))
savsm=savsm/xn
imax=-1
xmax=-99.
do i=2,NH1-1
if(savsm(i).gt.savsm(i-1).and. &
savsm(i).gt.savsm(i+1).and. &
savsm(i).gt.xmax) then
xmax=savsm(i)
imax=i
endif
enddo
f0=imax*df
if(xmax.gt.1.2) then
if(ncand.lt.maxcand) ncand=ncand+1
candidate(1,ncand)=f0
endif
return
end subroutine getcandidates2

BIN
lib/ft2/libportaudio.a Normal file

Binary file not shown.

BIN
lib/ft2/libwsjt_cxx.a Normal file

Binary file not shown.

BIN
lib/ft2/libwsjt_fort.a Normal file

Binary file not shown.

1123
lib/ft2/portaudio.h Normal file

File diff suppressed because it is too large Load Diff

58
lib/ft2/ptt.c Normal file
View File

@ -0,0 +1,58 @@
#include <windows.h>
#include <stdio.h>
int ptt_(int *nport, int *ntx, int *ndtr, int *iptt)
{
static HANDLE hFile;
static int open=0, nhold=0;
char s[10];
int i3,i4,i5,i6,i9,i00;
if(*nport==0) {
*iptt=*ntx;
return(0);
}
nhold=0;
if(*nport>100) nhold=1;
if(*ntx && (!open)) {
sprintf(s,"\\\\.\\COM%d",*nport%100);
hFile=CreateFile(
TEXT(s),
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if(hFile==INVALID_HANDLE_VALUE) {
printf("PTT: Cannot open COM port %d.\n",*nport%100);
return(-1);
}
open=1;
}
if(*ntx && open) {
if(*ndtr)
EscapeCommFunction(hFile,5); //set DTR
else
EscapeCommFunction(hFile,3); //set RTS
*iptt=1;
}
else {
if(*ndtr)
EscapeCommFunction(hFile,6); //clear DTR
else
EscapeCommFunction(hFile,4); //clear RTS
EscapeCommFunction(hFile,9); //clear BREAK
if(nhold==0) {
i00=CloseHandle(hFile);
open=0;
}
*iptt=0;
}
return(0);
}

341
lib/ft2/ptt_unix.c Normal file
View File

@ -0,0 +1,341 @@
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
//#include <sys/ioctl.h>
//#include <linux/ppdev.h>
//#include <linux/parport.h>
//#include <dev/ppbus/ppi.h>
//#include <dev/ppbus/ppbconf.h>
int lp_reset (int fd);
int lp_ptt (int fd, int onoff);
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#if (defined(__unix__) || defined(unix)) && !defined(USG)
# include <sys/param.h>
#endif
#include <string.h>
/* parport functions */
int dev_is_parport(int fd);
int ptt_parallel(int fd, int *ntx, int *iptt);
int ptt_serial(int fd, int *ntx, int *iptt);
int fd=-1; /* Used for both serial and parallel */
/*
* ptt_
*
* generic unix PTT routine called from Fortran
*
* Inputs
* unused Unused, to satisfy old windows calling convention
* ptt_port device name serial or parallel
* ntx pointer to fortran command on or off
* iptt pointer to fortran command status on or off
* Returns - non 0 if error
*/
/* Tiny state machine */
#define STATE_PORT_CLOSED 0
#define STATE_PORT_OPEN_PARALLEL 1
#define STATE_PORT_OPEN_SERIAL 2
int
ptt_(int *unused, char *ptt_port, int *ntx, int *ndtr, int *iptt)
{
static int state=0;
char *p;
/* In the very unlikely event of a NULL pointer, just return.
* Yes, I realise this should not be possible in WSJT.
*/
if (ptt_port == NULL) {
*iptt = *ntx;
return (0);
}
switch (state) {
case STATE_PORT_CLOSED:
/* Remove trailing ' ' */
if ((p = strchr(ptt_port, ' ')) != NULL)
*p = '\0';
/* If all that is left is a '\0' then also just return */
if (*ptt_port == '\0') {
*iptt = *ntx;
return(0);
}
if ((fd = open(ptt_port, O_RDWR|O_NONBLOCK)) < 0) {
fprintf(stderr, "Can't open %s.\n", ptt_port);
return (1);
}
if (dev_is_parport(fd)) {
state = STATE_PORT_OPEN_PARALLEL;
lp_reset(fd);
ptt_parallel(fd, ntx, iptt);
} else {
state = STATE_PORT_OPEN_SERIAL;
ptt_serial(fd, ntx, iptt);
}
break;
case STATE_PORT_OPEN_PARALLEL:
ptt_parallel(fd, ntx, iptt);
break;
case STATE_PORT_OPEN_SERIAL:
ptt_serial(fd, ntx, iptt);
break;
default:
close(fd);
fd = -1;
state = STATE_PORT_CLOSED;
break;
}
return(0);
}
/*
* ptt_serial
*
* generic serial unix PTT routine called indirectly from Fortran
*
* fd - already opened file descriptor
* ntx - pointer to fortran command on or off
* iptt - pointer to fortran command status on or off
*/
int
ptt_serial(int fd, int *ntx, int *iptt)
{
int control = TIOCM_RTS | TIOCM_DTR;
if(*ntx) {
ioctl(fd, TIOCMBIS, &control); /* Set DTR and RTS */
*iptt = 1;
} else {
ioctl(fd, TIOCMBIC, &control);
*iptt = 0;
}
return(0);
}
/* parport functions */
/*
* dev_is_parport(fd):
*
* inputs - Already open fd
* output - 1 if parallel port, 0 if not
* side effects - Unfortunately, this is platform specific.
*/
#if defined(HAVE_LINUX_PPDEV_H) /* Linux (ppdev) */
int
dev_is_parport(int fd)
{
struct stat st;
int m;
if ((fstat(fd, &st) == -1) ||
((st.st_mode & S_IFMT) != S_IFCHR) ||
(ioctl(fd, PPGETMODE, &m) == -1))
return(0);
return(1);
}
#elif defined(HAVE_DEV_PPBUS_PPI_H) /* FreeBSD (ppbus/ppi) */
int
dev_is_parport(int fd)
{
struct stat st;
unsigned char c;
if ((fstat(fd, &st) == -1) ||
((st.st_mode & S_IFMT) != S_IFCHR) ||
(ioctl(fd, PPISSTATUS, &c) == -1))
return(0);
return(1);
}
#else /* Fallback (nothing) */
int
dev_is_parport(int fd)
{
return(0);
}
#endif
/* Linux wrapper around PPFCONTROL */
#ifdef HAVE_LINUX_PPDEV_H
static void
parport_control (int fd, unsigned char controlbits, int values)
{
struct ppdev_frob_struct frob;
frob.mask = controlbits;
frob.val = values;
if (ioctl (fd, PPFCONTROL, &frob) == -1)
{
fprintf(stderr, "Parallel port PPFCONTROL");
exit (1);
}
}
#endif
/* FreeBSD wrapper around PPISCTRL */
#ifdef HAVE_DEV_PPBUS_PPI_H
static void
parport_control (int fd, unsigned char controlbits, int values)
{
unsigned char val;
if (ioctl (fd, PPIGCTRL, &val) == -1)
{
fprintf(stderr, "Parallel port PPIGCTRL");
exit (1);
}
val &= ~controlbits;
val |= values;
if (ioctl (fd, PPISCTRL, &val) == -1)
{
fprintf(stderr, "Parallel port PPISCTRL");
exit (1);
}
}
#endif
/* Initialise a parallel port, given open fd */
int
lp_init (int fd)
{
#ifdef HAVE_LINUX_PPDEV_H
int mode;
#endif
#ifdef HAVE_LINUX_PPDEV_H
mode = PARPORT_MODE_PCSPP;
if (ioctl (fd, PPSETMODE, &mode) == -1)
{
fprintf(stderr, "Setting parallel port mode");
close (fd);
return(-1);
}
if (ioctl (fd, PPEXCL, NULL) == -1)
{
fprintf(stderr, "Parallel port is already in use.\n");
close (fd);
return(-1);
}
if (ioctl (fd, PPCLAIM, NULL) == -1)
{
fprintf(stderr, "Claiming parallel port.\n");
fprintf(stderr, "HINT: did you unload the lp kernel module?");
close (fd);
return(-1);
}
/* Enable CW & PTT - /STROBE bit (pin 1) */
parport_control (fd, PARPORT_CONTROL_STROBE, PARPORT_CONTROL_STROBE);
#endif
#ifdef HAVE_DEV_PPBUS_PPI_H
parport_control (fd, STROBE, STROBE);
#endif
lp_reset (fd);
return(0);
}
/* release ppdev and close port */
int
lp_free (int fd)
{
#ifdef HAVE_LINUX_PPDEV_H
lp_reset (fd);
/* Disable CW & PTT - /STROBE bit (pin 1) */
parport_control (fd, PARPORT_CONTROL_STROBE, 0);
ioctl (fd, PPRELEASE);
#endif
#ifdef HAVE_DEV_PPBUS_PPI_H
/* Disable CW & PTT - /STROBE bit (pin 1) */
parport_control (fd, STROBE, 0);
#endif
close (fd);
return(0);
}
/* set to a known state */
int
lp_reset (int fd)
{
#if defined (HAVE_LINUX_PPDEV_H) || defined (HAVE_DEV_PPBUS_PPI_H)
lp_ptt (fd, 0);
#endif
return(0);
}
/* SSB PTT keying - /INIT bit (pin 16) (inverted) */
int
lp_ptt (int fd, int onoff)
{
#ifdef HAVE_LINUX_PPDEV_H
if (onoff == 1)
parport_control (fd, PARPORT_CONTROL_INIT,
PARPORT_CONTROL_INIT);
else
parport_control (fd, PARPORT_CONTROL_INIT, 0);
#endif
#ifdef HAVE_DEV_PPBUS_PPI_H
if (onoff == 1)
parport_control (fd, nINIT,
nINIT);
else
parport_control (fd, nINIT, 0);
#endif
return(0);
}
/*
* ptt_parallel
*
* generic parallel unix PTT routine called indirectly from Fortran
*
* fd - already opened file descriptor
* ntx - pointer to fortran command on or off
* iptt - pointer to fortran command status on or off
*/
int
ptt_parallel(int fd, int *ntx, int *iptt)
{
if(*ntx) {
lp_ptt(fd, 1);
*iptt=1;
} else {
lp_ptt(fd, 0);
*iptt=0;
}
return(0);
}