unit MCAD_MI1201_ThreadPeakCentering; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Math, MMTimer, MultiThreadList, MiscFunc, MCAD_MI1201_FileOutput, MCAD_MI1201_Range, c_ISSB, MassClbr, c_Mi1201, MITypes, MCAD_MI1201_Registry, MCAD_Event, McadUserTypes, MCAD_MI1201_TChartSeries, MCAD_MI1201_Thread_Types, MCAD_MI1201_Thread0, MCAD_MI1201_Thread1; type tLocalData=record // запись для последней измеренной точки Mass:tMass; Signal:double; end; tPeakCenterFlags=(pcBreak,pcStop,pcReMeasure,pcRunning,pcInterpolate); tFlags=set of tPeakCenterFlags; tMyEvent=(mevStart, mevStop,mevBreak,mevEnd,mevError); tErrorParameter=(erNoData,erNoMassSp,erIncorrectRatio,Ok);//флаги типов ошибок tPeakParameter=record Center: tMass; CenterSignal:double; MaxMass: tMass; MaxSignal: double; LeftBorder: tMass; RightBorder: tMass; HalfSigMassRight:tMass; HalfSigMassLeft:tMass; end; tDirection=(Right,Left); //направление сканирования пика tNoiseRatio=record BackGround:Single; //запись для соотношения шумов (см форму) Peak :Single; end; tMsThread=class(MCAD_MI1201_Thread1.tMsThread) private prLocalData:tLocalData; //поле последней измеренной точки prPeakParameter:tPeakParameter; // параметры пика prPeakCenterMass0:tMass;//начальная масса, заданная пользователем prFlagStatus:tFlags;//поле флагов prNoiseRatio:tNoiseRatio;//поле соотношения шумов //вычисление шума function GetNoiseValue(PlaceMass:tMass;Mult:single):double; //Добывание данных function GetData(InitMass:tMass):tLocalData; function FindPeakBorder(var InitMass:tMass):boolean; //ищет (должна) края пика //Влево-вправо от InitMass до массы, отличной более чем на значение шума procedure GoRightLeft(InitMass:tMass;var RightMass,LeftMass:tMass);//ищет рядом с InitMass //точки с сигналом, отличным более чем на значение шума procedure GoToOppositeSide(InitMass:tMass;var OppositeMass:tMass);//ищет // противоположную грань пика // ищет точку с сигналом, отличным более чем на значение шума в направлении Direction, // вызывается из GoRightLeft procedure FindNearestWithRealyDifferentSignal(InitMass:tMass;IgnorePointCount:integer; Direction:tDirection;LimitSignal:double;var NearestMass:tMass); function GetIgnorePointCount(Mass:tMass):integer;// возвращает число пропускаемых точек //Ищет подробно максимум возле максимума,найденного при обзорном проходе procedure FindMaxSignal(InitMass:tMass); procedure FindHalfSignalMass; // ищет на сторонах пика точки, с сигналом, <=половине максимального procedure MyNotifyEx(Event:tMI1201_Thread_EventID; parByte:tMyEvent; parWord:tErrorParameter); procedure SetNoiseRatio(Relation:tNoiseRatio); protected public function PeakCenter(AMass:tMass):boolean; procedure FindPeakCenter(Sender:TObject); procedure FindPeakCenterX; function FlagChecking(Flag:tPeakCenterFlags):boolean; //проверка поля флагов и вызов нотификатора constructor Create(const Name:string); property GetPeakParameter : tPeakParameter read prPeakParameter; property PeakCenterMass0:tMass read prPeakCenterMass0 write prPeakCenterMass0; property FlagStatus:tFlags read prFlagStatus write prFlagStatus; property LocalData: tLocalData read prLocalData; property NoiseRatioValue:tNoiseRatio read prNoiseRatio write SetNoiseRatio; end; implementation constructor tMsThread.Create(const Name:string); var InitNoiseRelation:tNoiseRatio; begin inherited Create(Name); FlagStatus:=[]; InitNoiseRelation.BackGround:=10; InitNoiseRelation.Peak:=1; NoiseRatioValue:=InitNoiseRelation; FlagStatus:=[pcInterpolate]; end; procedure tMsThread.SetNoiseRatio(Relation:tNoiseRatio); begin With Relation do if (BackGround=Peak) or (BackGround=0) or (Peak=0) then begin MyNotifyEx(evPeakCenter,mevError,erIncorrectRatio); exit; end else begin prNoiseRatio:=Relation; end; end; function tMsThread.GetData(InitMass:tMass):tLocalData; var s:tChannelSignal; begin try if pcReMeasure in FlagStatus then begin DataGetAndStoreEx(InitMass,False); //перемериваем Result.Mass:=Signals.Mass; Result.Signal:=Signals.Signals[tChannel(MainChannel)]; prLocalData:=Result; end else begin if pcInterpolate in FlagStatus then DataSignalGetEx(InitMass,MainChannel,[sgfInterpolate],S) else DataSignalGetEx(InitMass,MainChannel,[sgfDirect],S); //просто лезем в хранилище Result.Mass:=s.Mass; Result.Signal:=s.Signal; prLocalData:=Result; end; except end; end; function tMsThread.GetNoiseValue(PlaceMass:tMass;Mult:single):double; begin Result:=Mult*1.1*Sqrt(abs(GetData(PlaceMass).Signal)); end; procedure tMsThread.GoRightLeft(InitMass:tMass;var RightMass,LeftMass:tMass); begin FindNearestWithRealyDifferentSignal(InitMass,GetIgnorePointCount(InitMass),Right, GetNoiseValue(InitMass,NoiseRatioValue.BackGround), RightMass);//Направо FindNearestWithRealyDifferentSignal(InitMass,GetIgnorePointCount(InitMass),Left, GetNoiseValue(InitMass,NoiseRatioValue.BackGround), LeftMass);//Налево end; function tMsThread.GetIgnorePointCount(Mass:tMass):integer; begin Result:= Round(sqrt(1/MassStepAbsoluteMinCalculate(Mass))); //корень из величины, обратной шагу..... end; function tMsThread.FlagChecking(Flag:tPeakCenterFlags):boolean; begin Result:=Flag in FlagStatus; if Result then case Flag of pcBreak: MyNotifyEx(evPeakCenter,mevBreak,Ok); pcStop: MyNotifyEx(evPeakCenter,mevStop,Ok); end; end; procedure tMsThread.GoToOppositeSide(InitMass:tMass;var OppositeMass:tMass); var //ищет на друнгой стороне пика точку, с такой же или меньшей интесивностью сигнала dM,Mass:tMass; //т.е. другую грань пика Noise,InitSignal,CurSignal :double; IgnorePointCount:integer; begin InitSignal:=GetData(InitMass).Signal; Noise:=GetNoiseValue(InitMass,NoiseRatioValue.Peak); dM:=MassStepAbsoluteMinCalculate(InitMass); prPeakParameter.MaxSignal:=InitSignal; IgnorePointCount:=GetIgnorePointCount(InitMass); if InitMass=GetPeakParameter.RightBorder then dM:=-dM; Mass:=InitMass; repeat if FlagChecking(pcBreak) then exit; Mass:=Mass+dM*IgnorePointCount; CurSignal:=GetData(Mass).Signal; if CurSignal>GetPeakParameter.MaxSignal then begin prPeakParameter.MaxSignal:=CurSignal; prPeakParameter.MaxMass:=prLocalData.Mass; end; until (InitSignal - CurSignal)>=Noise ; while (InitSignal - CurSignal)>Noise do begin if FlagChecking(pcBreak) then exit; Mass:=Mass-dM; //пошли назад, и нашли ближайшую к нужной... CurSignal:=GetData(Mass).Signal; end; OppositeMass:=GetData(Mass+dM).Mass; end; function tMsThread.FindPeakBorder(var InitMass:tMass):boolean; var RightMass,LeftMass:tMass; InitSignal:double; begin InitMass:=GetData(InitMass).Mass; InitSignal:=prLocalData.Signal; Result:=FALSE; GoRightLeft(InitMass,RightMass,LeftMass); if (GetData(LeftMass).Signal < InitSignal) and (GetData(RightMass).Signal < InitSignal) then begin prPeakParameter.MaxMass:=InitMass; prPeakParameter.MaxSignal:=GetData(InitMass).Signal; exit; end; if (RightMass - InitMass) < abs(LeftMass - InitMass) then // в prLocalData сейчас лежат данные для RightMass if prLocalData.Signal > InitSignal then begin prPeakParameter.LeftBorder:=RightMass; GoToOppositeSide(prPeakParameter.LeftBorder,prPeakParameter.RightBorder); end else begin prPeakParameter.RightBorder:=InitMass; GoToOppositeSide(prPeakParameter.RightBorder,prPeakParameter.LeftBorder); end else if GetData(LeftMass).Signal > InitSignal then begin prPeakParameter.RightBorder:=LeftMass; GoToOppositeSide(prPeakParameter.RightBorder,prPeakParameter.LeftBorder); end else begin prPeakParameter.LeftBorder:=InitMass; GoToOppositeSide(prPeakParameter.LeftBorder,prPeakParameter.RightBorder); end; end; procedure tMsThread.FindMaxSignal(InitMass:tMass); var //справа-слева поищем (при скачках могли прозевать настоящий максимум) i:integer; Mass,dM:tMass; stop:boolean; begin Mass:=InitMass; dM:=MassStepAbsoluteMinCalculate(InitMass); Stop:=True; repeat if FlagChecking(pcBreak) then exit; for i:=1 to GetIgnorePointCount(InitMass) do begin if GetData(Mass+i*dM).Signal>prPeakParameter.MaxSignal then begin prPeakParameter.MaxSignal:=GetData(Mass+i*dM).Signal; prPeakParameter.MaxMass:=GetData(Mass+i*dM).Mass; end; end; stop:=not Stop; dM:=-dM until stop; end; procedure tMsThread.FindNearestWithRealyDifferentSignal(InitMass:tMass; IgnorePointCount:integer;Direction:tDirection; LimitSignal:double;var NearestMass:tMass); var dM,Mass:tMass; InitSignal,CurSignal:double; begin dM:=MassStepAbsoluteMinCalculate(InitMass); Mass:=InitMass; InitSignal:=GetData(InitMass).Signal; if Direction=Left then dM:=-dM; repeat if FlagChecking(pcBreak) then exit; Mass:=Mass+dM*IgnorePointCount; CurSignal:=GetData(Mass).Signal; until abs(InitSignal - CurSignal)>=LimitSignal; while abs(InitSignal - CurSignal)>=LimitSignal do begin if FlagChecking(pcBreak) then exit; Mass:=Mass-dM; //пошли назад, и нашли ближайшую к InitMass CurSignal:=GetData(Mass).Signal; end; NearestMass:=GetData(Mass+dM).Mass; end; procedure tMsThread.FindHalfSignalMass; begin with prPeakParameter do begin FindNearestWithRealyDifferentSignal(MaxMass,GetIgnorePointCount(MaxMass),Left, MaxSignal/2,HalfSigMassLeft); FindNearestWithRealyDifferentSignal(MaxMass,GetIgnorePointCount(MaxMass),Right, MaxSignal/2,HalfSigMassRight); end; end; procedure tMsThread.MyNotifyEx(Event:tMI1201_Thread_EventID; parByte:tMyEvent; parWord:tErrorParameter); var Ev:tMI1201_Thread_Event; begin Ev.EventID:=Event; Ev.ParameterB:=Ord(ParByte); Ev.ParameterW:=Ord(ParWord); NotifyA(Ev); end; function tMsThread.PeakCenter(AMass:tMass):boolean; begin MyNotifyEx(evPeakCenter,mevStart,Ok); FindPeakBorder(AMass); FindMaxSignal(GetPeakParameter.MaxMass); FindHalfSignalMass; with prPeakParameter do begin Center:=MassAjust((HalfSigMassLeft+HalfSigMassRight)/2); CenterSignal:= GetData(Center).Signal; end; if FlagChecking(pcBreak) then exit; MyNotifyEx(evPeakCenter,mevEnd,Ok); Result:=FALSE; end; procedure tMsThread.FindPeakCenter(Sender:TObject); begin PeakCenter(PeakCenterMass0); end; procedure tMsThread.FindPeakCenterX;// для постановки в очередь, так как там можно //передавать в качестве парметра только метод с одним параметром begin ExtendedCommand(FindPeakCenter); end; end.