unit MCAD_MI1201_Thread01; { Определение центра пика } {--------------------------------------------------------------------------- The control program for mass-spectrometer MI1201-AGM (c) Copyright Aleksandrov O.E., 2001 Molecular Physics department, USTU, Ekaterinsburg, K-2, 620002, RUSSIA phone 75-47-15 E-mail: aleks@dpt.ustu.ru Программа управления масс-спектрометром МИ1201-АГМ (c) Собственность Александрова О.Е., 2001 620002, Екатеринбург, К-2, УГТУ, Кафедра молекулярной физики тел. 75-47-15 E-mail: aleks@dpt.ustu.ru ----------------------------------------------------------------------------} interface uses Windows, Classes, SysUtils, SyncObjs, Registry, xSystem, MITypes, MassClbr, MCAD_MI1201_TChartSeries, MCAD_MI1201_Thread_Types, MCAD_MI1201_Thread0; const cRegSubKey='PeakCentering'; cChannelSignal0:tChannelSignal=(Time:0; Mass:0; Signal:0); type tNoiseData=record M:tMass; // значение массы N:cardinal; // число измерений ABS_dM, // отклонение ABS_deltaM:=Сумма(|M[i]-M[i-1]|) Sqr_dM:tMass; // отклонение Sqr_deltaM:=Сумма(Sqr(M[i]-M[i-1])) end; // ошибки процедуры поиска пика tPCError=( pceOK, pceMassSpectrometerKernelError, // ошибка ядра масс-спектрометра pceAborted, pceLowerLevelError, // ошибка процедур MCAD_MI1201_Thread0, используемых процедурой поиска pceUnknown, // неизвестная и неподдерживаемая ошибка pceBusy, // процесс поиска уже запущен pceCommandNotAvailable, // команда не может быть поставлена в очередь (только для асинхронного запуска) pceCommandPending, // команда уже поставлена в очередь (только для асинхронного запуска) pceMassSetError, // ??? - не использ. pceSignalReadError, // ошибка чтения сигнала pceSignalReadErrorMSisOFF, // ошибка чтения сигнала - масс-спектрометр не включен pcePeakCentering, // ??? - не использ. pcePeakCenteringFindPeakLeftSide, // невозможно найти левый край пика pcePeakCenteringFindPeakRightSide, // невозможно найти правый край пика pceNoiseError // ошибка измерения шума ); tPCErrorStrs=array[tPCError] of string; tPCFlag=( fDoNotRemeasure, // использовать только существующие данные fMoveToPeakCenterAtEnd, // переместиться на центр пика после окончания процесса поиска, иначе остаться там где закончился поиск fUseAdvancedMethod // использовать методы ускоренного поиска ); tPCFlags=set of tPCFlag; // тип измерения - откуда данные брать tPCMeasureType=(pcmtAuto, pcmtDirect, pcmtData); tPCEvent=( evPeakCenterError, // ошибка при вычисление новой калибровки шкалы масс evPeakCenterMeasureTypeChanged, evPeakCenterMassRangeChanged, evPeakCenterParametersChanged, evPeakCenterIntegrationTimeChanged, evPeakCenterFlagsChanged, evPeakCenterNoiseParametersChanged, evPeakCenterNoiseParametersChangeFail, evPeakCenterMsgStart, // запущен процесс поиска evPeakCenterMsgAborted, // прекращен поиск центра пика по запросу evPeakCenterMsgTryLocatePeak, // выполняется попытка обнаружить пик evPeakCenterMsgFailLocatePeak,// не удалось обнаружить пик evPeakCenterMsgPeakLocated, // удалось обнаружить пик evPeakCenterMsgTryLocatePeakTop,// выполняется попытка обнаружить вершину пика evPeakCenterMsgFailLocatePeakTop, // не удалось обнаружить вершину пика evPeakCenterMsgPeakTopLocated, // удалось обнаружить вершину пика evPeakCenterMsgTryLocatePeakSides,// выполняется попытка обнаружить стороны пика evPeakCenterMsgFailLocatePeakSides, // не удалось обнаружить стороны пика evPeakCenterMsgPeakLeftSideLocated, // удалось обнаружить стороны пика evPeakCenterMsgPeakRightSideLocated, // удалось обнаружить стороны пика evPeakCenterMsgPeakSidesLocated, // удалось обнаружить стороны пика evPeakCenterMsgEnd // завершено ); // результат сравнения сигналов tSignalComparisonResult=( scrEqual, scrBelow, scrAbove ); // идентификатор места на пике tPeakArea=(paNoPeak, paUnknown, paLeftSide, paRightSide, paTop); tQueryResult=(qrOK, qrCantLock, qrFail); tPeakPoint=record Signal:tChannelSignal; // данные о точке Area:tPeakArea; // идентифицированное место на пике end; tPeakPoints2=record HalfLevel:double; // уровень определения левой и правой границы Left,Right:tChannelSignal; // данные о левой и правой границах end; // результат поиска центра пика tPeakCenterProcessResult=(pcprInProgress, pcprError, pcprAborted, pcprDataValid); // перечень параметров процедуры поиска пика для property PeakCenterParameters[aIndex:tPeakCenterParameters] tPeakCenterParameters=( pcpMass0, pcpHalfLevel, pcpMassRange, pcpMinStepDivMass, pcpMaxStepDivMass ); // Исходные данные для поиска центра пика tPeakCenterData=record MeasureType:tPCMeasureType; // тип измерения - откуда точки брать IntegrationTime:word; // время интегрирования для процесса, если =0, то использовать глобальное case Byte of 0:( M0:double; // начальное значение массы HalfLevel:double; // значение уровня для которого фиксируются склоны пика, обычно 0.5 MassSearchRange:double; // диапазон масс, в котором ведется сканирование при поиске пика MassSearchMinStep:double; // минимальный шаг/масса, с которым ведется сканирование при поиске пика MassSearchMaxStep:double; // максимальный шаг/масса, с которым ведется сканирование при поиске пика ); 1:( Pars:array[tPeakCenterParameters] of double); end; tPeakCenterResult=record Valid:tPeakCenterProcessResult; Error:tPCError; // ошибка при поиске MeasureCount:cardinal; // число замеров точек на масс-спектрометре StartAtDateTime,EndAtDateTime:tDateTime; Stage:word; // Этап поиска M0:tMass; // начальное значение массы M:tMass; // значение массы для центра пика HalfLevel:double; // уровень определения левой и правой границы Width:double; // ширина пика MassDivWidth:double; // ширина отнесенная к массе Signal0:tChannelSignal; // данные максимума SignalHalfLeft,SignalHalfRight:tChannelSignal; // данные левой и правой границы InitialPoint:tPeakPoint; // исходная точка пика, которую обнаружила процедура FindPeakPointAtMass MaxSignalPoint:tPeakPoint; // исходная точка максимального сигнала на пике end; tChannelSignalID=record Signal:tChannelSignal; Age:cardinal; end; tPeakCenterIntermediateData=class private prSignals: array of tChannelSignalID; // данные по самой левой и самой правой точке пика prUsedCount:cardinal; // заполненная часть prMaxCount:cardinal; // максимальный размер prIncrement:cardinal; // приращение размера prAge:cardinal; function prfGetSignals(aIndex:cardinal):tChannelSignal; function prfSize:cardinal; function prfInsertAt(aIndex:cardinal; const aSignal:tChannelSignal):integer; procedure prfReplaceAt(aIndex:cardinal; const aSignal:tChannelSignal); procedure prfReAge; function prfMinAgeIndex:cardinal; public constructor Create(aMaxCount,aIncrement:cardinal); overload; constructor Create(aMaxCount:cardinal); overload; constructor Create; overload; destructor Destroy; override; procedure Clear; function Add(const aSignal:tChannelSignal):integer; function FindNearest(aSignal:tSignalValue):integer; property Count:cardinal read prUsedCount; property MaxCount:cardinal read prMaxCount; property Size:cardinal read prfSize; property Increment:cardinal read prIncrement; property Signals[aIndex:cardinal]:tChannelSignal read prfGetSignals; end; tNoiseParameters=record Valid:tQueryResult; Coeff:double; // коэффициент шума ZeroNoise:double; // шум при нулевом сигнале, постоянная составляющая шума CoeffTime:double; // время измерения prNoiseCoeff в мс end; tNoiseParametersArray=array of tNoiseParameters; tMassRange=object Min,Max:tMass; function InRange(aMass:tMass):boolean; overload; function InRange(aMassLeft,aMassRight:tMass):boolean; overload; procedure SetRange(aMass:tMass; aRelDelta:tMass); procedure SetRangeExact(aMass1,aMass2:tMass); end; const cPCErrors:tPCErrorStrs=( 'ОК', 'Ошибка ядра программы', 'Прервано', 'Ошибка базовых процедур', 'Неизвестная ошибка', 'Команда выполняется', 'Команда недоступна', 'Повтор команды до завершения предыдущей', 'Ошибка при установке массы', 'Ошибка при чтении сигнала', 'Установлено "измерять на приборе", но масс-спектрометр не включен', 'Ошибка при поиске центра пика', 'Ошибка при поиске левого склона пика', 'Ошибка при поиске правого склона пика', 'Ошибка при измерении шума' {, 'Ошибка при вычислении новой калибровки', 'Ошибка при установке новой калибровки', } ); type tMsThread = class(MCAD_MI1201_Thread0.tMsThread) private prFlags:tPCFlags; prError:tPCError; prCommandPendingCounter:integer; prPeakCenterData:tPeakCenterData; // исходные данные по поиску центра пика prDataLockMutex:tHANDLE; // для блокировки данных на период поиска prParameterSetResult:boolean; prPeakCenterTerminated:boolean; prNoiseParametersArray:tNoiseParametersArray; prPeakCenterResult:tPeakCenterResult; prPeakCenterIntermediateData:tPeakCenterIntermediateData; // промежуточные данные для хранения результатов процесса поиска function GetPeakCenterTerminated:boolean; function CheckPeakCenterTerminated:boolean; function PeakCenterCanContinue:boolean; procedure SetError(e:tPCError); procedure SetUnknownError(State:boolean); function PeakCenterGetSignal(AMass:tMass):tChannelSignal; function FindPeakPointAtMass(AMass:tMass):tPeakPoint; function FindPeakTopPointAt(const APoint:tPeakPoint):tPeakPoint; function RefineFindPeakTopPointAt(var aPeakTopSignal:tChannelSignal):boolean; function FindPeakSidePointsAt_Base(const aPeakTopPoint:tPeakPoint; var aPeakSidePoints:tPeakPoints2):boolean; function FindPeakSidePointsAt_Adv(const aPeakTopPoint:tPeakPoint; var aPeakSidePoints:tPeakPoints2):boolean; function FindPeakSidePointsAt(const aPeakPoint:tPeakPoint; var aPeakSidePoints:tPeakPoints2):boolean; function FindPeakSidePointAt( const aPeakTopPoint:tMass; const aPeakPoint:tChannelSignal; aLevel:double ):tPeakPoint; procedure SetPeakCenterMassRange(AMass:tMass); procedure SetIntegrationTime(aTime:word); function GetPeakCenterParameters(aIndex:tPeakCenterParameters):tMass; procedure SetPeakCenterParameters(aIndex:tPeakCenterParameters; aValue:tMass); procedure SetPeakCenterMass0(m:tMass); function GetPeakCenterNoiseParameters:tNoiseParameters; procedure SetPeakCenterNoiseParameters(aNoiseParameters:tNoiseParameters); function GetPeakCenterNoiseParametersArray(aIndex:integer):tNoiseParameters; procedure SetPeakCenterNoiseParametersArray(aIndex:integer; aNoiseParameters:tNoiseParameters); protected function CheckCommandPending:boolean; function CheckExCommand:boolean; function CheckExCommandPending:boolean; function ExecuteCmd(p:tNotifyEvent):integer; function PCErrorGet:string; procedure PCNotify(ev:tPCEvent); overload; procedure PCNotify(ev:tPCEvent; Value:word); overload; procedure ConfigSave; override; procedure ConfigRead; override; procedure FlagsSet(AFlags:tPCFlags); procedure DoPeakCenter(Sender:TObject); procedure SetMeasureType(MeasureType:tPCMeasureType); procedure PropertySetSuccess; procedure PropertySetFail; function DoRemeasure:boolean; public constructor Create(const Name:string); destructor Destroy; override; // блокирует данные procedure PeakCenterLockData; // деблокирует данные procedure PeakCenterUnLockData; // делает попытку блокировать данные function PeakCenterTryLockData:boolean; overload; function PeakCenterTryLockData(aTimeOut:cardinal):boolean; overload; // ищет центр пика для текущих параметров function PeakCenter:tMass; overload; function PeakCenter(M0:tMass):tMass; overload; // запускает фоновый поиск центра пика для текущих параметров и массы m function PeakCenterX(m:tMass):boolean; // сравнивает два сигнала с учетом шума function PeakCenterCompareSignal(const Signal1,Signal2:tChannelSignal):tSignalComparisonResult; // измеряет значение шума на местности // function PeakCenterGetNoise(RepeatCount:cardinal):tNoiseData; // возвращает значение шума для сигнала Signal function PeakCenterNoise(const Signal:tChannelSignal):tSignalValue; // функции сообщений об ошибке при поиске центра function PeakCenterIsNoError:boolean; function PeakCenterIsError:boolean; property PeakCenterError:tPCError read prError write SetError; property PeakCenterErrorMsg:string read PCErrorGet; function PeakCenterErrorMessage(aError:tPCError):string; // запрос немедленной остановки процесса поиска property PeakCenterTerminated:boolean read GetPeakCenterTerminated write prPeakCenterTerminated; property PeakCenterFlags:tPCFlags read prFlags write FlagsSet; property PeakCenterMass0:tMass read prPeakCenterData.M0 write SetPeakCenterMass0; property PeakCenterIntegrationTime:word read prPeakCenterData.IntegrationTime write SetIntegrationTime; // время интегрирования для процесса, если =0, то использовать глобальное property PeakCenterMeasureType:tPCMeasureType read prPeakCenterData.MeasureType write SetMeasureType; property PeakCenterResult:tPeakCenterResult read prPeakCenterResult; property PeakCenterPropertySetResult:boolean read prParameterSetResult; property PeakCenterMassRange:tMass read prPeakCenterData.MassSearchRange write SetPeakCenterMassRange; property PeakCenterParameters[aIndex:tPeakCenterParameters]:tMass read GetPeakCenterParameters write SetPeakCenterParameters; property PeakCenterData:tPeakCenterData read prPeakCenterData; // параметры шума для главного канала property PeakCenterNoiseParameters:tNoiseParameters read GetPeakCenterNoiseParameters write SetPeakCenterNoiseParameters; // параметры шума для всех каналов property PeakCenterNoiseParametersArray[Index:integer]:tNoiseParameters read GetPeakCenterNoiseParametersArray write SetPeakCenterNoiseParametersArray; end; implementation function tMsThread.PeakCenter(M0:tMass):tMass; begin Result:=-1; if CheckPeakCenterTerminated then Exit; if PeakCenterTryLockData then begin try PeakCenterMass0:=M0; Result:=PeakCenter; except end; PeakCenterUnlockData; end else begin SetError(pceBusy); end; end; function tMsThread.PeakCenter:tMass; var PeakPoint:tPeakPoint; Exception:boolean; PeakPoints:tPeakPoints2; begin Result:=-1; if CheckPeakCenterTerminated then Exit; Exception:=FALSE; if PeakCenterTryLockData then begin PCNotify(evPeakCenterMsgStart); // Задаем начальные значения SetError(pceOK); prPeakCenterIntermediateData.Clear; prPeakCenterResult.StartAtDateTime:=Now(); prPeakCenterResult.Valid:=pcprInProgress; prPeakCenterResult.Error:=pceOK; prPeakCenterResult.M0:=PeakCenterMass0; prPeakCenterResult.Stage:=0; prPeakCenterResult.MeasureCount:=0; prPeakCenterResult.MaxSignalPoint.Area:=paNoPeak; try // Пытаемся найти точку, принадлежащую ближайшему пику Inc(prPeakCenterResult.Stage); PeakPoint:=FindPeakPointAtMass(PeakCenterMass0); if PeakCenterCanContinue and (PeakPoint.Area in [paLeftSide, paRightSide, paTop]) then begin // Нашли. // Пытаемся найти вершину Inc(prPeakCenterResult.Stage); PeakPoint:=FindPeakTopPointAt(PeakPoint); if PeakCenterCanContinue and (PeakPoint.Area=paTop) then begin // Нашли вершину // Пытаемся точки на склонах Inc(prPeakCenterResult.Stage); if FindPeakSidePointsAt(PeakPoint, PeakPoints) then begin Inc(prPeakCenterResult.Stage); // Успешно - вычисляем параметры prPeakCenterResult.Valid:=pcprDataValid; Result:=(PeakPoints.Left.Mass+PeakPoints.Right.Mass)/2; prPeakCenterResult.M:=Result; prPeakCenterResult.Width:=(PeakPoints.Right.Mass-PeakPoints.Left.Mass); prPeakCenterResult.MassDivWidth:=prPeakCenterResult.M/prPeakCenterResult.Width; if (fMoveToPeakCenterAtEnd in PeakCenterFlags) and IsOn then begin // перемещаем текущую массу в центр пика Mass:=prPeakCenterResult.M; end; end; end; end; except Exception:=TRUE; end; try // Завершение Inc(prPeakCenterResult.Stage); if PeakCenterIsError then begin // Ошибка prPeakCenterResult.Valid:=pcprError; prPeakCenterResult.Error:=PeakCenterError; end else if CheckPeakCenterTerminated then begin // Прерывание prPeakCenterResult.Valid:=pcprAborted; SetError(pceAborted); prPeakCenterResult.Error:=PeakCenterError; end; prPeakCenterResult.EndAtDateTime:=Now(); PeakCenterTerminated:=TRUE; PCNotify(evPeakCenterMsgEnd); except Exception:=TRUE; end; PeakCenterUnlockData; end else begin SetError(pceBusy); end; SetUnknownError(Exception); end; function NotExceed(MassNext,MassLimit:tMass; StepSign:ShortInt):boolean; begin if StepSign=0 then begin Result:=FALSE; end else if StepSign>0 then begin Result:=MassNext<=MassLimit; end else begin Result:=MassLimit<=MassNext; end; end; function MaxStep(aStep1, aStep2:tMass):tMass; begin if aStep1>aStep2 then Result:=aStep1 else Result:=aStep2; end; function tMsThread.FindPeakSidePointAt( const aPeakTopPoint:tMass; const aPeakPoint:tChannelSignal; aLevel:double ):tPeakPoint; var Exception:boolean; StepSign:shortint; SignalNext,SignalLevel,SignalPred:tChannelSignal; DeltaMass:tMass; MassRange:tMassRange; lArea:tPeakArea; DoNotProcess:boolean; cr,cr0:tSignalComparisonResult; begin Result.Area:=paUnknown; if not PeakCenterCanContinue then Exit; Exception:=FALSE; try if aPeakTopPoint>aPeakPoint.Mass then begin // левая сторона lArea:=paLeftSide; if aLevelcr0) then begin SignalPred.Signal:=SignalNext.Signal-SignalPred.Signal; if Abs(SignalPred.Signal)>1e-10 then begin SignalPred.Mass:=SignalPred.Mass-SignalNext.Mass; SignalNext.Mass:=SignalNext.Mass+(SignalPred.Mass/SignalPred.Signal)*(SignalNext.Signal-aLevel); SignalNext.Signal:=aLevel; end; Result.Signal:=SignalNext; Result.Area:=lArea; end; except Exception:=TRUE; end; SetUnknownError(Exception); end; function tMsThread.FindPeakSidePointsAt_Adv(const aPeakTopPoint:tPeakPoint; var aPeakSidePoints:tPeakPoints2):boolean; var Exception:boolean; SignalOppSide:tChannelSignal; PointSide:tPeakPoint; Level:tMass; i:integer; begin Result:=FALSE; if not PeakCenterCanContinue then Exit; Exception:=FALSE; try aPeakSidePoints.HalfLevel:=PeakCenterData.HalfLevel; // Уровень "середины" пика Level:=aPeakTopPoint.Signal.Signal*aPeakSidePoints.HalfLevel; //Точка, наиболее близкая из измеренных ранее i:=prPeakCenterIntermediateData.FindNearest(Level); if i<0 then begin //не нашли - применяем испытанный метод Result:=FindPeakSidePointsAt_Base(aPeakTopPoint,aPeakSidePoints); end else begin // нашли - применяем новый, ускоренный метод PointSide:=FindPeakSidePointAt(aPeakTopPoint.Signal.Mass, prPeakCenterIntermediateData.Signals[i], Level); if PointSide.Area=paLeftSide then begin aPeakSidePoints.Left:=PointSide.Signal; prPeakCenterResult.SignalHalfLeft:=PointSide.Signal; PCNotify(evPeakCenterMsgPeakLeftSideLocated); SignalOppSide:=PeakCenterGetSignal(2*aPeakTopPoint.Signal.Mass-PointSide.Signal.Mass); PointSide:=FindPeakSidePointAt(aPeakTopPoint.Signal.Mass, SignalOppSide, Level); Result:= PointSide.Area = paRightSide; if Result then begin aPeakSidePoints.Right:=PointSide.Signal; prPeakCenterResult.SignalHalfRight:=PointSide.Signal; PCNotify(evPeakCenterMsgPeakRightSideLocated); end; end else if PointSide.Area=paRightSide then begin aPeakSidePoints.Right:=PointSide.Signal; prPeakCenterResult.SignalHalfRight:=PointSide.Signal; PCNotify(evPeakCenterMsgPeakRightSideLocated); SignalOppSide:=PeakCenterGetSignal(2*aPeakTopPoint.Signal.Mass-PointSide.Signal.Mass); PointSide:=FindPeakSidePointAt(aPeakTopPoint.Signal.Mass, SignalOppSide, Level); Result:= PointSide.Area = paLeftSide; if Result then begin aPeakSidePoints.Left:=PointSide.Signal; prPeakCenterResult.SignalHalfLeft:=PointSide.Signal; PCNotify(evPeakCenterMsgPeakLeftSideLocated); end; end; end; except Exception:=TRUE; end; SetUnknownError(Exception); end; function tMsThread.FindPeakSidePointsAt(const aPeakPoint:tPeakPoint; var aPeakSidePoints:tPeakPoints2):boolean; begin if not PeakCenterCanContinue then begin Result:=FALSE; Exit; end; PCNotify(evPeakCenterMsgTryLocatePeakSides); if fUseAdvancedMethod in PeakCenterFlags then Result:=FindPeakSidePointsAt_Adv(aPeakPoint,aPeakSidePoints) else Result:=FindPeakSidePointsAt_Base(aPeakPoint,aPeakSidePoints); if Result then begin PCNotify(evPeakCenterMsgPeakSidesLocated); end else begin PCNotify(evPeakCenterMsgFailLocatePeakSides); end; end; function tMsThread.FindPeakSidePointsAt_Base(const aPeakTopPoint:tPeakPoint; var aPeakSidePoints:tPeakPoints2):boolean; var Exception:boolean; SignalPred,SignalLeft,SignalRight:tChannelSignal; Level,DeltaMass:tMass; MassRange:tMassRange; DoNotProcess,Reverse:boolean; cr:tSignalComparisonResult; NotBelow:boolean; begin Result:=FALSE; if CheckPeakCenterTerminated then Exit; Exception:=FALSE; try aPeakSidePoints.HalfLevel:=PeakCenterData.HalfLevel; // Уровень "середины" пика Level:=aPeakTopPoint.Signal.Signal*aPeakSidePoints.HalfLevel; // Шаг в исходной точке DeltaMass:=MaxStep(MassStepAbsoluteMinCalculate(aPeakTopPoint.Signal.Mass)/aPeakTopPoint.Signal.Mass, prPeakCenterData.MassSearchMinStep); // Границы интервала сканирования MassRange.SetRange(aPeakTopPoint.Signal.Mass,prPeakCenterData.MassSearchRange); // Сигнал слева SignalLeft:=aPeakTopPoint.Signal; DoNotProcess:=FALSE; // Сравнение сигнала слева и в исходной точке cr:=scrEqual; // Уменьшения сигнала не было NotBelow:=TRUE; { Пока мы не можем утверждать, что сигнал слева достоверно отличается от исходной точки продолжаем смещение точки сканирования дальше от исходной, но не далее чем в пределах интервала сканирования } while PeakCenterCanContinue and Not Result and not DoNotProcess and MassRange.InRange(SignalLeft.Mass) and (cr in [scrEqual, scrBelow]) do begin SignalPred:=SignalLeft; SignalLeft:=PeakCenterGetSignal(SignalLeft.Mass*(1-DeltaMass)); DoNotProcess:=MassLimit; cr:=PeakCenterCompareSignal(SignalLeft, aPeakTopPoint.Signal); if (cr=scrAbove) and NotBelow then begin cr:=scrEqual; end else if cr=scrBelow then begin Result:=not (SignalLeft.Signal>Level); NotBelow:=FALSE; end; end; if PeakCenterCanContinue and Result and not DoNotProcess and MassRange.InRange(SignalLeft.Mass) and (cr in [scrEqual, scrBelow]) then begin SignalPred.Signal:=SignalLeft.Signal-SignalPred.Signal; if Abs(SignalPred.Signal)>1e-10 then begin SignalPred.Mass:=SignalPred.Mass-SignalLeft.Mass; SignalLeft.Mass:=SignalLeft.Mass+(SignalPred.Mass/SignalPred.Signal)*(SignalLeft.Signal-Level); SignalLeft.Signal:=Level; end; end else begin PeakCenterError:=pcePeakCenteringFindPeakLeftSide; Result:=FALSE; end; if Result then begin aPeakSidePoints.Left:=SignalLeft; prPeakCenterResult.SignalHalfLeft:=SignalLeft; PCNotify(evPeakCenterMsgPeakLeftSideLocated); Result:=FALSE; // Прыгаем, считая пик симметричным, относительно середины SignalRight.Mass:=2*aPeakTopPoint.Signal.Mass-SignalLeft.Mass; // Сигнал справа SignalRight:=PeakCenterGetSignal(SignalRight.Mass); // Невозможно движение вправо DoNotProcess:=MassLimit; if not DoNotProcess then begin Reverse:=SignalRight.SignalLevel); end; if PeakCenterCanContinue and Result and not DoNotProcess and MassRange.InRange(SignalRight.Mass) and (cr in [scrEqual, scrBelow]) then begin SignalPred.Signal:=SignalRight.Signal-SignalPred.Signal; if Abs(SignalPred.Signal)>1e-10 then begin SignalPred.Mass:=SignalPred.Mass-SignalRight.Mass; SignalRight.Mass:=SignalRight.Mass+(SignalPred.Mass/SignalPred.Signal)*(SignalRight.Signal-Level); SignalRight.Signal:=Level; end; end else begin PeakCenterError:=pcePeakCenteringFindPeakRightSide; Result:=FALSE; end; end else begin PeakCenterError:=pcePeakCenteringFindPeakRightSide; Result:=FALSE; end; end; if Result then begin aPeakSidePoints.Right:=SignalRight; prPeakCenterResult.SignalHalfRight:=SignalRight; prPeakCenterResult.HalfLevel:=aPeakSidePoints.HalfLevel; PCNotify(evPeakCenterMsgPeakRightSideLocated); end; except Exception:=TRUE; end; SetUnknownError(Exception); end; { Функция уточняет максимум пика для точки пика APoint возращает данные о местоположении найденной вершины } function tMsThread.RefineFindPeakTopPointAt(var aPeakTopSignal:tChannelSignal):boolean; var Exception:boolean; SignalLeft,SignalRight:tChannelSignal; Mass, MassLeft,MassRight,DeltaMass:tMass; MassRange:tMassRange; cr1,cr2:tSignalComparisonResult; DoNotProcessLeft,DoNotProcessRight:boolean; begin if not PeakCenterCanContinue then begin Result:=FALSE; Exit; end; Exception:=FALSE; try // Шаг в исходной точке Mass:=aPeakTopSignal.Mass; DeltaMass:=MaxStep(MassStepAbsoluteMinCalculate(Mass)/Mass, prPeakCenterData.MassSearchMinStep); // Точка слева и справа, отстоящие на шаг MassLeft:=Mass*(1-DeltaMass); MassRight:=Mass*(1+DeltaMass); // Границы интервала сканирования MassRange.SetRange(Mass,prPeakCenterData.MassSearchMaxStep*Mass); // Сигнал слева SignalLeft:=PeakCenterGetSignal(MassLeft); // Невозможно движение влево DoNotProcessLeft:=MassLimit; // Сигнал справа SignalRight:=PeakCenterGetSignal(MassRight); // Невозможно движение вправо DoNotProcessRight:=MassLimit; // Сравнение сигнала слева и в исходной точке cr1:=PeakCenterCompareSignal(SignalLeft, aPeakTopSignal); // Сравнение сигнала в исходной точке и справа cr2:=PeakCenterCompareSignal(SignalRight, aPeakTopSignal); { Пока мы не можем утверждать, что сигналы слева или справа достоверно отличаются от исходной точки продолжаем смещение точек сканирования дальше от исходной, но не далее чем в пределах интервала сканирования } while PeakCenterCanContinue and not (DoNotProcessRight and DoNotProcessLeft) and MassRange.InRange(MassLeft,MassRight) and ((cr1 in [scrEqual, scrAbove]) or (cr2 in [scrEqual, scrAbove])) do begin if not DoNotProcessLeft and (cr1 in [scrEqual, scrAbove]) then begin MassLeft:=SignalLeft.Mass*(1-DeltaMass); SignalLeft:=PeakCenterGetSignal(MassLeft); if SignalLeft.Signal>aPeakTopSignal.Signal then aPeakTopSignal:=SignalLeft; DoNotProcessLeft:=MassLimit; cr1:=PeakCenterCompareSignal(SignalLeft, aPeakTopSignal); end; if not DoNotProcessRight and (cr2 in [scrEqual, scrAbove]) then begin MassRight:=SignalRight.Mass*(1+DeltaMass); SignalRight:=PeakCenterGetSignal(MassRight); if SignalRight.Signal>aPeakTopSignal.Signal then aPeakTopSignal:=SignalRight; DoNotProcessRight:=MassLimit; cr2:=PeakCenterCompareSignal(SignalRight, aPeakTopSignal); end; end; prPeakCenterIntermediateData.Add(SignalLeft); prPeakCenterIntermediateData.Add(SignalRight); except Exception:=TRUE; end; Result:= not Exception; SetUnknownError(Exception); end; { Функция ищет максимум пика для точки пика APoint возращает данные о местоположении найденной вершины } function tMsThread.FindPeakTopPointAt(const APoint:tPeakPoint):tPeakPoint; var StepSign:shortint; Mass,LimitMass:tMass; SignalNext:tChannelSignal; DoNotProcess:boolean; cr:tSignalComparisonResult; Exception:boolean; DeltaMass:tMass; begin Exception:=FALSE; Result.Area:=paNoPeak; if not PeakCenterCanContinue then Exit; PCNotify(evPeakCenterMsgTryLocatePeakTop); try if APoint.Area=paTop then begin Result:=APoint; end else begin if APoint.Area=paLeftSide then begin StepSign:=1; end else if APoint.Area=paRightSide then begin StepSign:=-1; end else begin StepSign:=0; end; if StepSign<>0 then begin // Ищем грубо // Шаг в исходной точке Mass:=APoint.Signal.Mass; DeltaMass:=StepSign*MaxStep(MassStepAbsoluteMinCalculate(Mass)/Mass, prPeakCenterData.MassSearchMaxStep); // Границы интервала сканирования LimitMass:=Mass+0.5*PeakCenterMassRange*StepSign; // Начальное значение Result.Signal:=APoint.Signal; SignalNext:=APoint.Signal; DoNotProcess:=FALSE; cr:=scrEqual; // Ищем точку с макс. сигналом грубо (с максимальным шагом) while PeakCenterCanContinue and (not DoNotProcess) and NotExceed(SignalNext.Mass,LimitMass,StepSign) and (cr<>scrBelow) do begin SignalNext:=PeakCenterGetSignal(SignalNext.Mass*(1+DeltaMass)); DoNotProcess:=MassLimit; cr:=PeakCenterCompareSignal(SignalNext,Result.Signal); if SignalNext.Signal>Result.Signal.Signal then Result.Signal:=SignalNext; prPeakCenterIntermediateData.Add(SignalNext); end; // Уточняем if PeakCenterCanContinue then begin Exception:=not RefineFindPeakTopPointAt(Result.Signal); if not Exception then begin Result.Area:=paTop; prPeakCenterResult.MaxSignalPoint:=Result; end; end; end; end; except Exception:=TRUE; end; SetUnknownError(Exception); if Result.Area=paNoPeak then begin if PeakCenterTerminated or PeakCenterIsError then // PCNotify(evPeakCenterMsgAborted) else PCNotify(evPeakCenterMsgFailLocatePeakTop); end else PCNotify(evPeakCenterMsgPeakTopLocated); end; { Функция ищет ближайшую к AMass точку, принадлежащую пику и возращает данные о местоположении найденной точки } function tMsThread.FindPeakPointAtMass(AMass:tMass):tPeakPoint; var Exception:boolean; Signal0,SignalLeft,SignalRight:tChannelSignal; MassLeft,MassRight,DeltaMass,MassMin,MassMax:tMass; crL0,cr0R,crLR:tSignalComparisonResult; DoNotProcessLeft,DoNotProcessRight:boolean; begin Exception:=FALSE; // Возврат, в случае неуспеха Result.Area:=paNoPeak; if not PeakCenterCanContinue then Exit; // Сообщение: запуск поиска точки на пике PCNotify(evPeakCenterMsgTryLocatePeak); try // Сигнал в исходной точке Signal0:=PeakCenterGetSignal(AMass); // Шаг в исходной точке DeltaMass:=MaxStep(MassStepAbsoluteMinCalculate(AMass)/AMass, prPeakCenterData.MassSearchMinStep); // Точка слева и справа, отстоящие на шаго MassLeft:=AMass*(1-DeltaMass); MassRight:=AMass*(1+DeltaMass); // Границы интервала сканирования MassMin:=AMass-PeakCenterMassRange; MassMax:=AMass+PeakCenterMassRange; // Сигнал слева SignalLeft:=PeakCenterGetSignal(MassLeft); // Невозможно движение влево DoNotProcessLeft:=MassLimit; // Сигнал справа SignalRight:=PeakCenterGetSignal(MassRight); // Невозможно движение вправо DoNotProcessRight:=MassLimit; // Сравнение сигнала слева и в исходной точке crL0:=PeakCenterCompareSignal(SignalLeft,Signal0); // Сравнение сигнала в исходной точке и справа cr0R:=PeakCenterCompareSignal(Signal0, SignalRight); // Сравнение сигнала слева исправа crLR:=PeakCenterCompareSignal(SignalLeft, SignalRight); { Пока мы не можем утверждать, что сигналы слева или справа достоверно отличаются от исходной точки продолжаем смещение точек сканирования дальше от исходной, но не далее чем в пределах интервала сканирования } while PeakCenterCanContinue and not (DoNotProcessRight and DoNotProcessLeft) and (MassMin<=MassLeft) and (MassRight<=MassMax) and (crL0=scrEqual) and (cr0R=scrEqual) and (crLR=scrEqual) do begin if DeltaMassprPeakCenterData.MassSearchMaxStep then DeltaMass:=prPeakCenterData.MassSearchMaxStep; end; if not DoNotProcessLeft then begin MassLeft:=SignalLeft.Mass*(1-DeltaMass); SignalLeft:=PeakCenterGetSignal(MassLeft); DoNotProcessLeft:=MassLimit; crL0:=PeakCenterCompareSignal(SignalLeft,Signal0); end; if not DoNotProcessRight then begin MassRight:=SignalRight.Mass*(1+DeltaMass); SignalRight:=PeakCenterGetSignal(MassRight); DoNotProcessRight:=MassLimit; cr0R:=PeakCenterCompareSignal(Signal0, SignalRight); end; crLR:=PeakCenterCompareSignal(SignalLeft, SignalRight); end; { разбираемся, где это находится } if PeakCenterCanContinue then begin if (crL0<>scrEqual) and (cr0R<>scrEqual) then begin // На пике if (crL0=scrBelow) and (cr0R=scrAbove) then begin // Возможно вершина if (crLR=scrEqual) then begin Result.Signal:=Signal0; Result.Area:=paTop; end else if (crLR=scrBelow) then begin Result.Signal:=SignalRight; Result.Area:=paRightSide; end else begin Result.Signal:=SignalLeft; Result.Area:=paLeftSide; end; end else if (crL0=scrBelow) and (cr0R=scrBelow) then begin // Левый склон Result.Signal:=SignalRight; Result.Area:=paLeftSide; end else if (crL0=scrAbove) and (cr0R=scrAbove) then begin // Правый склон Result.Signal:=SignalLeft; Result.Area:=paRightSide; end else {Похоже слева и справа есть пик} if SignalLeft.Signal>SignalRight.Signal then begin // Пожалуй слева побольше if (crL0=scrAbove) then begin Result.Signal:=SignalLeft; Result.Area:=paRightSide; end else begin Result.Signal:=Signal0; Result.Area:=paLeftSide; end; end else begin // Пожалуй справа побольше if (cr0R=scrBelow) then begin Result.Signal:=SignalRight; Result.Area:=paLeftSide; end else begin Result.Signal:=Signal0; Result.Area:=paRightSide; end; end; end else if (crLR<>scrEqual) then begin if (crLR=scrAbove) then begin Result.Signal:=SignalLeft; Result.Area:=paRightSide; end else begin Result.Signal:=SignalRight; Result.Area:=paLeftSide; end; end else if (cr0R<>scrEqual) then begin if (cr0R=scrBelow) then begin Result.Signal:=SignalRight; Result.Area:=paLeftSide; end else begin Result.Signal:=Signal0; Result.Area:=paRightSide; end; end else if (crL0<>scrEqual) then begin if (crL0=scrAbove) then begin Result.Signal:=SignalLeft; Result.Area:=paRightSide; end else begin Result.Signal:=Signal0; Result.Area:=paLeftSide; end; end; end; except Exception:=TRUE; end; prPeakCenterResult.InitialPoint:=Result; SetUnknownError(Exception); if Result.Area=paNoPeak then begin if PeakCenterTerminated or PeakCenterIsError then // PCNotify(evPeakCenterMsgAborted) else PCNotify(evPeakCenterMsgFailLocatePeak); end else begin prPeakCenterIntermediateData.Add(Result.Signal); PCNotify(evPeakCenterMsgPeakLocated); end; end; { Функция сравнивает сигнал с учетом шума } function tMsThread.PeakCenterCompareSignal(const Signal1,Signal2:tChannelSignal):tSignalComparisonResult; var dS:tSignalValue; n1,n2,na:tSignalValue; begin dS:=Signal2.Signal-Signal1.Signal; n1:=PeakCenterNoise(Signal1); n2:=PeakCenterNoise(Signal2); na:=(n1+n2)/2; if Abs(dS)<=na then begin Result:=scrEqual; end else if dS>0 then begin Result:=scrBelow; end else begin Result:=scrAbove; end; end; function tMsThread.PeakCenterNoise(const Signal:tChannelSignal):tSignalValue; var cl:integer; begin // шум падает пропорционально корню из времени измерения сигнала cl:=Ord(MainChannel); Result:=(prNoiseParametersArray[cl].Coeff*Sqrt(Abs(Signal.Signal)) +prNoiseParametersArray[cl].ZeroNoise )/(Sqrt(Signal.Time)); end; procedure tMsThread.SetUnknownError(State:boolean); begin if State then SetError(pceUnknown); end; constructor tMsThread.Create(const Name:string); var i:integer; begin prDataLockMutex:=CreateMutex(NIL,FALSE,NIL); Win32Check(prDataLockMutex<>0); PeakCenterLockData; prPeakCenterIntermediateData:=tPeakCenterIntermediateData.Create(100,30); // промежуточные данные для хранения результатов процесса поиска prPeakCenterData.M0:=0; prPeakCenterData.HalfLevel:=0.5; prPeakCenterData.MassSearchRange:=0.3; prPeakCenterData.MassSearchMaxStep:=0.0005; prPeakCenterData.MassSearchMinStep:=0.0001; SetLength(prNoiseParametersArray,ChannelCount); for i:=0 to Pred(Length(prNoiseParametersArray)) do begin with prNoiseParametersArray[i] do begin Coeff:=0.05; // коэффициент шума CoeffTime:=1; // время измерения prNoiseCoeff в мс ZeroNoise:=0; // коэффициент шума end; end; with prNoiseParametersArray[Ord(IonCounter)] do begin Coeff:=0.5; // коэффициент шума CoeffTime:=1; // время измерения prNoiseCoeff в мс ZeroNoise:=0.02; // коэффициент шума end; PeakCenterUnLockData; inherited Create(Name); end; destructor tMsThread.Destroy; begin inherited Destroy; PeakCenterLockData; prPeakCenterIntermediateData.Free; CloseHandle(prDataLockMutex); end; procedure tMsThread.ConfigSave; begin Inherited; if not Assigned(Self) then Exit; PeakCenterLockData; if RegistryOpenKey(cRegSubKey,TRUE) then begin try if Length(prNoiseParametersArray)>0 then Registry.WriteBinaryData('NoiseParameters', prNoiseParametersArray[0], SizeOf(prNoiseParametersArray[0])*Length(prNoiseParametersArray)); except end; try Registry.WriteFloat('MassSearchRange', prPeakCenterData.MassSearchRange); except end; try Registry.WriteFloat('MassSearchMaxStep', prPeakCenterData.MassSearchMaxStep); except end; try Registry.WriteFloat('MassSearchMinStep', prPeakCenterData.MassSearchMinStep); except end; try Registry.WriteInteger('MeasureType', Ord(prPeakCenterData.MeasureType)); except end; try Registry.WriteInteger('Flags',Byte(prFlags)); except end; try Registry.WriteInteger('IntegrationTime',prPeakCenterData.IntegrationTime); except end; end; Registry.CloseKey; PeakCenterUnlockData; end; procedure tMsThread.ConfigRead; var x:tMass; i:integer; begin Inherited; if not Assigned(Self) then Exit; PeakCenterLockData; if RegistryOpenKey(cRegSubKey,FALSE) then begin try if Length(prNoiseParametersArray)>0 then Registry.ReadBinaryData('NoiseParameters', prNoiseParametersArray[0], SizeOf(prNoiseParametersArray[0])*Length(prNoiseParametersArray)); except end; try prPeakCenterData.MassSearchRange:=Abs(Registry.ReadFloat('MassSearchRange')); except end; try prPeakCenterData.MassSearchMaxStep:=Abs(Registry.ReadFloat('MassSearchMaxStep')); except end; try prPeakCenterData.MassSearchMinStep:=Abs(Registry.ReadFloat('MassSearchMinStep')); except end; if prPeakCenterData.MassSearchMaxStep=0) and (i<=Ord(High(prPeakCenterData.MeasureType))) then Byte(prPeakCenterData.MeasureType):=i; except end; try i:=Registry.ReadInteger('Flags'); if (i>=0) and (i<(1 shl Succ(Ord(High(tPCFlag)))) ) then Byte(prFlags):=i; except end; try prPeakCenterData.IntegrationTime:=Registry.ReadInteger('IntegrationTime'); except end; end; Registry.CloseKey; PeakCenterUnlockData; end; function tMsThread.CheckCommandPending:boolean; begin if prCommandPendingCounter>0 then begin Result:=FALSE; SetError(pceCommandPending); end else begin Result:=TRUE; end; end; function tMsThread.CheckExCommand:boolean; begin Result:=IsON and IsNoError; if not Result then SetError(pceCommandNotAvailable); end; function tMsThread.CheckExCommandPending:boolean; begin Result:=CheckExCommand; if Result then Result:=CheckCommandPending; end; procedure tMsThread.SetError(e:tPCError); begin if (e=pceOK) then begin prError:=pceOK; PCNotify(evPeakCenterError); end else if (prError=pceOK) then begin prError:=e; PCNotify(evPeakCenterError); end; end; function tMsThread.DoRemeasure:boolean; begin Result:=(PeakCenterMeasureType=pcmtDirect) or ((PeakCenterMeasureType=pcmtAuto) and MeasureAvailable); end; function tMsThread.PeakCenterGetSignal(AMass:tMass):tChannelSignal; var MgrFlags:tSignalsGetFlags; begin if not PeakCenterCanContinue then begin Result:=cChannelSignal0 ;Exit; end; case PeakCenterMeasureType of pcmtAuto: begin if IsON then MgrFlags:=[sgfDirect] else MgrFlags:=[sgfInterpolate]; end; pcmtDirect: if IsON then begin MgrFlags:=[sgfDirect]; end else begin SetError(pceSignalReadErrorMSisOFF); Exit; end; pcmtData: MgrFlags:=[sgfInterpolate]; end; Inc(prPeakCenterResult.MeasureCount); if not SignalGetEx(AMass, MainChannel, MgrFlags, PeakCenterIntegrationTime, Result) then SetError(pceSignalReadError); end; (*function tMsThread.PeakCenterGetNoise(RepeatCount:cardinal):tNoiseData; var i:cardinal; x,x1,x2,dx:extended; begin Result.M:=Mass; Result.N:=RepeatCount; Result.ABS_dM:=0; Result.Sqr_dM:=0; if RepeatCount=0 then Exit; try x2:=PeakCenterGetSignal(Mass).Signal; x:=x2; if DoRemeasure then begin for i:=1 to RepeatCount do begin x1:=x2; x2:=PeakCenterGetSignal(Mass).Signal; x:=x+x2; dx:=x2-x1; Result.ABS_dM:=Result.ABS_dM+Abs(dx); Result.Sqr_dM:=Result.Sqr_dM+Sqr(dx); end; // x:=x/Succ(RepeatCount); // prNoiseCoeff:=(Result.ABS_dM/RepeatCount)/Sqrt(Abs(x)); end else begin // if prNoiseCoeff<=0 then // prNoiseCoeff:=1; // Result.ABS_dM:=prNoiseCoeff*Sqrt(Abs(x2)); // Result.Sqr_dM:=Sqr(Result.ABS_dM); end; except SetError(pceNoiseError); end; end;*) function tMsThread.ExecuteCmd(p:tNotifyEvent):integer; begin InterlockedIncrement(prCommandPendingCounter); Result:=ExtendedCommand(p); if Result=-1 then InterlockedDecrement(prCommandPendingCounter); end; function tMsThread.PeakCenterX(m:tMass):boolean; begin PeakCenterMass0:=m; Result:=CheckExCommand; if Result Then begin Result:=CheckCommandPending; if Result then begin Result:=ExecuteCmd(DoPeakCenter)<>-1; end; end else begin Result:=PeakCenter<>-1; end; end; procedure tMsThread.DoPeakCenter; begin SetError(pceOK); try PeakCenter; except end; InterlockedDecrement(prCommandPendingCounter); end; function tMsThread.PCErrorGet:string; begin Result:=PeakCenterErrorMessage(prError) end; function tMsThread.PeakCenterErrorMessage(aError:tPCError):string; begin if (prError=pceOK) and (ErrorCode<>ecOK) then begin Result:=cPCErrors[pceMassSpectrometerKernelError]; end else begin Result:=cPCErrors[prError]; end; if (ErrorCode<>ecOK) then begin Result:=Result+'. Ядро масс-спек.: '+ErrorMsg; end; end; procedure tMsThread.PCNotify(ev:tPCEvent); begin Notify(evPeakCenter, Byte(ev),0); end; procedure tMsThread.PCNotify(ev:tPCEvent; Value:word); begin Notify(evPeakCenter, Byte(ev),Value); end; procedure tMsThread.PeakCenterLockData; begin WaitForSingleObject(prDataLockMutex, INFINITE); end; procedure tMsThread.PeakCenterUnLockData; begin Win32Check(ReleaseMutex(prDataLockMutex)); end; function tMsThread.PeakCenterTryLockData:boolean; begin Result:=WaitForSingleObject(prDataLockMutex, 0)=WAIT_OBJECT_0; end; function tMsThread.PeakCenterTryLockData(aTimeOut:cardinal):boolean; begin Result:=WaitForSingleObject(prDataLockMutex, aTimeOut)=WAIT_OBJECT_0; end; procedure tMsThread.SetMeasureType(MeasureType:tPCMeasureType); begin if PeakCenterTryLockData then begin prPeakCenterData.MeasureType:=MeasureType; PropertySetSuccess; PeakCenterUnlockData; end else begin PropertySetFail; end; PCNotify(evPeakCenterMeasureTypeChanged); end; procedure tMsThread.FlagsSet(AFlags:tPCFlags); begin if PeakCenterTryLockData then begin prFlags:=AFlags; PropertySetSuccess; PeakCenterUnlockData; end else begin PropertySetFail; end; PCNotify(evPeakCenterFlagsChanged); end; procedure tMsThread.PropertySetSuccess; begin prParameterSetResult:=TRUE; end; procedure tMsThread.PropertySetFail; begin prParameterSetResult:=FALSE; end; function tMsThread.GetPeakCenterTerminated:boolean; begin Result:=prPeakCenterTerminated or Terminated; end; function tMsThread.CheckPeakCenterTerminated:boolean; begin Result:=prPeakCenterTerminated or Terminated; if Result then begin PCNotify(evPeakCenterMsgAborted); end; end; function tMsThread.PeakCenterIsNoError:boolean; begin Result:=not PeakCenterIsError; end; function tMsThread.PeakCenterIsError:boolean; begin Result:=IsError or (PeakCenterError<>pceOK); end; procedure tMsThread.SetPeakCenterMassRange(AMass:tMass); begin if PeakCenterTryLockData then begin prPeakCenterData.MassSearchRange:=Abs(AMass); PropertySetSuccess; PeakCenterUnlockData; end else begin PropertySetFail; end; PCNotify(evPeakCenterMassRangeChanged); end; procedure tMsThread.SetIntegrationTime(aTime:word); begin if PeakCenterTryLockData then begin prPeakCenterData.IntegrationTime:=aTime; PropertySetSuccess; PeakCenterUnlockData; PCNotify(evPeakCenterIntegrationTimeChanged); end else begin PropertySetFail; end; PCNotify(evPeakCenterParametersChanged); end; function tMsThread.GetPeakCenterParameters(aIndex:tPeakCenterParameters):tMass; begin Result:=prPeakCenterData.Pars[aIndex]; end; procedure tMsThread.SetPeakCenterParameters(aIndex:tPeakCenterParameters; aValue:tMass); var Success:boolean; begin if PeakCenterTryLockData then begin Success:=TRUE; case aIndex of pcpMassRange: begin prPeakCenterData.MassSearchRange:=Abs(aValue); end; pcpMinStepDivMass: begin aValue:=Abs(aValue); if aValue>prPeakCenterData.MassSearchMaxStep then prPeakCenterData.MassSearchMinStep:=prPeakCenterData.MassSearchMaxStep else prPeakCenterData.MassSearchMinStep:=aValue; end; pcpMaxStepDivMass: begin aValue:=Abs(aValue); if aValue=0) and (aIndex=0) and (aIndex=Count then Result:=prSignals[Pred(Count)].Signal else Result:=prSignals[aIndex].Signal; end; function tPeakCenterIntermediateData.prfSize:cardinal; begin Result:=Length(prSignals); end; procedure tPeakCenterIntermediateData.prfReAge; var i:cardinal; da:cardinal; begin da:=High(prAge)-Count; prAge:=Count; for i:=0 to Pred(Count) do begin prSignals[i].Age:=prSignals[i].Age-da; end; end; function tPeakCenterIntermediateData.prfMinAgeIndex:cardinal; var i:cardinal; ma:cardinal; begin Result:=0; if Count>0 then begin ma:=prSignals[0].Age; for i:=1 to Pred(Count) do if prSignals[i].AgeCount then aIndex:=Count; if Count>=Size then begin // Увеличение размера if CountMaxCount then begin sz:=MaxCount; end; if sz>Count then try SetLength(prSignals,sz); except end; end; end; if CountaIndex then begin Move(prSignals[i],prSignals[i+1],SizeOf(prSignals[i])*(i-aIndex)); end else if (i=prSignals[Pred(Count)].Signal.Mass then begin Result:=Pred(Count); if aSignal.Mass=prSignals[Result].Signal.Mass then begin prfReplaceAt(Result,aSignal); end else begin prfInsertAt(Succ(Result),aSignal); end; end else begin // ищем место вставки i:=0; j:=Pred(Count); repeat Result:=(i+j) div 2; if aSignal.Mass=prSignals[Result].Signal.Mass then begin prfReplaceAt(Result,aSignal); Exit; end else if aSignal.Mass=j; prfInsertAt(Result,aSignal); end; { $IfOpt R+} for i:=1 to Pred(Count) do begin if prSignals[Pred(i)].Signal.Mass>prSignals[i].Signal.Mass then RunError(201); end; { $EndIf} end; function tPeakCenterIntermediateData.FindNearest(aSignal:tSignalValue):integer; var dSmin, dS:double; i:cardinal; begin if Count>0 then begin Result:=0; dSmin:=Abs(aSignal-prSignals[0].Signal.Signal); for i:=1 to Pred(Count) do begin dS:=Abs(aSignal-prSignals[i].Signal.Signal); if dSmin>dS then begin Result:=i; dSmin:=dS; end; end; end else begin Result:=-1; end; end; procedure tPeakCenterIntermediateData.Clear; begin prUsedCount:=0; end; END.