@
@    Loran-C receiver for STM32F407
@    Copyright (C) 2014  Matthias Koch
@
@    This program is free software: you can redistribute it and/or modify
@    it under the terms of the GNU General Public License as published by
@    the Free Software Foundation, either version 3 of the License, or
@    (at your option) any later version.
@
@    This program is distributed in the hope that it will be useful,
@    but WITHOUT ANY WARRANTY; without even the implied warranty of
@    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@    GNU General Public License for more details.
@
@    You should have received a copy of the GNU General Public License
@    along with this program.  If not, see <http://www.gnu.org/licenses/>.
@

;------------------------------------------------------------------------------
  This is the second experimental release of a software Loran-C receiver
  running on STM32F407 Discovery board.
;------------------------------------------------------------------------------

  Loran-C is a longwave radio navigation system.

  In a nutshell:
  Stations transmit pulses on 100 kHz with precisely timed patterns.
  
  If you receive at least three transmitters and know their location and timing,
  you can calculate your position from the delays mentioned between the arrival
  of the three pulse trains to each other, as their distance in time depends
  on the distance in space between each transmitting station to receiver.

  You don't know absolute timing, but with two "time delays" you can do
  hyberbolic navigation, which mathematic principle is known as Trilateration.

  Additionally, Loran-C is providing a very accurate clock base which is
  usable if you can receive at least one station.

  Loran-C is available in Europe, parts of Asia and Russia (called Chayka there).
  It has been shut down in north America some years ago.


  I suggest you to see Loran-C signal documentation before continuing,
  as I only draw an outline of the signal characteristics needed for
  this particular implementation and I assume you are familiar with it.

    Introduction:
    http://www.jproc.ca/hyperbolic/loran_c_sigchar.html

    Technical documentation:
    "Specification of the transmitted Loran-C Signal", 1994
    http://www.navcen.uscg.gov/?pageName=loranSignalSpec

    Sylt chain data:
    http://www.loran-europe.eu/viewpage.php?page_id=10
    Lessay chain data:
    http://www.loran-europe.eu/viewpage.php?page_id=11

;------------------------------------------------------------------------------

  How to get it up and running:
  
  You need: - A STM32F407 Discovery board  
            - A serial 115200 baud terminal connected to USART2 
              (TX on PA2, RX on PA3)
            - A longwave antenna for 100 kHz, bandwidth 90-110 kHz
              with a signal between 0V and about 3V connected to PC1

  Tools:    - Oscilloscope
            - Frequency counter with at least 1 ppm resolution

  Set up your antenna. Hook oscilloscope and search for Loran-C pulse trains.
  Take care of proper signal level to not fry your analog-digital converters !

  Connect terminal and signal to STM32F407 board and flash the binary image.
  This one is based on Mecrisp-Stellaris 0.9.2 with special memory layout and
  interrupt handlers written in assembly.

  st-flash erase
  st-flash write mecrisp-stellaris-stm32f4-loran.bin 0x08000000

  Get terminal running and load loran.txt file. Reset afterwards.


  Usage:

  Orange LED will start to glow. Measure its frequency on PD13.
  This is very close to 1 MHz and the free running frequency of
  crystal feed through PLL and clock dividers.

  In my case, this is 1000.074 to 1000.076 kHz depending on temperature.

  Type 1000,074 khz (with comma!) and frequency should be (almost) right.

  Make sure that Loran-C signal on PC1 input looks good on oscilloscope.
  Choose a GRI of a chain near to you. Examples are for default Sylt chain 
  with GRI7499 = 74990 us. 

  In Germany, the Lessay chain with GRI6731 = 67310 us is also useful.

  Set this in microseconds with 74990 gri!
  Maximum GRI with this memory layout is currently about 81000us.

  Fine-tune the local frequency with calfreq which sums up 32 GRI cycles to see
  which one gives the strongest signal. Result looks like this:


Tack: 210  Maximum: 973
Tack: 211  Maximum: 1153
Tack: 212  Maximum: 1028
Tack: 213  Maximum: 958
Tack: 214  Maximum: 917
Tack: 215  Maximum: 844
Tack: 216  Maximum: 833
Tack: 217  Maximum: 1237
Tack: 218  Maximum: 1448
Tack: 219  Maximum: 1998
Tack: 220  Maximum: 2446
Tack: 221  Maximum: 2776
Tack: 222  Maximum: 2984
Tack: 223  Maximum: 3357
Tack: 224  Maximum: 3019
Tack: 225  Maximum: 3249
Tack: 226  Maximum: 3493
Tack: 227  Maximum: 3580
Tack: 228  Maximum: 3663  <-- Choose this ! "One switch cycle every 228 samples"
Tack: 229  Maximum: 3617
Tack: 230  Maximum: 3271
Tack: 231  Maximum: 3001
Tack: 232  Maximum: 2652
Tack: 233  Maximum: 2395
Tack: 234  Maximum: 1942
Tack: 235  Maximum: 1533
Tack: 236  Maximum: 1406
Tack: 237  Maximum: 1911
Tack: 238  Maximum: 1723
Tack: 239  Maximum: 1067

  In loop simply 32 GRIs for each Tack are sampled and added together, 
  afterwards buffer is searched for searched for absolute maximum value.

  "Tack" value is after how many normal cycled samples a "switch cycle" is inserted. 
  Crystal on this board is too fast, switch cycles slow down.

  Choose maximum, and have noise in mind. Plotting there values will help you decide
  which one is best. You have to do this from time to time, as crystal frequency
  depends on temperature of the board.

  In this example, choose the best value and set it with
  228 tack!


  You can do complete frequency calibration manually, if you wish:

  There are three interrupt handlers, which can be switched manually with:
    exact-xtal   for perfect clock sources or initial measurements
     slow-xtal   for too slow crystals, it will hurry up
     fast-xtal   for too fast crystals, it will wait a bit.

  Use 228 tack! or its abbrevation 228 t for manual fine tuning.

  The idea is to insert switch cycles after a number of samples.
  It is done with a timer who counts cycles, starts the ADC conversation in time and 
  the interrupt handler inserts "switch cycles" into the counting every given number 
  of samples. Normally, the Timer takes 60 cycles/sample, but every n-th sample 
  (which is changeable on the fly), it waits for 61 cycles (or 59 in case of slow crystal) 
  instead. This is very precise and only depends on short-term stability of crystal.

  Here an example measurement for fast-xtal mode:

# Tack  Frequency [kHz] determined with frequency counter

# 0   983.682
# 1   983.682 # Every cycle is a switch cycle

2   991.812
3   994.551
4   995.927
5   996.754
6   997.306
7   997.701
8   997.997
9   998.228
10  998.413
11  998.563
12  998.690
13  998.796
14  998.887
15  998.966

20  999.244
30  999.521
40  999.656
50  999.744
60  999.799
70  999.838
80  999.868
90  999.891

100 999.909
150 999.965
200 999.993   # Useful range begin
250 1000.010  #              end   for this particular crystal
300 1000.020

400 1000.034
500 1000.043

# 30000 1000.076 # Almost without switch-cycles, close to free running crystal frequency

  Useful ranges are roughly from 150 (bad crystal, 100ppm offset) to 30000 (0.3ppm crystal offset).

  Now as you have a precise local clock, you can capture the signal:

  offsetmeasurement  to adjust for DC offset of signal - important to not overflow buffer
  128 samplegris     to sample and sum up over 128 gri cycles, or how many you want.
                     This should be 1 or an even number because of phase coding of Loran-C signal
                     Start with less if in doubt, 4 is a good place for a first try. 

  printbuffer        to get the captured signal.
                     Save this into a text file, see with Gnuplot.

;------------------------------------------------------------------------------

  Implementation details of clock system:

  On board crystal is not very precise, around 80 ppm.
  PLL generates 120MHz CPU clock
  AD converter gets clock for 2 Msps sample rate
  Timer 2 gets 60 MHz clock signal and triggers
    * every 60 clock cycles (exact-xtal)
    * every 60 or sometimes 61 clock cycles (fast-xtal)
    * every 60 or sometimes 59 clock cycles (slow-xtal)

  Timing of captures in Timer ticks @ ~60 MHz:

  | 60 | 60 | 60 | 60 | 60 | 60 | ...  Theoretical f = 1 MHz

  With Switch-cycles:

  Tack=1: | 60 +- 1 | 60 +- 1 | 60 +- 1 | 60 +- 1 | 60 +- 1 | 60 +- 1 |...
  Tack=2: | 60      | 60 +- 1 | 60      | 60 +- 1 | 60      | 60 +- 1 |...
  Tack=3: | 60      | 60      | 60 +- 1 | 60      | 60      | 60 +- 1 |...
  ...

Frequency is theoretically given by
  f = q / (1 +- (1/60*tack))
    f : Frequency on orange LED
    q : Free-running frequency of crystal through PLL

  <=> tack = 60 / (q/f - 1)

  Some examples:

   Offset: Free frequency 
    0 ppm: 1 000 000 Hz  Tack infinite, use exact-xtal handler
    1 ppm: 1 000 001 Hz  Tack 16666
   10 ppm: 1 000 010 Hz  Tack  1666
   20 ppm: 1 000 020 Hz  Tack   833
   50 ppm: 1 000 050 Hz  Tack   333
   80 ppm: 1 000 080 Hz  Tack   208
  100 ppm: 1 000 010 Hz  Tack   166

  The more far off the crystal is, the more coarse the Tack steps will be.
  Useful Tack range will be around 150 to 20000...

  (Tack got its name from the alternating sound a mechanical clock makes:
   Tick, Tack, Tick, Tack - at least for German tounges.
   A natural fit to switch-cycles :-)


Precision needed for sampling of n GRI cycles:

  To record a well summed up signal, some rough considerations:
  100 kHz leads to 10 samples/cycle at 1MHz sampling rate,
  and for a good summing result, it should not be off more than about 3 samples.

  A rough calculation gives sample point offset for one GRI cycle:
  74990 * (1/1.000078 - 1) = -5.8488
  Almost 6 samples off for each cycle is no option.

  With a roughly calibrated switch cycle handler:
  74990 * (1/1.000001 - 1) = -0.074990
  A maximum offset of 3 samples is reached within 3/0.074990 = 40 GRI cycles.

  If you calibrate the Tack value very finely against the signal itself and
  tolerate a little bit more than 3 samples offset, then the practical maximum
  for GRI cycle summations should be around 128 cycles.

  Keep in mind that the buffer has signed 16-Bit values, so if you have a good
  antenna and strong signals, a much lower summation count will perform fine.

;------------------------------------------------------------------------------
  Loran-C signal and signal processing:

  Pulses are transmitted at 100 kHz and have a duration of about 300us.

  Pulses are spaced 1 ms apart, 8 pulses for slave stations.
  Master stations add a ninth pulse 2 ms apart at the end.

  Pulse trains are repeated after a precise GRI cycle time.
  The GRI=Group Repetition Interval values are typically given in 10 us units.
  This implementation uses 1 MHz sampling rate to sample the 100 kHz signal.

  To receive we have to capture and sum up signal into circular buffer with
  exactly the given GRI period length. All other stations with other GRIs and
  unperiodical noise will not sum up.

  When summing up two adjacent GRI cycles, polarity of pulses in pulse train
  changes in a special order:

          Master pulses:          Slave pulses:
  A code: + + - - + - + -   +     + + + + + - - +
  B code: + - - + + + + +   -     + - + - + + - -

  Sum:    + 0 - 0 + 0 + 0   0     + 0 + 0 + 0 - 0

  You will see 4 pulses in each pulse train after summing up, 
  good cancellation of the pulses in between is a timing quality indicator.

  To see the effect, you can change to Frame Repetition Interval FRI = 2*GRI
  capture at 500 kHz sample rate and see nine pulses for master station and 
  eight for slave stations. Commands are "gricycle" (standard) and "fricycle".
  
  To determine positions of signals, you can fold with the complete pulse train:
    For Master: + - + +
    For Slave:  + + + -

  f = 100 kHz
  k = 1/72.5 us

  p(t) = (k*t)^2 * exp(-2*(k*t-1)) * sin(2 * Pi * f * t)

  Master pulse train: m(t) = p(t) - p(t - 2000us) + p(t - 4000us) + p(t - 6000us)
  Slave  pulse train: s(t) = p(t) + p(t - 2000us) + p(t - 4000us) - p(t - 6000us)

  Examples data points of Signal and how folding result looks like are included.
  You can determine Time Delays manually out of this data sets, too.

  Correlation results for included example signal:

  Master pulse train at 72843
  Slave  pulse train at 29921
  Slave  pulse train at 14076

  Time Delay for X: 16223  Time Delay for Y: 32068


;------------------------------------------------------------------------------
  Additional definitions included in Mecrisp-Stellaris-STM32F4-Loran core:

        loran-register-init ( -- ) Initialise special registers

        sample>address  ( u -- address ) Get location of given sample in memory
                                         Each sample is stored in 16 Bits.

        sign16          ( n16 -- n32 ) Sign extend 16 Bits to 32 Bits

       pulseconvolution ( sample# -- intensity ) Calculate one point of convolution
                                       with pulse shape starting on given sample#

        gri@            ( -- u ) Fetch current GRI in us units
        gri!            ( u -- ) Set GRI. Maximum is 81920 (us).

        offset@         ( -- u ) Fetch offset currently in use
        offset!         ( u -- ) Set   offset for capture

        samples@        ( -- u ) Fetch samples still to do
        samples!        ( u -- ) Set   samples to capture

        sampleno@       ( -- u ) Fetch circular buffer pointer
        sampleno!       ( u -- ) Set   circular buffer pointer

        tack@           ( -- u ) Get Tack value, do "$FFFF and" with it.
                                 Low 16 Bits contain Tack, high halfword 
                                 contains current tack counter.
        tack!           ( u -- ) Set Tack value

        avg2h!          ( x 16-addr -- )  Store with exponential average over two 16 bit samples
        avg4h!          ( x 16-addr -- )    for 4 samples
        avg8h!          ( x 16-addr -- )    for 8 samples
        avg16h!         ( x 16-addr -- )    for 16 samples

        avg2!           ( x addr -- )    Store with exponential average over two 32 bit samples
        avg4!           ( x addr -- )      for 4 samples
        avg8!           ( x addr -- )      for 8 samples
        avg16!          ( x addr -- )      for 16 samples

  Interrupt handlers:

        exact-crystal-loran-handler ( -- )  Simply capture...
         fast-crystal-loran-handler ( -- )    with slow-down switch cycles
         slow-crystal-loran-handler ( -- )    with hurry-up  switch cycles

  Copy them into RAM before use because execution from Flash needs
  waitstates for 120 MHz CPU clock and simply is too slow for capturing.

  Memory map is:

    $10000000-$1000FFFF ( 64 kb) Circular buffer,  first part
    $20000000-$20017FFF ( 96 kb) Circular buffer, second part
    $20018000-$20020000 ( 32 kb) Stacks, variables and RAM for everything else 

;------------------------------------------------------------------------------
  Agenda and Wishes:

    - Testing is needed ! 
      Please write to m-atthias@users.sf.net 
      and include your plain captured signal as text file.
      Don't forget to include your position or 
      at least the country and rough location.

    - Do you have hints for a good antenna design for this purpose ?
      Currently I am using a simple magnetic loop antenna 
      with an opamp bandpass filter.

    - Simply ask if you have design questions, 
      documentation grows with the questions of the ones using it ! 

    - Time Delay to latidute & longitude calculation

;------------------------------------------------------------------------------

  Matthias Koch, January 2014
