return to article

DavData Software
Delphi-7 project
Exponential curve fitter source code
David Dirkse, feb. 2015

unit Unit2;
{
 function  y = ae^(bx)+c
 calculate a,b,c from [x1,y1] , [x2,y2] , ...., [xn,yn]
}

interface

uses sysutils;

type  Tpnt = record
               x,y : double;
              end;
      Tpoints = array[1..10] of Tpnt;

procedure makeExpFunction;

procedure GetParams(var pa,pb,pc : double);
function getXY(nr : byte; var pdx,pdy, pcx, pdq : double) : boolean;

implementation

uses unit1,unit3,unit4;

const FlString : string = '####0.0#####';
      scaleDy = 1;

var Epoints : TPoints;
    a,b,c : double;                       //y = a*e(bx)+c
    dx,dy : array[1..10] of double;
    cx    : array[1..10] of double;       //center between x values
    dq    : array[1..10] of double;       //differential quotient
    maxpnt : byte;                        //number of points

//testmode helpers

procedure GetParams(var pa,pb,pc : double);
begin
 pa := a; pb := b; pc := c;
end;

function getXY(nr : byte; var pdx,pdy, pcx, pdq : double) : boolean;
begin
 result := nr < maxpnt;
 if result then
  begin
   pdx := dx[nr];
   pdy := dy[nr];
   pcx := cx[nr];
   pdq := dq[nr];
  end;
end;

//----

procedure makeXYarrays;
var i : byte;
begin
 for i := 1 to maxpnt-1 do
  begin
   dx[i] := epoints[i+1].x - epoints[i].x;
   dy[i] := epoints[i+1].y - epoints[i].y;
   cx[i] := (epoints[i].x + epoints[i+1].x)/2;
   dq[i] := dy[i]/dx[i];
  end;
end;    

function Deviation(cc : double; var af : boolean) : double;
//calculate variation in growth factors after c asymptote correction
//called for fine tuning c in ae^(bx)+c formula
//cc: y value (asymptote); af : abort flag...abort if intersection
var i : byte;
    avG,ee : double;
    growthfactor : array[1..40] of double;
begin
 result := 0;
 avG := 0;
 for i := 1 to maxpnt-1 do
  begin
   ee := (Epoints[i].y-cc);//denominator
   if ee <> 0 then ee := (Epoints[i+1].y-cc)/ee;
   if ee <= 0 then //test for intersection of asymptote and function
    begin
     af := true;//abort flag
     exit;
    end;
   ee := ln(ee);
   avG := avG + ee;
   growthfactor[i] := exp(ee/dx[i]);//save
  end;
 af := false;
 avG := exp(avG /(Epoints[maxpnt].x-Epoints[1].x));//exp added
 ee := 0;
 for i := 1 to maxpnt-1 do
  ee := ee + abs(avG-growthFactor[i]);
 result :=  ee;
end;

procedure morepointsExp(var code : byte);
//called for >= 3 points
var Sum,deltaC,Bvar1,Bvar2 : double;
    i : byte;
    w : word;
    abort : boolean;
begin
//approximate value of b ....growthfactor e^b
 try
  sum := 0;
  for i := 1 to maxpnt-2 do
  Sum := Sum + ln(dq[i+1]/dq[i])/(cx[i+1]-cx[i]);
  b := Sum / (maxpnt - 2);
  if testmode then testreport(2);
//approximate value of a
  Sum := 0;
  for i := 1 to maxpnt-1 do
   Sum := Sum + dy[i]/(exp(b*epoints[i+1].x) - exp(b*epoints[i].x));
  a := Sum / (maxpnt - 1);
  if testmode then testreport(3);
//approximate value of c
  sum := 0;
  for i := 1 to maxpnt do
   sum := sum + epoints[i].y-a*exp(b*epoints[i].x);
  c := sum / maxpnt;
  if testmode then testreport(4);
 except
  code := 1;
  exit;
 end;
 if a > 0 then deltaC := -scaledY //move away from asymptote first
  else deltaC := scaledY;
 if testmode then
  begin
   testreport(5);
   testreportDC(deltaC);
  end;
 repeat
  Bvar1 := Deviation(c,abort);
  if abort then c := c + deltaC;
  if testmode then testreportDCA(Bvar1,c,deltaC,abort);
 until not abort;
 w := 0;
 deltaC := -deltaC;//towards asymptote first
 if testmode then testreport(6);
 repeat
  inc(w);
  Bvar2 := Deviation(c + deltaC,abort);
  if abort then deltaC := deltaC/2  //half distance in same direction
  else
   if Bvar2 < Bvar1 then
    begin
     Bvar1 := Bvar2;  //if better result
     c := c + deltaC;
    end
   else deltaC := -deltaC/2;
  if testmode then testreportDCA(Bvar1,c,deltaC,abort);
 until (abs(deltaC) <= 1e-8) or (w > 100);
 if abort then
  begin
   code := 1;
   exit;
  end;
 if w > 100 then
  begin
   code := 2;
   exit;
  end;
 if testmode then testreport(7);
//call least square procedure to make best a,b
 for i := 1 to maxpnt do epoints[i].y := ln(abs(epoints[i].y-c));
 buildexp12(epoints,maxpnt);  //call least square unit
 if a < 0 then a := -exp(epoints[1].y)
  else a := exp(epoints[1].y);
 b := epoints[2].y;
 if testmode then testreport(8);
end;

//--- main call ---

procedure makeExpFunction;
var i,code : byte; //0:OK  1,2: error
    s : string;
begin
 maxpnt := pcount;
 for i := 1 to maxpnt do            //get points
  begin
   Epoints[i].x := points[i].x;
   Epoints[i].y := points[i].y;
  end;
//
 makeXYarrays;
 if testmode then testreport(1);
 code := 0;
 morePointsExp(code);
 case code of
  0 : begin
       aa := a; bb := exp(b); cc := c;     //ae^(bx)+c ....>   ab^x + c
       setparamsOK(true);
       s := 'a,b,c calculated';
      end;
  1 : begin
       setparamsOK(false);
       s := 'no exponential function';
      end;
  2 : begin
       setparamsOK(false);
       s := 'points out of range';
      end;
   else s := '';
 end;
 form1.msgtext.Caption := s;
end;

end.