Быстрое преобразование сигнала в спекр и обратно (методы Хартли, Фурье и классический)
{$A+,B-,C+,D+,E-,F-,G+,H+,I+,J+,K-,L+,M-,N+,O-,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y+
,Z1}
{$MINSTACKSIZE $00004000}
{$MAXSTACKSIZE $00100000}
{$IMAGEBASE $00400000}
{$APPTYPE GUI}
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Buttons, ExtCtrls, ComCtrls, Menus;
type
TfmMain = class(TForm)
MainMenu1: TMainMenu;
N1: TMenuItem;
N2: TMenuItem;
StatusBar1: TStatusBar;
N3: TMenuItem;
imgInfo: TImage;
Panel1: TPanel;
btnStart: TSpeedButton;
procedure btnStartClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
end;
var
fmMain: TfmMain;
implementation
Uses PFiles;
{$R *.DFM}
function Power2(lPower: Byte): LongInt;
begin
Result := 1 Shl lPower;
end;
procedure ClassicDirect(Var aSignal, aSpR, aSpI: Array Of Double; N:
LongInt);
var lSrch : LongInt;
var lGarm : LongInt;
var dSumR : Double;
var dSumI : Double;
begin
for lGarm := 0 to N div 2 - 1 do begin
dSumR := 0;
dSumI := 0;
for lSrch := 0 to N - 1 do begin
dSumR := dSumR + aSignal[lSrch] * Cos(lGarm * lSrch / N * 2 * PI);
dSumI := dSumI + aSignal[lSrch] * Sin(lGarm * lSrch / N * 2 * PI);
end;
aSpR[lGarm] := dSumR;
aSpI[lGarm] := dSumI;
end;
end;
procedure ClassicInverce(Var aSpR, aSpI, aSignal: Array Of Double; N:
LongInt);
var lSrch : LongInt;
var lGarm : LongInt;
var dSum : Double;
begin
for lSrch := 0 to N-1 do begin
dSum := 0;
for lGarm := 0 to N div 2 -1
do dSum := dSum
+ aSpR[lGarm] * Cos(lSrch * lGarm * 2 * Pi / N)
+ aSpI[lGarm] * Sin(lSrch * lGarm * 2 * Pi / N);
aSignal[lSrch] := dSum*2;
end;
end;
Function InvertBits(BF, DataSize, Power: Word) : Word;
Var BR : Word;
Var NN : Word;
Var L : Word;
Begin
br:= 0;
nn:= DataSize;
For l:= 1 To Power Do
Begin
NN:= NN Div 2;
If (BF >= NN) Then
Begin
BR:= BR + Power2(l - 1);
BF:= BF - NN
End;
End;
InvertBits:=BR;
End;
Procedure FourierDirect(Var RealData,VirtData,ResultR,ResultV: Array Of
Double; DataSize: LongInt);
Var A1 : Real;
Var A2 : Real;
Var B1 : Real;
Var B2 : Real;
Var D2 : Word;
Var C2 : Word;
Var C1 : Word;
Var D1 : Word;
Var I : Word;
Var J : Word;
Var K : Word;
Var Cosin : Real;
Var Sinus : Real;
Var wIndex : Word;
Var Power : Word;
Begin
C1:= DataSize Shr 1;
C2:= 1;
for Power:=0 to 15 //hope it will be faster then
round(ln(DataSize)/ln(2))do if Power2(Power)=DataSize
then Break;
For I:= 1 To Power Do Begin
D1:= 0;
D2:= C1;
For J:= 1 To C2 Do Begin
wIndex:=InvertBits(D1 Div C1, DataSize, Power);
Cosin:= +(Cos((2 * Pi / DataSize)*wIndex));
Sinus:= -(Sin((2 * Pi / DataSize)*wIndex));
For K:= D1 To D2 - 1 Do Begin
A1:= RealData[K];
A2:= VirtData[K];
B1:= ((Cosin * RealData[K + C1] - Sinus * VirtData[K + C1]) );
B2:= ((Sinus * RealData[K + C1] + Cosin * VirtData[K + C1]) );
RealData[K]:= A1 + B1 ;
VirtData[K]:= A2 + B2 ;
RealData[K + C1]:= A1 - B1;
VirtData[K + C1]:= A2 - B2;
End;
Inc(D1,C1 * 2);
Inc(D2,C1 * 2);
End;
C1:=C1 Div 2;
C2:=C2 * 2;
End;
For I:= 0 To DataSize Div 2 -1 Do Begin
ResultR[I]:= + RealData[InvertBits(I, DataSize, Power)];
ResultV[I]:= - VirtData[InvertBits(I, DataSize, Power)];
End;
End;
Procedure Hartley(iSize: LongInt;Var aData : Array Of Double);
Type taDouble = Array[0..MaxLongInt Div SizeOf(Double)-1] Of Double;
Var prFI,prFN,prGI : ^taDouble;
Var rCos,rSin : Double;
Var rA,rB,rTemp : Double;
Var rC1,rC2,rC3,rC4 : Double;
Var rS1,rS2,rS3,rS4 : Double;
Var rF0,rF1,rF2,rF3 : Double;
Var rG0,rG1,rG2,rG3 : Double;
Var iK1,iK2,iK3,iK4 : LongInt;
Var iSrch,iK,iKX : LongInt;
Begin
iK2:=0;
For iK1:=1 To iSize-1 Do Begin
iK:=iSize Shr 1;
Repeat
iK2:=iK2 Xor iK;
If (iK2 And iK)<>0 Then Break;
iK:=iK Shr 1;
Until False;
If iK1>iK2 Then Begin
rTemp:=aData[iK1];
aData[iK1]:=aData[iK2];
aData[iK2]:=rTemp;
End;
End;
iK:=0;
While (1 Shl iK)<iSize Do Inc(iK);
iK:=iK And 1;
If iK=0 Then Begin
prFI:=@aData;
prFN:=@aData;
prFN := @prFN[iSize];
While Word(prFI)<Word(prFN) Do Begin
rF1:=prFI^[0]-prFI^[1];
rF0:=prFI^[0]+prFI^[1];
rF3:=prFI^[2]-prFI^[3];
rF2:=prFI^[2]+prFI^[3];
prFI^[2]:=rF0-rF2;
prFI^[0]:=rF0+rF2;
prFI^[3]:=rF1-rF3;
prFI^[1]:=rF1+rF3;
prFI := @prFI[4];
End;
End Else Begin
prFI:=@aData;
prFN:=@aData;
prFN := @prFN[iSize];
prGI:=prFI;
prGI := @prGI[1];
While Word(prFI)<Word(prFN) Do begin
rC1:=prFI^[0]-prGI^[0];
rS1:=prFI^[0]+prGI^[0];
rC2:=prFI^[2]-prGI^[2];
rS2:=prFI^[2]+prGI^[2];
rC3:=prFI^[4]-prGI^[4];
rS3:=prFI^[4]+prGI^[4];
rC4:=prFI^[6]-prGI^[6];
rS4:=prFI^[6]+prGI^[6];
rF1:=rS1-rS2;
rF0:=rS1+rS2;
rG1:=rC1-rC2;
rG0:=rC1+rC2;
rF3:=rS3-rS4;
rF2:=rS3+rS4;
rG3:=Sqrt(2)*rC4;
rG2:=Sqrt(2)*rC3;
prFI^[4]:=rF0-rF2;
prFI^[0]:=rF0+rF2;
prFI^[6]:=rF1-rF3;
prFI^[2]:=rF1+rF3;
prGI^[4]:=rG0-rG2;
prGI^[0]:=rG0+rG2;
prGI^[6]:=rG1-rG3;
prGI^[2]:=rG1+rG3;
prFI := @prFI[8];
prGI := @prGI[8];
End;
End;
If iSize<16 Then Exit;
Repeat
Inc(iK,2);
iK1:=1 Shl iK;
iK2:=iK1 Shl 1;
iK4:=iK2 Shl 1;
iK3:=iK2+iK1;
iKX:=iK1 Shr 1;
prFI:=@aData;
prGI:=prFI;
prGI := @prGI[iKX];
prFN:=@aData;
prFN := @prFN[iSize];
Repeat
rF1:= prFI^[000]-prFI^[iK1];
rF0:= prFI^[000]+prFI^[iK1];
rF3:= prFI^[iK2]-prFI^[iK3];
rF2:= prFI^[iK2]+prFI^[iK3];
prFI^[iK2]:=rF0-rF2;
prFI^[000]:=rF0+rF2;
prFI^[iK3]:=rF1-rF3;
prFI^[iK1]:=rF1+rF3;
rG1:=prGI^[0]-prGI^[iK1];
rG0:=prGI^[0]+prGI^[iK1];
rG3:=Sqrt(2)*prGI^[iK3];
rG2:=Sqrt(2)*prGI^[iK2];
prGI^[iK2]:=rG0-rG2;
prGI^[000]:=rG0+rG2;
prGI^[iK3]:=rG1-rG3;
prGI^[iK1]:=rG1+rG3;
prGI := @prGI[iK4];
prFI := @prFI[iK4];
Until Not (Word(prFI)<Word(prFN));
rCos:=Cos(Pi/2/Power2(iK));
rSin:=Sin(Pi/2/Power2(iK));
rC1:=1;
rS1:=0;
For iSrch:=1 To iKX-1 Do Begin
rTemp:=rC1;
rC1:=(rTemp*rCos-rS1*rSin);
rS1:=(rTemp*rSin+rS1*rCos);
rC2:=(rC1*rC1-rS1*rS1);
rS2:=(2*(rC1*rS1));
prFN:=@aData;
prFN := @prFN[iSize];
prFI:=@aData;
prFI := @prFI[iSrch];
prGI:=@aData;
prGI := @prGI[iK1-iSrch];
Repeat
rB:=(rS2*prFI^[iK1]-rC2*prGI^[iK1]);
rA:=(rC2*prFI^[iK1]+rS2*prGI^[iK1]);
rF1:=prFI^[0]-rA;
rF0:=prFI^[0]+rA;
rG1:=prGI^[0]-rB;
rG0:=prGI^[0]+rB;
rB:=(rS2*prFI^[iK3]-rC2*prGI^[iK3]);
rA:=(rC2*prFI^[iK3]+rS2*prGI^[iK3]);
rF3:=prFI^[iK2]-rA;
rF2:=prFI^[iK2]+rA;
rG3:=prGI^[iK2]-rB;
rG2:=prGI^[iK2]+rB;
rB:=(rS1*rF2-rC1*rG3);
rA:=(rC1*rF2+rS1*rG3);
prFI^[iK2]:=rF0-rA;
prFI^[0]:=rF0+rA;
prGI^[iK3]:=rG1-rB;
prGI^[iK1]:=rG1+rB;
rB:=(rC1*rG2-rS1*rF3);
rA:=(rS1*rG2+rC1*rF3);
prGI^[iK2]:=rG0-rA;
prGI^[0]:=rG0+rA;
prFI^[iK3]:=rF1-rB;
prFI^[iK1]:=rF1+rB;
prGI := @prGI[iK4];
prFI := @prFI[iK4];
Until Not (LongInt(prFI) < LongInt(prFN));
End;
Until Not (iK4<iSize);
End;
Procedure HartleyDirect(
Var aData : Array Of Double;
iSize : LongInt);
Var rA,rB : Double; Var iI,iJ,iK : LongInt; Begin
Hartley(iSize,aData);
iJ:=iSize-1;
iK:=iSize Div 2;
For iI:=1 To iK-1 Do Begin
rA:=aData[ii];
rB:=aData[ij];
aData[iJ]:=(rA-rB)/2;
aData[iI]:=(rA+rB)/2;
Dec(iJ);
End;
End;
Procedure HartleyInverce(
Var aData : Array Of Double;
iSize : LongInt);
Var rA,rB : Double;
Var iI,iJ,iK : LongInt;
Begin
iJ:=iSize-1;
iK:=iSize Div 2;
For iI:=1 To iK-1 Do Begin
rA:=aData[iI];
rB:=aData[iJ];
aData[iJ]:=rA-rB;
aData[iI]:=rA+rB;
Dec(iJ);
End;
Hartley(iSize,aData);
End;
//not tested
procedure HartleyDirectComplex(real,imag: Array of Double;n: LongInt);
var a,b,c,d : double;
q,r,s,t : double;
i,j,k : LongInt;
beginj:=n-1;
k:=n div 2;
for i:=1 to k-1 do begin
a := real[i]; b := real[j]; q:=a+b; r:=a-b;
c := imag[i]; d := imag[j]; s:=c+d; t:=c-d;
real[i] := (q+t)*0.5; real[j] := (q-t)*0.5;
imag[i] := (s-r)*0.5; imag[j] := (s+r)*0.5;
dec(j);
end;
Hartley(N,Real);
Hartley(N,Imag);
end;//not tested
procedure HartleyInverceComplex(real,imag: Array Of Double;N: LongInt);
var a,b,c,d :double;
q,r,s,t :double;
i,j,k :longInt;
begin
Hartley(N,real);
Hartley(N,imag);
j:=n-1;
k:=n div 2;
for i:=1 to k-1 do begin
a := real[i]; b := real[j]; q:=a+b; r:=a-b;
c := imag[i]; d := imag[j]; s:=c+d; t:=c-d;
imag[i] := (s+r)*0.5; imag[j] := (s-r)*0.5;
real[i] := (q-t)*0.5; real[j] := (q+t)*0.5;
dec(j);
end;
end;
procedure DrawSignal(var aSignal: Array Of Double;N,lColor : LongInt);
var lSrch : LongInt;
var lHalfHeight : LongInt;
begin
with fmMain do begin
lHalfHeight := imgInfo.Height Div 2;
imgInfo.Canvas.MoveTo(0,lHalfHeight);
imgInfo.Canvas.Pen.Color := lColor;
for lSrch := 0 to N-1 do begin
imgInfo.Canvas.LineTo(lSrch,Round(aSignal[lSrch]) + lHalfHeight);
end;
imgInfo.Repaint;
end;
end;
procedure DrawSpector(var aSpR, aSpI: Array Of Double;N, lColR, lColI :
LongInt);
var lSrch : LongInt;
var lHalfHeight : LongInt;
begin
with fmMain do begin
lHalfHeight := imgInfo.Height Div 2;
for lSrch := 0 to N Div 2 do begin
imgInfo.Canvas.Pixels[lSrch ,Round(aSpR[lSrch]/N) + lHalfHeight] :=
lColR;imgInfo.Canvas.Pixels[lSrch + N Div 2 ,Round(aSpI[lSrch]/N) +
lHalfHeight] := lColI;end;
imgInfo.Repaint;
end;
end;
const N = 512;
var aSignalR : Array[0..N-1] Of Double;//
var aSignalI : Array[0..N-1] Of Double;//
var aSpR, aSpI : Array[0..N Div 2-1] Of Double;//
var lFH : LongInt;
procedure TfmMain.btnStartClick(Sender: TObject);
const Epsilon = 0.00001;
var lSrch : LongInt;
var aBuff : Array[0..N-1] Of ShortInt;
begin
if lFH > 0 then begin
// Repeatif F.Read(lFH,@aBuff,N) <> N then begin
Exit;
end;
for lSrch := 0 to N-1 do begin
aSignalR[lSrch] := ShortInt(aBuff[lSrch]+$80);
aSignalI[lSrch] := 0;
end;
imgInfo.Canvas.Rectangle(0,0,imgInfo.Width,imgInfo.Height);
DrawSignal(aSignalR, N, $D0D0D0);
// ClassicDirect(aSignalR, aSpR, aSpI, N); //result in aSpR & aSpI,
aSignal unchanged
// FourierDirect(aSignalR, aSignalI, aSpR, aSpI, N); //result in aSpR &
aSpI, aSiggnalR & aSignalI modified
HartleyDirect(aSignalR, N); //result in source aSignal ;-)
DrawSpector(aSignalR, aSignalR[N Div 2 -1], N, $80, $8000);
DrawSpector(aSpR, aSpI, N, $80, $8000);
{ for lSrch := 0 to N div 2 -1 do begin //comparing classic & Hartley
if (Abs(aSpR[lSrch] - aSignal[lSrch]) > Epsilon)
or ((lSrch > 0) And (Abs(aSpI[lSrch] - aSignal[N - lSrch]) > Epsilon))
then MessageDlg('Error comparing',mtError,[mbOK],-1);
end;}
HartleyInverce(aSignalR, N); //to restore original signal with
HartleyDirect// ClassicInverce(aSpR, aSpI, aSignalR, N); //to restore original
signal with ClassicDirect or FourierDirect
for lSrch := 0 to N -1
do aSignalR[lSrch]:= aSignalR[lSrch]/N; //scaling
DrawSignal(aSignalR, N, $D00000);
Application.ProcessMessages;
// Until False;end;
end;
procedure TfmMain.FormCreate(Sender: TObject);
begin
lFH := F.Open('input.pcm', ForRead);
end;
procedure TfmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
F.Close(lFH);
end;
end.