
\ Convolution based fine frequency tuning

\ This calibration algorithm is faster than calfreq
\ and runs with much smaller signal to noise ratios.
\ It depends on an already coarsely pre-calibrated frequency.

\ Needs loran.txt and signalprocessing.txt

compiletoflash

\ -----------------------------------------------------------------------------
\ A quick run to find the strongest pulse train location only
\ -----------------------------------------------------------------------------

0 variable location-max
0 variable amplitude-max

: findstrongestpulsetrain ( -- location )

  convolution \ Do this with enabled interrupts to keep in sync with signal !

  0 location-max !  0 amplitude-max !
  gri@ 0 do
    i getmastertrain i getslavetrain umax  \ If your stations have roughly equal signal levels, change this to getmastertrain only.
    dup amplitude-max @ u>= if i location-max ! amplitude-max ! else drop then
  loop

  location-max @
;

\ -----------------------------------------------------------------------------
\ A small 32 Bit stopwatch running at 60 MHz
\ -----------------------------------------------------------------------------

$40000C00 constant TIM5-Base

TIM5-Base $00 + constant TIM5_CR1    \ Control register 1
TIM5-Base $04 + constant TIM5_CR2    \ Control register 2
TIM5-Base $24 + constant TIM5_CNT    \ Counter
TIM5-Base $28 + constant TIM5_PSC    \ Prescaler
TIM5-Base $2C + constant TIM5_ARR    \ Auto-reload register

: init-counting ( -- ) \ Prepare Timer 5 for stop watch function
  8 RCC_APB1ENR bis! \ Enable clock for Timer 5
  100 0 do loop \ Wait a little bit
;

: start-clock ( -- )
  0   TIM5_CR1 ! \ Stop Timer
  0   TIM5_CNT ! \ Counter to 0
 -1   TIM5_ARR ! \ Maximum possible overflow value
  0   TIM5_PSC ! \ Prescaler off, full clock, 60 MHz
  CEN TIM5_CR1 ! \ Enable Timer
;

: stop-clock ( -- elapsedcycles )
  0   TIM5_CR1 ! \ Stop Timer
  TIM5_CNT @     \ Fetch elapsed time
  ( 60 /         \ Convert to microseconds )  \ Removed for more accurate result
;

\ -----------------------------------------------------------------------------
\ Frequency calculation helpers
\ -----------------------------------------------------------------------------

: s>f ( n -- f ) 0 swap ; \ Single to Fixpoint conversion

: getsignedtack ( -- n ) \ Fetch current tack with sign for frequency calculations
  irq-tim2 @
  case
    ram-fast-crystal-loran-handler @ of tack@ $FFFF and        endof
   ram-exact-crystal-loran-handler @ of 0                      endof
    ram-slow-crystal-loran-handler @ of tack@ $FFFF and negate endof
  endcase
;

: readkhz ( -- f ) \ Determine which frequency for crystal is currently in use.
  1000,
  getsignedtack if 1000, 60, getsignedtack s>f f* f/ d+ then
;

\ : f-correction ( -- f ) \ Gives 1 + 1 / (60 * tack)
\   getsignedtack if 1,0 getsignedtack s>f 60,0 f* f/ 1,0 d+
\                 else 1,0 then
\ ; Not in use.

: shift-in-time-to-frequency-offset ( u-elapsedcycles n-timedifference -- f-frequencyoffset )

  s>f ( u-elapsed f-timedifference )
  \  ." Time difference in samples: " 2dup f.
  60,0 1000,0 f*   f*  \ To get elapsed time in microseconds for 60 MHz timer frequency

  rot ( f-timedifference u-elapsedcycles )
  s>f ( f-timedifference f-elapsedcycles )
  f/

  \ timedifference is measured with tacked f, elapsedcycles is measured with plain q.
  \ To be mathematically correct, a tack-correction should be applied, 
  \ but in practise, its effect is less than 0,00001 khz, so it can be omitted.
;

: timeshift ( location-old location-new -- n-timeshift ) \ Gives the signed time shift between two locations in buffer.
  2dup - abs  gri@ 2/ u<=  \ If distance is less then GRI/2, a wrap-over is unlikely to have happened.
  if
    swap -
  else
    2dup > if swap - gri@ +
           else    - gri@ + negate then
  then
;

32 variable grisconvolutionadjust

: convolution-adjust ( -- ) \ Calibrate frequency by position shift of strongest pulse train
                            \ Possible with low Signal to Noise ratio, but needs a coarse frequency calibration done before.
  init-counting             \ Much faster than calfreq.
  ." Current q in use: " readkhz 5 f.n ." kHz" cr
  offsetmeasurement
  ( -- )
  grisconvolutionadjust @ samplegris             \ Do the first sample run.
  start-clock                                    \ Start a clock...
  findstrongestpulsetrain                        \ Find location of strongest pulse train in buffer
  ( location-old )                               \ This calculation needs a few seconds as interrupts eat a lot of CPU cycles meanwhile.
  grisconvolutionadjust @ samplegris             \ Do the second sample run.
  stop-clock                                     \ ... Stop the clock to see how many time elapsed while doing calculations and resampling.
    dup 60 / ." Time elapsed: " . ." us" cr

  ( location-old elapsedcycles )                 \ Sampling takes exactly the same time for first and second run, so it is ok to get time after each sampling runs.
  dint findstrongestpulsetrain eint              \ Find out where the strongest pulse train is now. No need to keep in sync, disable interrupts.
  ( location-old elapsedcycles location-new )
  rot
  ( elapsedcycles location-new location-old )
  swap
  ( elapsedcycles location-old location-new )
  timeshift                                       \ How many microseconds shift has accumulated over the elapsed time ?
    dup ." Shift: " . ." samples" cr
  ( elapsedcycles timedifference )
  shift-in-time-to-frequency-offset               \ Calculate the actual crystal frequency on these values.
  ( f-crystalfrequency )  
  2dup ." Frequency Offset: " 5 f.n ." kHz" cr
  readkhz d+ 
  2dup ." New q in use: " 5 f.n ." kHz" cr
  khz                                             \ Set freshly calculated "exact untacked crystal frequency" into tack corrections.
;

: cold-ca ( -- )
  grisconvolutionadjust @ 
    4 grisconvolutionadjust ! 
    convolution-adjust 
  grisconvolutionadjust !
;

: ca ( -- ) convolution-adjust ;

: measure-convolution ( -- )
  cr
  cold-ca
  begin
    convolution-adjust
    capture-advanced
    locate
    cr
  ?key until
;
