      program cmplxdemod
c
c     Copyright (C) 1995
c
c     Michael Mann
c
c     perform envelope analysis in a moving loop through a given time
c     series
c
c     v1.0
c
c  uses  stripped version of original  multitaper spectral code of
c  Jeffrey Park (c)
c
c   mmax     - maximum number of spatial dofs desired (ie, max # of time series)
c   ntapmax  - maximum number of spectral dofs desired
c   nscanmax - maximum length of time series (may have to be increased!)
c
      parameter (nscanmax=4000,ntapmax=3)
      real*8 ell,env0,sum,cs,sn
      real aaa(nscanmax),atot1(nscanmax,180),atot2(nscanmax,180)
      integer icount(nscanmax)
c
c
      character*4 chead(158)
      complex zed
c
      common/npiprol/anpi
      common/nnaamm/iwflag
      common/work/b1(8194)
      common/taperz/ta(4100,16),tai(4100,16),dcf(4100,16),amu(4100,4)
      common/staple/tad(8200)
      common/stap2/tas(8192,16)
      common/stap4/cs(8200),sn(8200)
      common/envel1/ell(20)
      common/data/a(50000)
c
      common/pltopt/npad,nst,mpts,nmin,nmax,t(3),fmin,fmax,nf,
     x                                ddt,nwin,inc,tt(3)
c
      equivalence (zed,reim),(iy,dum),(iah,ahead),(iah,chead),
     x          (tad,env0)
c
c
      dimension dum(35),ahead(158),env0(2,4100)
      iwflag=0
      zero = 0.0
      one = 1.0
      pi=3.14159265358979
c
c
c
c     dt    = time interval spacing in units of years
c
c     here we're assuming monthly data
c
      dt = 0.0833333
c
c     set frequency range for DFT, frequency spacing, zero
c     padding, etc.
c     ** NOTE: maximum padded DFT length is npad=8192 **
c
c
      npad = 8192
c
c     f1 = minimum frequency for analysis = 0
c     f2 = maximum frequency  (in cyc/year -- this must always be
c          less than/equal to the nyquist sampling frequency = 0.5/dt)
c     ( presently set for the range 0 to 0.5 cycle year, or, periods greater
c
      f1 = zero
      f2 = 0.5/dt
c
c
c
      ddf=1./(npad*dt)
      eps=.1*ddf
      nf1=(f1+eps)/ddf
      nf2=(f2+eps)/ddf
      nf=nf2-nf1+1
      nf = nf-1
      nfmax = nf
c
      t(1)=f1
      t(2)=ddf
      t(3)=ddf
      nn1=(f1+eps)/ddf
      fmin=n1*ddf
      nn2=(f2+eps)/ddf
      fmax=n2*ddf
      nmin=nn1*2+1
      nmax=nn2*2+2
c
c
c     read in data
c
c     *** this part of the program may have to modified depending
c         on data format ***
c
c     here we assume input file format with dummy x column,
c     and y value taken from 2nd column
c
      open (unit=15,file='evol.in',status='old')
      do i=1,nscanmax
         read (15,*,end=99) adum,aaa(i)
      end do
99    close (unit=15)
c
c     determine length of series
c
      nscan = i-1
c
222   format (4x,f6.2)
      write (6,*) 'read in: '
      write (6,*) ' time series of length: ',nscan
      write (6,*) '(',nscan*dt, ' years long)'
c
c
c     get frequency, determine associated DFT index
c
      write (6,*) 'frequency for complex demod (cycles/year):'
      read (5,*) freq
      iif = 1+(freq+eps)/ddf
c
c     default settings
c
      npi = 2
      anpi=float(npi)
      nwin=3
      imode = 0
      nmode = 1
      iseg = 0
      n1 = 1
      n2 = nscan
      npts = n2-n1+1
      iformat = 0
      iref = 1
      jsmoo = 0
      iaverage = 0
      irecon = 0
      imoderecon = 0
      ioutput = 0
      itype = 0
      nbeg=1
      nend = nscan
      neval = nscan
      nmove = nscan
      ninterv = nmove

444   write (6,*) 'present settings'
      write (6,*) '1) use',nwin, ' x ',npi,' pi tapers'
      write (6,*) '2) data interval (',nbeg,' to ',nend,' )'
      if (itype.eq.0) then
        write (6,*) 
     $    '3) fixed interval (change this for time-dependent analysis)'
      else
         write (6,*) '3) evolutive analysis'
         write (6,*) '    --',nmove,' year moving window'
         write (6,*) '    --',ninterv,' year time step'
      endif
      if (jsmoo.eq.0) then
        write (6,*) '4) inversion type :minimum norm (gen advised)'
      else
        if (jsmoo.eq.1) then
          write (6,*) '4) inversion type: minimum slope'
        else
           write (6,*) '4) inversion type: minimum roughness'
        endif
      endif
      write (6,*) 'item to change (0=continue)'
      read (5,*) icont
      if (icont.eq.1) then
       goto 1111
      else
       if (icont.eq.2) then
         goto 2222
       else
         if (icont.eq.3) then
          goto 3333
         else
            if (icont.eq.4) then
              goto 4444
            else
              goto 9999
            endif
         endif
       endif
      endif
c
c     anpi = desired time/frequency bandwith "p" (ie, "p pi tapers")
c     nwin = number of tapers used (maximum is 2p-1)
c
1111  write (6,*) 'time-frequency bandwidth product "p"'
      read (5,*) npinew
      if (ntapmax.lt.2*npinew-1) then
         write (6,*) 'sorry--exceeds maximum bandwidth'
      else
         write (6,*) 'number of tapers'
         read (5,*) nwinnew
         if (nwinnew.gt.2*npinew-1) then
           write (6,*) 'sorry--cannot exceed 2*p-1'
         else
           npi = npinew
           anpi = float(npi)
           nwin=nwinnew
         endif
      endif
      goto 444

2222  write (6,*) 'beginning time index'
      read (5,*) nbeg
      write (6,*) 'ending time index'
      read (5,*) nend
      nmove = nend-nbeg+1
      neval = nend-nbeg+1
      ninterv = nmove
      goto 444

3333  write (6,*) 'choose'
      write (6,*) '(0) fixed window'
      write (6,*) '(1) evolutive analysis'
      read (5,*) itype
      if (itype.eq.1) then
         write (6,*) '(WARNING--for some reason certain choices'
         write (6,*) 'of window and interval of parameters lead'
         write (6,*) 'to an error (zero output). If this happens,'
         write (6,*) 'make another choice. sorry!)'
         write (6,*) 'window width (in months e.g., 120 for 10 year)'
         read (5,*) nmove
         write (6,*) 
     $ 'interval between evaluations (in months, e.g., 12 for 1 year)'
         read (5,*) ninterv
      endif
      goto 444

c
c     "minimum norm" : minimize the overall size of the envelope of the
c              oscillation. Generally the most faithful constraint, but
c              may artificially minimize amplitude ends of time interval.
c     "minimum slope" :minimize the average slope of the envelope of
c              the oscillation. Particularly useful for reconstructing
c              secular trends in the series which do not have the periodic
c              property of non-secular oscillations.
c
c     "minimum roughness" : minimize the average 2nd derivative of the
c              envelope of the oscillation. Useful if the amplitude of the
c              envelope is believed to be changing abruptly near the ends of
c              the time interval
c
4444  write (6,*) 'inversion for time domain'
      write (6,*) '(0) minimum norm'
      write (6,*) '(1) minimum slope'
      write (6,*) '(2) minimum roughness'
      read (5,*) jsmoo
      goto 444
c
c     we're happy with the settings. Continue...
c
9999  open (unit=4,file='cmplxdemod-amp.out',status='unknown')
      open (unit=7,file='cmplxdemod-phase.out',status='unknown')
c
c     construct tapers appropriate for window width
c
      npts = nmove
      call taper(npts,nwin,ell)
c
      write (6,*) 'nscan ',nscan
      write (6,*) 'neval ',neval
      write (6,*) 'nbeg ',nbeg
      write (6,*) 'nend ',nend
      write (6,*) 'dt    ',dt
      write (6,*) 'npts  ',npts
      write (6,*) 'nmove ',nmove
      write (6,*) 'ninterv ',ninterv
      niter = (neval+0.5-nmove)/ninterv + 1
      write (6,*) 'beginning 1st of ',niter, ' iteration/s...'
      do i=1,nscan
         icount(i)=0
      end do
c
c     begin moving time window loop
c
      do it = 1,niter
        n1 = 1+(it-1)*ninterv
        n2 = n1+npts-1
        ncent = (n1+n2-1)/2
        write (6,*) 'n1  n2 ',n1,n2
c
c       calculate DFTs
c
c       demean gridpoint series locally
c
        sum = 0.
        do i=n1,n2
           sum = sum+aaa(i)
        end do
        sum = sum/float(npts)
c
c
        do iwin=1,nwin
          do i=1,npad
             b1(i)=0.
          end do
          do i=n1,n2
            b1(i-n1+1)=(aaa(i)-sum)*tas(i+1-n1,iwin)
          end do
          call realft(b1,npad/2,1)
          b1(npad-1)=b1(2)
          b1(npad)=0.
          b1(2)=0.
          j=0
          do i=1,npad,2
            j=j+1
            ta(j,iwin)=b1(i)
            tai(j,iwin)=b1(i+1)
          end do
        end do
c
c       do time reconstructions
c
        do i=1,2*nscanmax
           a(i)=0.0
        end do
c
c       decimate tapers
c
1660    inc=(npts-1)/500+1
        j=0
        do i=1,npts,inc
          j=j+1
          do k=1,nwin
           tas(j,k)=tas(i,k)
          end do
        end do
        mpts=j
        ddt=dt*inc
c
        ifreq = iif
        ff0 = freq
        if (ifreq.eq.1) ta(ifreq,iwin)=ta(ifreq,iwin)/2.0
c
c       invert spectral envelope to get time envelope
c
        call envel(ff0,jsmoo)
        call cossin(npts,cs,sn,1.d0,0.d0,dble(ff0),dble(dt))
        finc=float(inc)
c
        do i=1,mpts-1
           ii=(i-1)*inc
           do j=1,inc
            im = ii+j
            a1=(env0(1,i)*(inc+1-j)+env0(1,i+1)*(j-1))/finc
            a2=(env0(2,i)*(inc+1-j)+env0(2,i+1)*(j-1))/finc
            envmag = sqrt(a1**2+a2**2)
            envphase = -180.0*atan2(a2,a1)/pi
            index = n1+im-1
            imonth = mod(index-1,12)+1
c            phase0 = (-float(imonth-1)+0.5)*dt*360.0
c            phase0 = dt*360.0
c            phase0 = 0.0
            envphase = envphase + 360.0
            if (envphase.gt.360.0) envphase=envphase-360.0
c            phase0 = 15.0
            phase0 = 0.0
            envphase = envphase-phase0
c            if (envphase.gt.360.0) envphase=envphase-360.0
c            if (envphase.gt.180.0) envphase=envphase-360.0
c            if (envphase.lt.-360.0) envphase=envphase+360.0
c            if (envphase.lt.-180.0) envphase=envphase+360.0
            if (index.eq.ncent) then
             icount(index)=icount(index)+1
             atot1(index,icount(index))=envmag
             atot2(index,icount(index))=envphase
            endif
           end do
        end do
c
c       linear extrapolate reconstructions one point
c
        ii=(mpts-1)*inc
        e1=2.*env0(1,mpts)-env0(1,mpts-1)
        e2=2.*env0(2,mpts)-env0(2,mpts-1)
        do j=1,inc
            im = ii+j
            a1=(env0(1,mpts)*(inc+1-j)+e1*(j-1))/finc
            a2=(env0(2,mpts)*(inc+1-j)+e2*(j-1))/finc
            envmag = sqrt(a1**2+a2**2)
            envphase = -180.0*atan2(a2,a1)/pi
            index = n1+im-1
            imonth = mod(index-1,12)+1
            phase0 = (float(imonth)+0.5)*dt*360.0
            envphase = envphase+phase0
            if (envphase.gt.360) envphase=envphase-360.0
            if (index.eq.ncent) then
             icount(index)=icount(index)+1
             atot1(index,icount(index))=envmag
             atot2(index,icount(index))=envphase
            endif
        end do
c
c       provide progress update
c
        open (unit=12,file='svdevals-standard.status',
     $         status='unknown')
        if ((niter.ge.10).and.(mod(it-1,niter/10).eq.0)) then
         write (12,*) it,' iterations of ',niter,' done...'
         write (6,*) it,' iterations of ',niter,' done...'
        endif
        close (unit=12)
c
c       terminate time loop
c
      end do
c
c     now determine ensemble averaged amplitude and phase
c     information
c
      do i=1,nscan
       time = float(i-1)*dt
       envmag = 0.0
       envphase = 0.0
       do j=1,icount(i)
          envmag = envmag+atot1(i,j)
          envphase = envphase+atot2(i,j)
       end do
       if (icount(i).gt.0) then
       envmag = envmag/float(icount(i))
       envphase = envphase/float(icount(i))
       if (envphase.gt.180.0) envphase=envphase-360.0
       write (4,888) time,envmag
       write (7,888) time,envphase
       endif
      end do
      write (6,*) 'done.'
c
c     done -- close files
c
      close (unit=4)
      close (unit=7)
888   format (2f12.2,i4)
c888   format (f6.2,f12.4)
      stop
      end
