PROGRAM READ_MSD-DATA;
{====================================================================
This Delphi unit contains routines to access and manipulate data
from Hewlett-Packard MSD Data Files. Derived from published
Hewlett-Packard documentation. © Quadtech Associates, 1997.
=====================================================================}
unit Hpunit3;
interface
Uses StLArr,SysUtils, Dialogs; //uses TurboPower SysTools StLArr
Type
Byte = 0 .. 255;
String80 = String[80]; {for filenames}
gTicType
Int_File_Type = File of SmallInt;
Header_Info_Type = Record
File_num_Str : String[3];
File_String : String[19];
Data_name : String[61];
Misc_info : String[61];
Operator : String[29];
Date_time : String[29];
Inst_model : String[9];
Inlet : String[9];
Method_File : String[19];
File_type : LongInt;
Seq_index : SmallInt;
Als_bottle : SmallInt;
Replicate : SmallInt;
Dir_ent_type : SmallInt;
Dir_Offset : LongInt;
Data_Offset : LongInt;
Run_Tbl_Offset : LongInt;
Norm_Offset : LongInt;
Extra_Records : SmallInt;
Num_Records : LongInt;
Start_rTime : LongInt;
End_rTime : LongInt;
Max_Signal : LongInt;
Min_Signal : LongInt;
End;
File_Header_Type = Record
Info : Header_Info_Type;
Filler: Packed Array [1..(512 - SizeOf (Header_Info_Type))]
of Byte;
End;
Spec_Rec_Type = Record
NumWds : SmallInt;
RetTime : Longint;
WdsLess3 : SmallInt;
DataType : SmallInt;
Status : SmallInt;
NumPks : SmallInt;
BasePk : Word;
BaseAb : SmallInt;
End;
Mass_Abund_Type = Packed Record
Mass : Word;
Abund : Longint;
End;
Function SwapLong (Incoming:LongInt):LongInt;
Function SwapDble (Incoming:Double):Double;
Function Unpack (Incoming:Word):LongInt;
Procedure Read_Data_Header (File_Name : String;
Var Data_Header : File_Header_Type);
Procedure Get_Tic (File_Name:String; DirOff:LongInt;
NumRecs:LongInt);
Procedure Get_Spec_Head (File_Name:String; DirOff:LongInt;
Var theRTime:LongInt;
Var SpecHead:Spec_Rec_Type;
Var TotAb:LongInt);
Procedure Get_Spec_Data (File_Name:String; DirOff:LongInt;
SpecRecs:SmallInt;Caller:SmallInt);
implementation
{ ===================== Variable Declarations ==================== }
Var
Header : File_Header_Type;
SwapInt : LongInt;
TheDir_Offset : LongInt;
Data_File : Int_File_Type;
File_Name : String;
Output_File_Name : String;
NumRecs, DirOff: LongInt;
SpecRecs : SmallInt;
ThisArray: array [1..3] of LongInt;
gTicType:TStLArray;
MA,MD,ME:Array[1..1000] of Mass_Abund_Type;
{ ========================== PROCEDURES ============================ }
Function SwapLong (Incoming:LongInt):LongInt;
{ This routine swaps type LongInt variables. These variables
are all stored in reverse byte order.
Last Edited: Dec 2, 1995}
type
AType = Array[1..4] of Byte;
var
i,j:Integer;
A1,A2:AType;
L2: Longint;
begin
A1:=AType(Incoming);
j:=4;
for i:=1 to 4 do
begin
A2[j]:=A1[i];
j:=j-1;
end;
L2 := LongInt(A2);
SwapLong := L2;
end;
{===================================================================}
Function SwapDble (Incoming:Double):Double;
{ This routine swaps type Double variables.
Last Edited: Dec 2, 1995.}
type
AType = array[1..8] of Byte;
var
i,j:Integer;
A1,A2:AType;
begin
A1:=AType(Incoming);
j:=8;
for i:=1 to 8 do
begin
A2[j]:=A1[i];
j:=j-1;
end;
SwapDble:=Double(A2);
end;
{===================================================================}
Function Unpack (Incoming:Word):LongInt;
{ This routine is used to unpack HP Packed-Abundance values. The
HP Packed-Abundance value is in a 16-bit (unsigned) integer such
that the two high-order bits are the scale and the remaining 14
bits are the mantissa. Then abundance = mantissa * (8 ^ scale).
Last edited on June 7, 1997}
var
J : Integer;
begin
J:=HI(Incoming) DIV 64;
Case J of
0: J:=1;
1: J:=8;
2: J:=64;
3: J:=512;
end;
Unpack := (Longint(HI(Incoming) AND 63)*256 + LO(Incoming))*J;
end;
{===================================================================}
Procedure Read_Data_Header (File_Name : String;
Var Data_Header : File_Header_Type);
{ This routine is used to assign file labels and read & store the
Data Header information from the HP data file.
Last edited December 2, 1995 }
var
Header_File : file of File_Header_Type;
begin
try
Assign (Header_File, File_Name);
Reset (Header_File);
Read (Header_File, Data_Header);
Close (Header_File)
except
MessageDlg('Error in accessing file.',
mtInformation,[mbOk], 0);
Close(Header_File);
exit;
end;
end;
{===================================================================}
Procedure Get_Tic (File_Name:String; DirOff:LongInt;
NumRecs:LongInt);
{ Procedure to obtain the Tic data from the file. These data are
extracted from the directory records which also provides offset
information for individual spectra. There are 3 longints per record
in the following order: (1)file offset; (2)RT;(3) Total abundance.
Last edited Dec 6, 1995 }
var
Tic_File: file of SmallInt;
TheEntry: array [1..2] of SmallInt;
I, J, k:SmallInt;
begin
Assign (Tic_File, File_Name);
Reset (Tic_File);
Seek (Tic_File, DirOff - 1);
If (Assigned(gTicType)) then gTicType.Free;
gTicType := TStLArray.Create(NumRecs+1,SizeOf(ThisArray));
gTicType.ElementsStorable := True;
k:=0;
for I:=1 to NumRecs do
begin
for J:= 1 to 3 do
begin
Read (Tic_File, TheEntry[1]);
Read (Tic_File, TheEntry[2]);
ThisArray[J]:= SwapLong(LongInt(TheEntry));
end;
gTicType.Put(i,ThisArray);
k:=k+1;
if k= 10 then
begin
k:=0;
Form1.Label14.Caption:=IntToStr(I);
Form1.Label14.Refresh;
end;
end;
Close(Tic_File);
end;
{===================================================================}
Procedure Get_Spec_Head (File_Name:String; DirOff:LongInt;
Var theRTime:LongInt;
Var SpecHead:Spec_Rec_Type;
Var TotAb:LongInt);
{ Procedure to recover a selected spectrums Header information.
This is then used by the Get_Spec_Data procedure below.
Last edited December 23, 1995 }
var
Spec_File: file of SmallInt;
RTime: array [1..2] of SmallInt;
tStr:String;
RT:Single;
begin
Assign (Spec_File, File_Name);
Reset (Spec_File);
Seek (Spec_File, DirOff - 1);
Read (Spec_File, SpecHead.NumWds);
SpecHead.NumWds := Swap(SpecHead.NumWds);
Read (Spec_File, RTime[1]);
Read (Spec_File, RTime[2]);
theRTime := SwapLong(Longint(RTime));
Read (Spec_File, SpecHead.WdsLess3);
SpecHead.WdsLess3 := Swap(SpecHead.WdsLess3);
Read (Spec_File, SpecHead.DataType);
SpecHead.DataType := Swap(SpecHead.DataType);
Read (Spec_File, SpecHead.Status);
SpecHead.Status := Swap(SpecHead.Status);
Read (Spec_File, SpecHead.NumPks);
SpecHead.NumPks := Swap(SpecHead.NumPks);
SpecRecs := SpecHead.NumPks;
Read (Spec_File, SmallInt(SpecHead.BasePk));
SpecHead.BasePk := Swap(SpecHead.BasePk);
Read (Spec_File, SpecHead.BaseAb);
TotAb:=Unpack(Swap(SpecHead.BaseAb));
Close (Spec_File);
RT:=theRTime/60000;
end;
{===================================================================}
Procedure Get_Spec_Data (File_Name:String; DirOff:LongInt;
SpecRecs:SmallInt; Caller:SmallInt);
{ Procedure to recover the mass & abundance information for a
spectrum and store it in a typed-array (MA). NOTE: the array
dimensions here are only temporary. This should be changed to
dynamically alocate memory for the array on the heap using size
information from the Get_Spec_Head call.
Last edited on June 7, 1997 }
var
Spec_File: file of Word;
I, Incoming : Word;
begin
Assign (Spec_File, File_Name);
Reset (Spec_File);
Seek (Spec_File, (DirOff-1) + SizeOf(Spec_Rec_Type) DIV 2);
If Caller=0 then {standard procs (Not MDA)}
begin
MA[0].Abund:=0;
for I := 1 to SpecRecs do
begin
Read (Spec_File, Incoming);
MA[I].Mass := Swap(Incoming);
Read (Spec_File, Incoming);
MA[I].Abund := Unpack(Swap(Incoming));
end;
end
else If Caller=1 then {MDA Call}
for I := 1 to SpecRecs do
begin
Read (Spec_File, Incoming);
MD[I].Mass := Swap(Incoming);
Read (Spec_File, Incoming);
MD[I].Abund := Unpack(Swap(Incoming));
end
else {EXTion call}
for i:=1 to SpecRecs Do
begin
Read (Spec_File, Incoming);
ME[I].Mass := Swap(Incoming);
Read (Spec_File, Incoming);
ME[I].Abund := Unpack(Swap(Incoming));
end;
Close (Spec_File);
end;
PROGRAM SCIEXAPI-IIIREAD;
{The purpose of this program is to read original Sciex API III }
{quantitation data files (zipd)collected on the Macintosh system }
{(with Motorola CPU). Compiled in ThinkPASCAL for the Macintosh }
{on April 23, 1993. © Quadtech Associates, 1993. }
CONST
BASE = 128;
KEEP_ON = 1;
QUIT = 2;
VAR
gEndit, gIsOpen: boolean;
gResFile, gHor, gVer, gCnt, gSrcFile: Integer;
gNMass, gNScans: Integer;
gtheWindow: WindowPtr;
gReply: SFReply;
gRect: Rect;
gS: str255;
{----------------------- PROCEDURES ----------------------- }
PROCEDURE Windowlnit;
BEGIN
gtheWindow := GetNewWindow(Base, NIL, WindowPtr(-1));
ShowWindow(gtheWindow);
SetPort(gtheWindow);
InitCursor;
gEndit:=false;
END;
{This procedure calls a filename and stores the filename and volume }
{reference numbers in a global record accessed as gReply.fName and }
{gReply.vRefNum, respectively. The typelist is set to ZIPD to access}
{only ZIPD Sciex Data File types, and the numtypes is set to 1 }
{---------------------------------------------------------}
PROCEDURE GetFileName (VAR replyPtr: SFReply);
VAR
Apoint: Point;
typelist: SFTypeList;
numtypes: integer;
BEGIN
Apoint.h := 100;
Apoint.v := 100;
typelist[0] := 'ZIPD';
numtypes := 1;
SFGetFile(Apoint, ", NIL, numtypes, typelist, NIL, replyPtr);
END;
{---------------------------------------------------------}
PROCEDURE DoError (msg: STRING; theSrc: Integer);
VAR
err: OSerr;
dummy: longint;
BEGIN
Moveto(150, 350);
WriteDraw('ERROR: ', msg);
CloseResFile(gResFile);
{dangerous to FSClose a closed file so test if open & if so Close}
IF gIsOpen THEN
dummy := FSClose(theSrc);
err := NoteAlert(129, NIL);
Halt;
END;
{---------------------------------------------------------}
PROCEDURE MoveIt (VAR VSet: Integer; VAR HSet: Integer;
VI, HI:Integer);
BEGIN
VSet := VSet + VI;
HSet := HSet + HI;
Moveto(HSet, VSet);
END;
{Procedure to access mass intensity data}
{---------------------------------------------------------}
PROCEDURE GetData;
TYPE
RealPtr = ^Real;
VAR
DataPtr: RealPtr;
Offset, Buff: Longint;
i, j: Integer;
A: ARRAY[1..8] OF Real;
err: OSerr;
watch: cursHandle;
BEGIN
if NOT gIsOpen then
BEGIN
gEndIt;
Exit;
END;
watch := GetCursor(4);
SetCursor(watch^^);
{set Buffer size and data offset based on # of masses}
Buff := Sizeof(Real);
Offset := Sizeof(Real) * gNMass;
Ptr(DataPtr) := NewPtr(Sizeof(Real));
{Process each scan in file}
FOR i:= 1 TO gNScans DO
BEGIN
FOR j:= 1 TO gNMass DO
{read each MRM mass pair into DataPtr and copy to array}
BEGIN
err := FSRead(gSrcFile, Buff, Ptr(DataPtr));
A[j] := DataPtr^;
{---------------------------------------}
{A[j] is an element in an array of Reals}
{ process data here - print,store, etc }
{---------------------------------------}
END;
END;
END;
{Main routine here. Declares three Record types for the 4 resources }
{accessed: GRUP, HEAD, and STAT (TEXT doesn't need a Record structure}
{Additional types are declared for STATArrayType(a 23 element array }
{of StatType)and a ScanIndex Type which is declared but not used in }
{this program. The Resources are accessed sequentially and a Handle }
{is assigned which is then doubly dereferenced to access the records.}
{After printing the relevant data in each section, the Handle is }
{released and the next resource is accessed. }
{---------------------------------------------------------}
PROCEDURE DoFile;
TYPE
GroupType = RECORD
StartTime: Longint;
StepSize: Real;
ScanType: Integer;
FirstScan: Integer;
NumScans: Integer;
NumMasses:Integer;
MaxTicScan: Integer;
TicMax: Real;
END;
HeadType = RECORD
Name: PACKED ARRAY[1..20] OF Char;
OrigiD: PACKED ARRAY[1..6] OF Char;
Day: Integer;
Month: Integer;
Year: Integer;
totScans: Integer;
beginTime: Longint;
lastTime: Longint;
END;
ScanIndex = RECORD
offset: Longint;
startTime: Real;
END;
StatType = RECORD
ShortName: PACKED ARRAY[1..6] OF Char;
LongName: PACKED ARRAY[1..32] OF Char;
Flag: Integer;
Value: Real;
END;
StatArrayType = ARRAY[1..23] OF StatType;
DataArray = ARRAY[1..100] OF Real;
VAR
i,j,z,err,srcFile,dummy,ScanT:Integer;
VSet, HSet, VI, HI, theFlag: Integer;
offset: Longint;
Step, SSD, ScanRate, theValue: Real;
fGroup: GroupType;
fHead: HeadType;
fStat: StatArrayType;
fScan: Scanindex;
theResult, DType: integer;
S, S1: STRING;
theString: STRING[6];
theDate, theTime: Str255;
UseIt: StatType;
testC: PACKED ARRAY[1 ..4] OF Char;
theS: ARRAY[1..8] OF STRING;
bufSize, headerSize, runTime: longint;
ResH, docText, IHandle: Handle;
fGroupHandle, fHeadHandle, fStatHandle, fScanHandle: Handle;
fGroupPtr: ^GroupType;
fHeadPtr: ^HeadType;
fStatPtr: ^StatArrayType;
fScanPtr: ^Scan Index;
DataPtr: ^DataArray;
MI: DataArray;
IRect, r1: Rect;
BEGIN
{Call GetFileName and store the selected file. The filename and }
{volume reference number are displayed. Then Open the file and return}
{a file handle in srcFile. Error checking is through the returned }
{value. The current volume must be set before accessing the resource }
{fork (only the data fork has been opened at this point). Then check }
{size of the file with the GetEOF function and store the result in }
{bufSize of type longint. Open the resource fork with a file handle }
{returned in gResFile and process selected resources. }
GetFileName(gReply);
IF gReply.good <> True THEN
Halt;
s := concat('Filename: ', gReply.fName);
gHor := 20;
gVer := 20;
moveto(gHor, gVer);
SetRect(r1, 5, 5, 600, 500);
EraseRect(r1);
TextMode(srcCopy);
WriteDraw(s);
gVer := gVer + 20;
moveto(gHor, gVer);
I:= (FSOpen(gReply.fName, gReply.vRefNum, gsrcFile));
IF I <> 0 THEN
BEGIN
IF i = -43 THEN
Halt
ELSE
BEGIN
si := concat('Cannot open file. Error Code: ', Stringof(i));
DoError(sl, gsrcFile);
END;
END;
{File was opened without error - first set the File Open variable}
gIsOpen := True;
err := SetVol(NIL, gReply.vRefNum);
IF err <> 0 THEN
BEGIN
s := concat(' in call to SetVol. Error number was ',
Stringof(err));
DoError(s, gsrcFile);
END;
{SetVol Occured without error}
err := GetEOF(gsrcFile, bufSize);
IF err = 0 THEN
BEGIN
si := Stringof(bufSize);
s := concat('Data Fork Size: ', si, bytes');
WriteDraw(s);
END
ELSE
BEGIN
s := concat(' in call to GetEOF. Error number was ',
Stringof(err));
DoError(s, gsrcFile);
END;
{GetEOF occured without error}
gResFile := OpenResFile(gReply.fName);
IF gResFile = -1 THEN
BEGIN
s := ('in call to OpenResFile.'); DoError(s, gsrcFile);
END;
ResH := GetResource('TEXT', 1001);
{Note: in ZIPD files 1000 is generic; the others refer to periods - }
{e.g. 1001,1002, etc. This program accesses only one period, 1001 }
IF ResH <> NIL THEN
BEGIN
SetRect(gRect, 10, 60, 300, 400);
{Need to lock this rascal first}
HLock(ResH);
TextBox(Ptr(ResH^), GetHandleSize(ResH), gRect, 0);
HUnlock(ResH);
ReleaseResource(ResH);
END
ELSE
DoError(' in Accessing TEXT Resource', gsrcFile);
ResH := GetResource('GRUP', 1001);
IF ResH <> NIL THEN
BEGIN
HLock(ResH);
{Coerce the fGroupPtr to a Pointer type and assign to}
{dereferenced ResH Handle}
Ptr(fGroupPtr) := ResH^;
{assign pointer to fGroup}
fGroup := GroupType(fGroupPtr^);
{can now access fields in the fGroup Record}
Step := (fGroup.StepSize);
ScanT := fGroup.ScanType;
CASE fGroup.ScanType OF
17: theS[2] := 'Q1 MI';
18: theS[2] := 'Q3 MI';
19: theS[2] := 'MRM';
OTHERWISE
{must be a spectrum scantype so can't use MacQuan}
theS[2] := 'Not Quantifiable';
END;
IF fGroup.ScanType = 19 THEN
{MRM. So correct for Parent-Daughter pair in Numlons count}
gNMass := fGroup.NumMasses DIV 2
ELSE
gNMass := fGroup.NumMasses;
gNScans := fGroup.NumScans;
HUnlock(ResH);
ReleaseResource(ResH);
Vset := 60;
HSet := 300;
VI := 16;
HI := 0;
MoveIt(VSet, HSet, VI, HI);
WriteDraw('Step Size: ', Step : 4 : 3);
MoveIt(VSet, HSet, VI, HI);
WriteDraw('Type of Analysis: ',theS[2]);
MoveIt(VSet, HSet, VI, HI);
WriteDraw('Number of Ions Monitored: ', gNMass : 2);
MoveIt(VSet, HSet, VI, HI);
WriteDraw('Total Scans: ', gNScans);
gVer := VSet;
END
ELSE
DoError(' in Accessing GROUP Resource', gsrcFile);
ResH := GetResource('HEAD', 1000);
IF ResH <> NIL THEN
BEGIN
HLock(ResH);
{same as above}
Ptr(fHeadPtr) := ResH^;
fHead := Headtype(fHeadPtr^);
SSD := fHead.lastTime / fHead.totScans;
ScanRate := fHead.totScans / fHead.lastTime;
runTime := fHead.lastTime;
IUDateString(fHead.beginTime, longdate, theDate);
IUTimeString(fHead.beginTime, False, theTime);
HUnlock(ResH);
ReleaseResource(ResH);
Vset := 0;
HSet := 300;
VI := 20;
HI := 0;
MoveIt(VSet, HSet, VI, HI);
WriteDraw('Date of Analysis: the Date);
MoveIt(VSet, HSet, VI, HI);
WriteDraw('Time of Analysis: theTime);
VSet := gVer;
MoveIt(VSet, HSet, VI, HI);
WriteDraw('Run Time (seconds): ', RunTime : 5);
VI := 16;
MoveIt(VSet, HSet, VI, HI);
WriteDraw('Scans per second: ScanRate : 5 : 1);
MoveIt(VSet, HSet, VI, HI);
WriteDraw('Single scan duration: ', SSD : 5 : 2, ' seconds');
MoveIt(VSet, HSet, VI, HI);
END
ELSE
DoError(' in Accessing HEAD Resource', gsrcFile);
ResH := GetResource('STAT', 1000);
{Note - only one STAT (State File) Resource -- so 1000}
IF ResH <> NIL THEN
BEGIN
HLock(ResH);
Ptr(fStatPtr) := ResH^;
fStat := StatArrayType(fStatPtr^);
HSet := 260;
gVer := Vset;
FOR i:=1 TO 8 DO
BEGIN
UseIt := fStat[i];
MoveIt(VSet, HSet, VI, HI);
CASE i OF
2, 7, 8:
WriteDraw(UseIt.shortName : 3, ' ', UseIt.Value : 8 : 2);
OTHERWISE
WriteDraw(UseIt.shortName : 2, ' ', UseIt.Value : 8 : 2);
END;
END;
HSet := 370;
VSet := gVer;
FOR i:= 9 TO 16 DO
BEGIN
UseIt := fStat[i];
MoveIt(VSet, HSet, VI, HI);
CASE i OF
13, 14:
WriteDraw(UseIt.shortName :3, ' ',UseIt.Value :8 :2);
OTHERWISE
WriteDraw(UseIt.shortName :2, ' ',UseIt.Value :8 :2);
END;
END;
HSet := 480;
VSet := gVer;
FOR i:= 17 TO 23 DO
BEGIN
UseIt := fStat[i];
MoveIt(VSet, HSet, VI, HI);
CASE i OF
19:
{This is the CC which is entered as a 4 byte Packed Array of}
{Char - however, reading it here as a Real. So 'calculate' the}
{true result based on the float value}
BEGIN
IF UseIt.Value > 2e-09 THEN
UseIt.Value := 10
ELSE
UseIt.Value := 1;
WriteDraw(UseIt.shortName : 2, ' ', UseIt.Value
END;
20:
CASE ScanT OF
19, 22, 23, 24:
{This is MRM, Daughter, Parent or Constant Neutral}
{'cheating' again here to show the 'proper' output}
WriteDraw(UseIt.shortName : 2, ' Ar');
OTHERWISE
WriteDraw(UseIt.shortName : 2, ' OFF');
END; {Case of ScanT}
22, 23:
WriteDraw(UseIt.shortName : 3, ' ', UseIt.Value : 8 : 2);
OTHERWISE
WriteDraw(UseIt.shortName : 2, ' ', UseIt.Value : 8 : 2);
END;
END;
HUnlock(ResH);
ReleaseResource(ResH);
END
ELSE
DoError(' in Accessing STAT Resource', gsrcFile);
CloseResFile(gResFile);
GetData;
{close and end program}
IF gIsOpen THEN
BEGIN
gIsOpen := False;
err := FSClose(gsrcFile);
gEndIt;
END;
END;
{---------------------------------------------------------}
PROCEDURE GetEvent;
BEGIN
WHILE NOT gEndit DO
BEGIN
DoFile;
END;
END;
{==========================MAIN===========================}
BEGIN
Windowinit;
GetEvent;
END.
|