
{ Loran-C signal pulse train correlations   Matthias Koch 2014 }

uses sysutils, math;

type tsignal = array[0..99990] of longint;

var signal, puls : tsignal;
    gri : longint;

function wertezeile(zeile : string) : boolean; 
// Gibt an, ob die Zeile einen Spektrumpunkt oder etwas anderes enthält.
begin
  wertezeile := (trim(zeile) <> '') and           // Nicht leer
                (pos('#', zeile) = 0);      // Kein Kommentar
end;


function signallesen(dateiname : string) : tsignal;
var zeile : string;
    stelle, messwert : longint;
    datei : text; 

begin    
  assign(datei, dateiname);
  reset(datei);
  gri := 0;

  while not eof(datei) do
  begin
    readln(datei, zeile);

    if wertezeile(zeile) then
    begin // Zeile auswerten
      stelle := strtoint(trim(copy(zeile, 1, pos(#32, zeile) - 1)));
      delete(zeile, 1, pos(#32, zeile));
      messwert := strtoint(trim(zeile));

      if gri <> stelle then writeln('Faulty data set at location ', stelle, ' which is <> counter ', gri);
      if gri <= 99990 then begin signallesen[gri] := messwert; inc(gri); end; 
    end;
  end;

  close(datei);   
  writeln(gri, ' signal samples read.');
end; 

procedure signalschreiben(var daten : tsignal; dateiname : string);
var datei : text;
    k : longint;
begin
  assign(datei, dateiname);
  rewrite(datei);
  for k := 0 to gri - 1 do writeln(datei, k, #9, daten[k]);
  close(datei);
end;

function circular_index(index : longint) : longint;
begin
//  writeln(index);
  if index >= gri then index := index - gri;
  if index  < 0   then index := index + gri;

  circular_index := index;
end;

function loranpuls(i : longint) : single;
const k : single = 1/72.5e-6;
      f : single = 100000;

var t : single;
begin
  if i < 0 then loranpuls := 0
  else
  begin
    t := 0.000001 * i; // Schrittweite 1 us.
    loranpuls := k*k*t*t * exp (-2*(k*t-1)) * sin(2*pi*f*t);
  end;

end;

procedure pulszug;
var i : longint;
begin
  for i := 0 to gri - 1 do puls[i] := round(1000 * loranpuls(i) );
end;

function faltung_mit_puls(var faltsignal : tsignal) : tsignal;
var k, l : longint;
    summe : int64;
begin
  for l := 0 to gri-1 do
  begin
    summe := 0;
    for k := 0 to 500 do // Für einen Puls
      summe := summe + puls[k] * faltsignal[circular_index(k + l)];

    faltung_mit_puls[l] := summe div 40000;
  end;
end;

function faltung_mit_hauptzug(var faltsignal : tsignal) : tsignal;
var k, l : longint;
    summe : int64;
begin
  for l := 0 to gri-1 do
  begin
    summe := faltsignal[l] - faltsignal[circular_index(l + 2000)] + faltsignal[circular_index(l + 4000)] + faltsignal[circular_index(l + 6000)];
    // Absolutwert der Faltung
    if summe < 0 then summe := -summe;
    faltung_mit_hauptzug[l] := summe;
  end;
end;

function faltung_mit_nebenzug(var faltsignal : tsignal) : tsignal;
var k, l : longint;
    summe : int64;
begin
  for l := 0 to gri-1 do
  begin
    summe := faltsignal[l] + faltsignal[circular_index(l + 2000)] + faltsignal[circular_index(l + 4000)] - faltsignal[circular_index(l + 6000)];
    // Absolutwert der Faltung
    if summe < 0 then summe := -summe;
    faltung_mit_nebenzug[l] := summe;
  end;
end;



var korrelation : tsignal;
    korrelation_master : tsignal;
    korrelation_slave  : tsignal;


function within(x, a, b : longint) : boolean;
// Within this range, with GRI-Wrap-Over !
begin
  if a < 0     then a := gri - a;
  if b > gri-1 then b := b - gri;
  if a < b then within := (a <= x) and (x <= b)   //  0     a...b      gri-1
           else within := (x <= b) or  (a <= x);  //  0..b             a...gri-1
end;

function distance(a, b : longint) : longint;
// Distance with GRI-Wrap-Over !
begin
  if a <= b then distance := b - a
            else distance := gri - a + b;
end;


procedure timedelays;

const abstandpositiv = 7000;  // How many samples are blocked 
      abstandnegativ = 7000;  //      around a found correlation maximum ?

var k : longint;
    maximum_master : longint = 0;
    maximum_slave : longint = 0;
    firstoutside : boolean;

    pulszug1, pulszug2, pulszug3, pulszug4 : boolean; // Type of correlation maxima
    maximum1, maximum2, maximum3, maximum4 : longint; // Location of maxima

begin
  // Search for global most intense sample in correlations
  for k := 0 to gri-1 do
  begin
    if korrelation_master[k] > korrelation_master[maximum_master] then maximum_master := k;
    if korrelation_slave[k] > korrelation_slave[maximum_slave] then maximum_slave := k;
  end;

  pulszug1 := korrelation_master[maximum_master] > korrelation_slave[maximum_slave];

  if pulszug1 then
  begin
    maximum1 := maximum_master;
    writeln('Master pulse train at ', maximum1);
  end
  else
  begin
    maximum1 := maximum_slave;
    writeln('Slave  pulse train at ', maximum1);
  end;

  // Search for second most intense sample in correlations

  firstoutside := false;
  for k := 0 to gri-1 do
  if not within(k, maximum1 - abstandnegativ, maximum1 + abstandpositiv) then
  begin
    if not firstoutside then begin firstoutside := true; maximum_master := k; maximum_slave := k; end;

    if korrelation_master[k] > korrelation_master[maximum_master] then maximum_master := k;
    if korrelation_slave[k] > korrelation_slave[maximum_slave] then maximum_slave := k;
  end;

  pulszug2 := korrelation_master[maximum_master] > korrelation_slave[maximum_slave];

  if pulszug2 then
  begin
    maximum2 := maximum_master;
    writeln('Master pulse train at ', maximum2);
  end
  else
  begin
    maximum2 := maximum_slave;
    writeln('Slave  pulse train at ', maximum2);
  end;

  // Search for third most intense sample in correlations

  firstoutside := false;
  for k := 0 to gri-1 do
  if not within(k, maximum1 - abstandnegativ, maximum1 + abstandpositiv) then
  if not within(k, maximum2 - abstandnegativ, maximum2 + abstandpositiv) then
  begin
    if not firstoutside then begin firstoutside := true; maximum_master := k; maximum_slave := k; end;

    if korrelation_master[k] > korrelation_master[maximum_master] then maximum_master := k;
    if korrelation_slave[k] > korrelation_slave[maximum_slave] then maximum_slave := k;
  end;

  pulszug3 := korrelation_master[maximum_master] > korrelation_slave[maximum_slave];

  if pulszug3 then
  begin
    maximum3 := maximum_master;
    writeln('Master pulse train at ', maximum3);
  end
  else
  begin
    maximum3 := maximum_slave;
    writeln('Slave  pulse train at ', maximum3);
  end;


  // Search for fourth most intense sample in correlations

  firstoutside := false;
  for k := 0 to gri-1 do
  if not within(k, maximum1 - abstandnegativ, maximum1 + abstandpositiv) then
  if not within(k, maximum2 - abstandnegativ, maximum2 + abstandpositiv) then
  if not within(k, maximum3 - abstandnegativ, maximum3 + abstandpositiv) then
  begin
    if not firstoutside then begin firstoutside := true; maximum_master := k; maximum_slave := k; end;

    if korrelation_master[k] > korrelation_master[maximum_master] then maximum_master := k;
    if korrelation_slave[k] > korrelation_slave[maximum_slave] then maximum_slave := k;
  end;

  pulszug4 := korrelation_master[maximum_master] > korrelation_slave[maximum_slave];

  if pulszug4 then
  begin
    maximum4 := maximum_master;
    writeln('Master pulse train at ', maximum4);
  end
  else
  begin
    maximum4 := maximum_slave;
    writeln('Slave  pulse train at ', maximum4);
  end;

  writeln;
  // We should have identified one master and two slave pulse trains now:
  // Some cases necessary, as they are ordered by intensity.

  if pulszug1 then writeln('TD2: ', distance(maximum1, maximum2), ' TD3: ', distance(maximum1, maximum3), ' TD4: ', distance(maximum1, maximum4) );
  if pulszug2 then writeln('TD1: ', distance(maximum2, maximum1), ' TD3: ', distance(maximum2, maximum3), ' TD4: ', distance(maximum2, maximum4) );
  if pulszug3 then writeln('TD1: ', distance(maximum3, maximum1), ' TD2: ', distance(maximum3, maximum2), ' TD4: ', distance(maximum3, maximum4) );
  if pulszug4 then writeln('TD1: ', distance(maximum4, maximum1), ' TD2: ', distance(maximum4, maximum2), ' TD3: ', distance(maximum4, maximum3) );
end;


function mittelwert(var messung : tsignal) : longint;
var k : longint;
    summe : int64;
begin
  summe := 0;
  for k := 0 to gri-1 do summe := summe + messung[k];
  mittelwert := summe div gri;
end;

function rauschsumme(var messung : tsignal; mitte : longint) : longint;
var k : longint;
    summe : int64;
begin
  summe := 0;
  for k := 0 to gri-1 do summe := summe + abs(messung[k] - mitte);
  rauschsumme := summe div gri;
end;

function maximum(var messung : tsignal) : longint;
var k, stelle : longint;
begin
  stelle := 0;
  for k := 0 to gri-1 do if messung[k] > messung[stelle] then stelle := k;
  maximum := messung[stelle];
end;

function minimum(var messung : tsignal) : longint;
var k, stelle : longint;
begin
  stelle := 0;
  for k := 0 to gri-1 do if messung[k] < messung[stelle] then stelle := k;
  minimum := messung[stelle];
end;


begin
  writeln('Signal input');
  signal := signallesen(paramstr(1));

  writeln('  Minimum: ', minimum(signal), ' Mean: ', mittelwert(signal), ' Maximum: ', maximum(signal), 
          ' Noise: ', rauschsumme(signal, mittelwert(signal)),
          ' SNR: ', max(abs(maximum(signal) - mittelwert(signal)), abs(minimum(signal) - mittelwert(signal))) / rauschsumme(signal, mittelwert(signal))
         );

  writeln('Calculate pulse shape...');
  pulszug;
  signalschreiben(puls, 'pulse.dat');

  writeln('Correlation with single pulse...');
  korrelation := faltung_mit_puls(signal);
  signalschreiben(korrelation, 'correlation-pulse.dat');

  writeln('Correlation with master pulse train...');
  korrelation_master := faltung_mit_hauptzug(korrelation);
  signalschreiben(korrelation_master, 'correlation-master.dat');

  writeln('Correlation with slave pulse train...');
  korrelation_slave := faltung_mit_nebenzug(korrelation);
  signalschreiben(korrelation_slave, 'correlation-slave.dat');

  writeln;
  writeln('Calculate pulse train positions and Time Delays:');
  writeln;
  timedelays;
end.
