!******************************************************************************!
! Tests the SBTUNER command. Since SBTUNER is simply a small extension of TUNER,
! the tests here are for checks that verify SBTUNER's command-line arguments
! (which differ slightly from TUNER) and for compatibility with X-Midas.
!
! @author  J. Czechowski IV
! @since   NeXtMidas 3.3.1
!******************************************************************************
startmacro
  switch INTERACTIVE interAct GET DEF=TRUE  SDEF=TRUE  ! for normal interactive test(s)
  switch BASELINE    baseline GET DEF=FALSE SDEF=TRUE  ! for defects or more obscure test case(s)

  call test_sbtuner         ! <-- Test cases taken from X-Midas SBTUNER
  call test_scale           ! <-- Compare scale to X-Midas SBTUNER
  call test_time_code       ! <-- Check time-code output
  call test_short_pipe      ! <-- SBTUNER on short piped input
endmacro

!---------------------------------------------------------------
! Test cases taken from X-Midas SBTUNER
! @since NeXtMidas 3.3.1
!---------------------------------------------------------------
procedure test_sbtuner n:startpoint[2000] n:endpoint[19000000]
  ! This test was created as a result of XMIDAS DR 769291(change phase arithmetic
  ! to double precision) It will compute the phase error drift of two signals
  ! between the beginning and end of the run. The first
  ! signal is the product of a 1 MHz sine wave generated by
  ! "waveform" (wave1) and a 1.2MHz sine wave produced by DPCO (carrier1). The
  ! second signal is wave1 sbtuned by "sbtune" and shifted to
  ! the sum of the 2 waveforms ie 2.2MHz. These two resulting complex
  ! waveforms are phase compared in the time domain to compute the amount
  ! of phase error gain between the beginning and the end of the signal.
  ! Before the sbtuner DR fix the phase error increased with time to about 6
  ! degrees, after the fix, the phase error now essentially remains
  ! constant throughout the signal.
  !
  ! In order to insure signal overlap don't specify a starting point <512 or
  ! an endpoint greater than the last point (number-512)
  !
  ! Due to the general nature of use - this code can also serve as a general
  ! sbtune pass/fail test.
  !
  ! To use in a pass/fail scenario check the value of the result "abs_difference",
  ! make sure it is less than 1e-6
  WAVEFORM,test_tuner_wave1,cf,20m,,,1e6,,,1/12.5e6    ! generate a 1 MHZ signal
  DPCO,,,,,20m,1/12.5e6,,1.2e6,,,test_tuner_carrier1   ! generate a 1.2 MHz carrier
  fcalc test_tuner_product1 test_tuner_wave1 test_tuner_carrier1 *   ! multiply in the time domain -> resultant at 2.2MHz
                                                                     ! Difference signal is canceled by complex multiply

  SBTUNER/EXACT,test_tuner_wave1,test_tuner_wavesb,1e6,50k,2.2e6,1,cf,,test_tuner_firout1  ! sb tune and shift the 1 MHz signal to 2.2MHz

  ! compute the phase difference for point number "startpoint"
  res startpoint_product1  FILE(test_tuner_product1).getData(^{startpoint}-1)
  res startpoint_wavesb    FILE(test_tuner_wavesb).getData(^{startpoint}-1)

  calc deg_product1_start startpoint_product1(1) startpoint_product1(0) / atan
  calc deg_wavesb_start   startpoint_wavesb(1)   startpoint_wavesb(0)   / atan

  calc start_diff deg_product1_start deg_wavesb_start -

  if /verbose GT 0 then
    say " "
    say "product1 start phase = ^deg_product1_start degrees"
    say "wavesb start phase = ^deg_wavesb_start degrees"
    say "start phase difference = ^start_diff degrees"
  endif

  ! compute the phase difference for point number "endpoint"
  res endpoint_product1  FILE(test_tuner_product1).getData(^{endpoint}-1)
  res endpoint_wavesb    FILE(test_tuner_wavesb).getData(^{endpoint}-1)

  calc deg_product1_end endpoint_product1(1) endpoint_product1(0) / atan
  calc deg_wavesb_end   endpoint_wavesb(1)   endpoint_wavesb(0)   / atan

  calc end_diff deg_product1_end deg_wavesb_end -

  if /verbose GT 0 then
    say "product1 end phase = ^deg_product1_end degrees"
    say "wavesb end phase = ^deg_wavesb_end degrees"
    say "end phase difference = ^end_diff degrees"
  endif

  calc abs_difference start_diff end_diff - ABS

  assert/text="Absolute difference less than 1e-6" abs_difference LE 1e-6

  ! verify that a FIR was output. TODO improve these checks
  status test_tuner_firout1 size=firsize type=firtype form=firformat
  assert/text="^reg.main.name: Valid FIR output file size"    firsize   EQ  1024
  assert/text="^reg.main.name: Valid FIR output file type"    firtype   EQ  1000
  assert/text="^reg.main.name: Valid FIR output file format"  firformat EQS "CF"

  erase test_tuner_wave1 test_tuner_carrier1 test_tuner_product1 test_tuner_wavesb test_tuner_firout1 
return

!---------------------------------------------------------------
! Test updates related to time-code
! @bug 2698
! @since NeXtMidas 3.3.1
!---------------------------------------------------------------
procedure test_time_code
  res L:cfreq 250

  foreach D:xstart inList "0.1,3.1"
    foreach D:xdelta inList "1e-3"
      foreach D:absc inList "9.999,10.000,10.001"
        foreach L:bw inList "500"
          foreach S:form inList "CF" !"SF,CF"
            foreach S:timeline inList "F,T"
              calc L:index absc xstart - xdelta /

              waveform ram:myfile form 128k CONST 0 ,,, xstart xdelta

              if (form EQS "SF") then
                res FILE(ram:myfile).setData(index) (1)
              else
                res FILE(ram:myfile).setData(index) (1,0)
              endif

              if (TimeLine isTrue) then
                headermod ram:myfile XSTART=0.0
                res FILE(ram:myfile).setTimeAt(0.0) xstart
              endif

              sbtuner/ntap=1k ram:myfile ram:myfile_out  cfreq bw         -1 1 CF ,, ram:myfilt
              sbtuner         ram:myfile ram:myfile_out2 cfreq ram:myfilt -1 1 CF

              fcalc ram:myfile_mag ram:myfile_out ABS

              statistics ram:myfile_mag AMAX=amax OMAX=omax

              res tmax FILE(ram:myfile_out).getTimeAt(^omax)
              invoke tmax nxm.sys.lib.Time.roundToMicro(tmax)

              res S:config "^form XS=^xstart XD=^xdelta CF=^cfreq BW=^bw T=^absc TL=^timeline"
              assert/text="^reg.main.name: Filt Delay: peak@T (^config)" tmax EQ absc
              assert/text="^reg.main.name: Filt Delay: xdelta (^config)" FILE(ram:myfile_out).xdelta EQ xdelta
              assert/text="^reg.main.name: Filt Delay: filter (^config)" ram:myfile_out FEQ/D/S/H ram:myfile_out2
            endfor
          endfor
        endfor
      endfor
    endfor
  endfor
return

!---------------------------------------------------------------
! Test to make sure the values in the files compare to X-Midas.
! @bug 2698
! @since NeXtMidas 3.3.1
!---------------------------------------------------------------
procedure test_scale
  res T:exp {}
  res T:act {}

  ! ===============================================================================
  ! Code to produce values we need in X-Midas (repeat for both form="SF" and "CF"):
  /*
  res form "SF"

  constant my_in form 128k 0 0 0.1 1e-3 1
  res my_in(~10) 1.0
  sbtuner/taps=1k my_in my_out 250 500 -1 1 CF ,, my_filt

  fcalc my_out_mag  my_out  MAG
  fcalc my_filt_mag my_filt MAG

  fstatistics/quiet my_out      MAXV=outMAX  MAXA=outAMAX  MINV=outMIN  MINA=outAMIN
  fstatistics/quiet my_filt     MAXV=filtMAX MAXA=filtAMAX MINV=filtMIN MINA=filtAMIN
  fstatistics/quiet my_out_mag  MEAN=outMEAN
  fstatistics/quiet my_filt_mag MEAN=filtMEAN
  
  say "NeXtMidas test code:"
  say "  res exp.^{form}_OUT      {MAX=^{outMAX},AMAX=^{outAMAX},MIN=^{outMIN},AMIN=^{outAMIN}}"
  say "  res exp.^{form}_OUT_MAG  {MEAN=^{outMEAN}}"
  say "  res exp.^{form}_FILT     {MAX=^{filtMAX},AMAX=^{filtAMAX},MIN=^{filtMIN},AMIN=^{filtAMIN}}"
  say "  res exp.^{form}_FILT_MAG {MEAN=^{filtMEAN}}"
  */
  ! ============================================================================

  res exp.SF_OUT      {MAX=0.99999994039535,AMAX=10,MIN=0,AMIN=0.6110}
  res exp.SF_OUT_MAG  {MEAN=4.07334818470340E-05}
  res exp.SF_FILT     {MAX=0.50,AMAX=0,MIN=0,AMIN=-0.5120}
  res exp.SF_FILT_MAG {MEAN=0.00258659338578}

  res exp.CF_OUT      {MAX=0.49999994039535,AMAX=10,MIN=0,AMIN=0.6110}
  res exp.CF_OUT_MAG  {MEAN=2.03667388188187E-05}
  res exp.CF_FILT     {MAX=0.50,AMAX=0,MIN=0,AMIN=-0.5120}
  res exp.CF_FILT_MAG {MEAN=0.00258659338578}

  ! ============================================================================

  calc L:index 10.000 0.1 - 1e-3 /
  waveform ram:in_sf SF 128k CONST 0 ,,, 0.1 1e-3 
  waveform ram:in_cf CF 128k CONST 0 ,,, 0.1 1e-3 
  
  res FILE(ram:in_sf).setData(index) (1)
  res FILE(ram:in_cf).setData(index) (1,0)

  foreach form inList "SF"
    sbtuner/ntap=1k RAM:IN_^{form} ram:out 250 500 -1 1 CF ,, ram:filt

    fcalc ram:out_mag  ram:out  MAG
    fcalc ram:filt_mag ram:filt MAG

    statistics ram:out      TABLE=act.^{form}_OUT
    statistics ram:filt     TABLE=act.^{form}_FILT
    statistics ram:out_mag  TABLE=act.^{form}_OUT_MAG
    statistics ram:filt_mag TABLE=act.^{form}_FILT_MAG

    foreach stat inList "^{form}_OUT,^{form}_OUT_MAG"
      foreach key inTable exp.^{stat}
        assert/text="^reg.main.name: ^{form} output ^{key}" exp.^{stat}.^{key} EQT act.^{stat}.^{key} /TOL=1e-5
      endfor
    endfor

    foreach stat inList "^{form}_FILT,^{form}_FILT_MAG"
      foreach key inTable exp.^{stat}
        assert/text="^reg.main.name: ^{form} filter ^{key}" exp.^{stat}.^{key} EQT act.^{stat}.^{key}
      endfor
    endfor
  endfor
return

!---------------------------------------------------------------
! Test to make sure we did not have the same issue as X-Midas.
! These steps would cause a segfault in X-Midas
! X-Midas DR #894937-21 Segfault in SBTUNER on short piped input
!---------------------------------------------------------------
procedure test_short_pipe
  pipe on
    waveform _shortconst shape=const form=cf elem=100
    sbtuner INPUT=_shortconst OUTPUT=_sbt INPUTIF=0 BWIMPRESP=0 OUTPUTIF=0.1 DECIMATION=1
    noop _sbt ram:test_sbtuner_small.tmp
  pipe off
  status ram:test_sbtuner_small.tmp size=sbtOutSize
  assert/text="^reg.main.name: Short piped input" 0 EQ sbtOutSize
return
