/
REC.PAS
2935 lines (2431 loc) · 105 KB
/
REC.PAS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
unit Rec;
{ ==============================================================================
WinWCP - Analogue Recording module (c) J. Dempster 1996, All Rights Reserved.
8/9/97 ... Event detector Pretrigger % can now be set by user V1.7b
31/5/98 ... GetInterfaceInfo put into Create method to force initialisation
of lab. interface before timed events start
DAC buffer now locked into memory to fix intermittent 0V glitches
between cycles of D/A waveforms
14/8/99 ... 32 bit version
4/9/99 ... Display grid added
11/12/99 ... TimeStart changed Settings.TimeRecordingStarted
20/12/01 ... D/A and digital default values now set using Main.WriteOutputPorts (V3.2.1)
17/3/02 .... Empty buffers now filled with pairs of positive/negative empty flags
to avoid hang ups when buffer was filled with A/D values equal
to empty flag.
Channel Units/Div scaling information added to display
15.7.02 .... Voltage pulse timing procedures moved to SESLABIO
13.2.03 .... Display time calibration now correctly updated when sweep duration
changes occur while in storage mode
16.4.03 .... Sweep now terminates correctly when A/D input = emptyflag
24.6.03 ... No. of display grid lines can be changed
6.9.03 .... Erase screen button enabled
18.03.04 .. A/D resolution now checked for compatibility with data in file
before form is opened
30.07.04 .. StartRecording & StopRecording states now replaced with
StartSweep & StopSweep procedures. Recording sweeps can now c0
be acquired at 10 sweeps/second
02.08.04 .. ADCDisplay buffer added to ensure trace remains displayed correctly
between sweeps
04.08.04 .. Channel calibration factor and units now updated automatically
from Amplifier module as well as gain. Switches between current & voltage
clamp mode can be handled (UpdateSettings)
04.05.06 .. Storage display mode now working again
25.07.06 Recording channel settings now stored in RecChannel (global.pas)
03.07.06 .. NumChannels, NumSamples, ADCVoltageIndex & RecordDuration now stored in settings (not RawFH)
EndOfSweepCount now used to detect completion of sweep
19.08.06 .. Storage display mode now working again
21.08.06 .. Record time stamp now derived from master Windows tick count
(rather than stimulus tick counter)
17.09.06 .. .ZoomOut now sets RecChannel YMin/YMax properties
29.10.07 .. Stimulus wavceforms now always recreated before each sweep
(Previously waveform was not recreated when pulse were repeated.
which caused duration errors with NIDAQ-MX and repeated waveforms)
28.11.07 .. Stimulus program selected when recording started now restored at stop of recording
Stimulus program name now included in status bar
08.02.08 .. Bug in event detector which produced incorrect sampling intervals
and number of samples / record fixed
05.08.08 .. Now possible to magnify display on X axis as well as Y
16.09.08 .. Leak waveform now re-created in leak mode to avoid different
leak pulse duration
11.02.09 .. Stimulus protocol folder can now be set by user.
13.05.09 .. Empty flags now replaced by last recorded samplewhen recording sweep
is prematurely terminated bu user
13.05.10 .. Channel display visibility setting now preserved in RecChannel[].InUse
22.07.10 .. Time of record acquisition (RH.Time) now set when record is saved
to provide consistent time for all trigger modes. Fixes bug with
external trigger mode where first record had T=0 and 2nd record
record had incorrectly long time.
06.01.11 .. TritonPanelFrm opened automatically when record form is displayed
when a Tecella amplifier is in use.
09.02.11 .. Second pair of cursors added for on-line analysis
27.05.11 .. Recording mode now selected from drop-down menu
No. records and no. samples/record now set in record window (not setup)
20.07.11 .. Protocol execution list added
17.08.11 .. Protocol file names now local to selected protocol folder so linked
protocols continue to work when folders renamed
12.09.11 .. Stimulator.Prot.NumAOChannels now used to set number of AO channels in use
All 4 AO channels of CED Power 1401 now work. DO0 and DO1 also checked and OK
14.09.11 .. Display Y ranges now updated on exit
20.09.11 .. Main.SESLabIO.ADCVoltageRangeIndex now set from Settings.ADCVoltageRangeIndex
every time .StartADC is called
13.03.12 .. VCLAMP/ICLAMP mode of amplifiers now indicated/set in recording window
16.04.12 .. Stimulus output channels now limited to number of available DAC channels
Global display channel vertical limits and enabled/disabled settings
now updated whenever changed.
Changes to recording parameters now disabled when recording in progress
Record display window (if open) now updated with number of records as they are collected
15.06.12 .. Prot.RecordDuration no longer updated after Stimulator.SetADCDAC
to avoid changes record duration during protocol due to adjustment
of A/D update interval by interface board
30.08.12 .. "Incl. stim protocol in file name" option added. Creates new file every time Record is started
or a protocol changes and appends protocol name and sequence number to data file name.
New files forced by changes in recording settings are now indicated .XX.wcp sequence number
rather than appended pluses.
31.08.12 .. Empty record no longer written to file when recording sweeps are aborted by pressing Stop button
29.07.13 .. Include Stim Protocol in File Name check box setting now stored in INI file
file name with appended stimulus protocol updated to handle stimuli with ".." in names
Recording window can now be opened without an open data file.
Correct stimulus protocol names now appended to file name for linked protocols
Record acquisition time now acquired at start of recording sweep (with 50ms accuracy)
26.08.13 .. CheckNewDataFileNeeded() now permits 1E-4 difference in channel gains without requiring
a new file to be opened to allow for loss of precision when data written/read from fiel header
06.09.13 .. EPC9PanelFrm opened if EPC9/10 patch clamp in use.
23.09.13 .. Recording start time now encoded in en-GB format
25.06.14 .. From Record zero level can now be set in recording window
15.10.14 .. Stimulus protocol status now updated during sweep and shows time till next stimulus
23.03.15 .. Incrementing of dynamic clamp Gmax added
10.04.15 .. Zero level now correctly set to fixed level rather than From Record
15.06.15 .. No. of samples/record now adjusted to maintain constant record duration
when sampling interval changed.
14.08.15 .. LeakSum buffer no longer allocated and deallocated during leak subtraction records
fixing spurious values which appeared in first samples of leak subtracted records
24.08.15 .. cbdetectEvents menu now set from SESLabIO, not ChannelNames
ADCDisplay buffer now only reallocated if size has changed, allowing recorded traces
from previous linked protocols to remain on display.
05.11.15 .. Marker Add button removed. Marker text now added automatically to record
05.11.15 HekaEPC9USB interface added
8.11.15 .. TMeasureFrmCursors.Base removed since use was redundant.
16.03.16 .. scDisplay.recordnumber incremented after SaveSweep to make storage mode work correctly
11.07.16 .. UpdateChannelSettings() removed from SaveSweep() to speed it up allow 5 HZ repeat
rates. Now called by brecord.click and when a protocol loaded
10.08.16 .. TImer interval reduced to 30ms to make reading of data from NIDAQ boards faster
30.08.16 .. RH.ADCVoltageRange now updated in SaveSweep() to account for changes
in amplifier gain (previously done by UpdateChannelSettings() which
was removed from SaveSweep() to speed up saving to file.
12.01.17 .. .VerticalCursors() now single type and converted to integer by round()
27.02.17 .. UpdateDisplay now called during .bOpenOLAWindowClick to ensure
window duration is valid when on-line window opened.
01.03.17 .. Display time scaling no longer changes between and within sweeps.
17.03.17 .. Main.SESLabIO.StimulusStartFlag now cleared in StartSweep() in
all recording modes other than rmProtocol to avoid generation of
unwanted stimulus from previous protocol in first sweep following
a stimulus protocol sweep
==============================================================================}
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls, Shared, FileIO,
maths, math, ValEdit, ScopeDisplay, Global, SESLabIO, mmsystem, ValidatedEdit,
ComCtrls, strutils, UITypes ;
const
rmProtocol = 0 ;
rmFreeRun = 1 ;
rmExtTrig = 2 ;
rmDetect = 3 ;
DCL_None = 0 ;
DCL_Initialise = 1 ;
DCL_NextRecord = 2 ;
DCL_NextProtocol = 3 ;
type
TDet = record
StartAt : Integer ;
EndAt : Integer ;
PreTriggerPoints : Integer ;
Baseline : Integer ;
Threshold : Integer ;
Polarity : Integer ;
Pointer : Integer ;
EndOfBuf : Integer ;
Chan : Integer ;
LastChan : Integer ;
ChanSelected : Integer ;
EventDetected : boolean ;
TimeDetected : single ;
BufferCycles : Integer ;
end ;
TStopwatch = record
ElapsedTime : TDateTime ;
Hour : Word ;
Minute : Word ;
Second : Word ;
MSec : Word ;
TickStart : single ;
OldSeconds : single ;
end ;
TState = ( Idle, SweepInProgress ) ;
TRecordFrm = class(TForm)
ControlGrp: TGroupBox;
bRecord: TButton;
bStop: TButton;
ckSaveRecords: TCheckBox;
Timer: TTimer;
DisplayGrp: TGroupBox;
edStatus: TEdit;
edIdent: TEdit;
Label4: TLabel;
TimerGrp: TGroupBox;
edTimeOfDay: TEdit;
bResetTimer: TButton;
bErase: TButton;
OnLineAnalysisGrp: TGroupBox;
ckFixedZeroLevels: TCheckBox;
bOpenOLAWindow: TButton;
bCloseOLAWindow: TButton;
edMarker: TEdit;
Label6: TLabel;
Label8: TLabel;
Bevel4: TBevel;
RecordModeGrp: TGroupBox;
cbRecordingMode: TComboBox;
RecordParametersPanel: TPanel;
edNumRecords: TValidatedEdit;
Label3: TLabel;
Label10: TLabel;
edNumSamples: TValidatedEdit;
Label11: TLabel;
edNumChannels: TValidatedEdit;
Label12: TLabel;
edSamplingInterval: TValidatedEdit;
Label13: TLabel;
edRecordDuration: TValidatedEdit;
panAmplifierGain0: TPanel;
lbAmplifier: TLabel;
edAmplifierGain0: TValidatedEdit;
panAmplifierGain1: TPanel;
Label14: TLabel;
edAmplifierGain1: TValidatedEdit;
panAmplifierGain2: TPanel;
Label15: TLabel;
edAmplifierGain2: TValidatedEdit;
panAmplifierGain3: TPanel;
Label16: TLabel;
edAmplifierGain3: TValidatedEdit;
panProtocol: TPanel;
rbSingleProtocol: TRadioButton;
rbProtocolList: TRadioButton;
panProtocolList: TPanel;
Label17: TLabel;
cbProtocolList: TComboBox;
meProtocolList: TMemo;
bAddProtocolToList: TButton;
bNewProtocolList: TButton;
edNewProtocolListName: TEdit;
bDeleteProtocolList: TButton;
panSingleProtocol: TPanel;
Label18: TLabel;
cbPulseProgram: TComboBox;
bSetStimFolder: TButton;
ExtTriggerGrp: TGroupBox;
rbExtTriggerHigh: TRadioButton;
rbExttriggerLow: TRadioButton;
DetectGrp: TGroupBox;
Label1: TLabel;
Label2: TLabel;
Label5: TLabel;
cbDetectChannel: TComboBox;
edDetectionThreshold: TValidatedEdit;
edPreTrigger: TValidatedEdit;
OpenDialog: TOpenDialog;
scDisplay: TScopeDisplay;
rbVCLAMP0: TRadioButton;
rbICLAMP0: TRadioButton;
rbVCLAMP1: TRadioButton;
RBICLAMP1: TRadioButton;
rbVCLAMP2: TRadioButton;
RBICLAMP2: TRadioButton;
rbVCLAMP3: TRadioButton;
rbICLAMP3: TRadioButton;
ckNewFileOnRecord: TCheckBox;
procedure TimerTimer(Sender: TObject);
procedure bRecordClick(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure bStopClick(Sender: TObject);
procedure cbPulseProgramChange(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure bResetTimerClick(Sender: TObject);
procedure EdDetectionThresholdKeyPress(Sender: TObject; var Key: Char);
procedure FormDeactivate(Sender: TObject);
procedure edIdentKeyPress(Sender: TObject; var Key: Char);
procedure FormShow(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure bEraseClick(Sender: TObject);
procedure edPreTriggerKeyPress(Sender: TObject; var Key: Char);
procedure cbDetectChannelChange(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure scDisplayCursorChange(Sender: TObject);
procedure rbExtTriggerHighClick(Sender: TObject);
procedure rbExttriggerLowClick(Sender: TObject);
procedure ckFixedZeroLevelsClick(Sender: TObject);
procedure edAmplifierGain0KeyPress(Sender: TObject; var Key: Char);
procedure FormActivate(Sender: TObject);
procedure bOpenOLAWindowClick(Sender: TObject);
procedure bCloseOLAWindowClick(Sender: TObject);
procedure bSetStimFolderClick(Sender: TObject);
procedure cbRecordingModeChange(Sender: TObject);
procedure edNumChannelsKeyPress(Sender: TObject; var Key: Char);
procedure edNumSamplesKeyPress(Sender: TObject; var Key: Char);
procedure edRecordDurationKeyPress(Sender: TObject; var Key: Char);
procedure edNumRecordsKeyPress(Sender: TObject; var Key: Char);
procedure edSamplingIntervalKeyPress(Sender: TObject; var Key: Char);
procedure rbSingleProtocolClick(Sender: TObject);
procedure rbProtocolListClick(Sender: TObject);
procedure cbProtocolListChange(Sender: TObject);
procedure bAddProtocolToListClick(Sender: TObject);
procedure bDeleteProtocolListClick(Sender: TObject);
procedure bNewProtocolListClick(Sender: TObject);
procedure rbVCLAMP0Click(Sender: TObject);
procedure rbICLAMP0Click(Sender: TObject);
procedure scDisplayMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormCreate(Sender: TObject);
procedure ckNewFileOnRecordClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
ADC : PSmallIntArray ; // A/D sample data buffer
DAC : PSmallIntArray ; // A/D waveform data buffer
DigBuf : PSmallIntArray ; // Digital port output array
ADCCircular : PSmallIntArray ; // A/D sample data buffer circular
ADCCopy : PSmallIntArray ;
ADCDetected : PSmallIntArray ;
ADCDisplay : PSmallIntArray ;
LeakSum : PIntArray ; // Summation buffer used to average LEAK records
Det : TDet ;
EndOfBuf : Integer ;
RH : TRecHeader ;
NumRecordsDone,EndAtRecord : Integer ;
NextBlock : Integer ;
DIGActive : Boolean ; { Digital port sub-system active }
NewSweep : Boolean ;
GroupNumber : Integer ; { Group of TEST and LEAK records produced by same
voltage program waveform }
OldStepCounter : Integer ; { Previous voltage program step number }
Stopwatch : TStopwatch ;
StatusCounter : Integer ;
EndOfSweep : boolean ;
EndOfSweepCount : Integer ;
AbortRecording : boolean ;
EraseScreen : boolean ;
NumBlocksDone : Integer ;
EmptyFlag : Integer ;
NewAmplifierGain : Boolean ;
C0Cursor : Integer ;
C1Cursor : Integer ;
C2Cursor : Integer ;
C3Cursor : Integer ;
C4Cursor : Integer ;
StartingPulseProgramIndex : Integer ; // Pulse program index at start of recording
ProtocolCount : Integer ; // Protocol execution list index counter
ProtocolFileName : String ; // Currently selected stimulus protocol file
DataFileIndex : Integer ;
DCLAMPAction : Integer ; // Dynamic clamp action required
procedure SaveSweep ;
procedure InitialiseEventDetector ;
procedure DetectEvent ;
procedure StartVoltageProgram ;
procedure RestoreIdleState ;
procedure UpdateStopwatch ;
procedure ShutDownLabInterface ;
procedure MainMenus( Enabled : Boolean ) ;
procedure UpdateChannelSettings ;
procedure UpdateAmplifierGain ;
procedure SetRecordingMode(iMode : Integer ) ;
function GetRecordingMode : Integer ;
function GetStimulusProtocol : String ;
procedure SetStimulusProtocol( Value : String ) ;
function GetRunning : Boolean ;
function GetRecording : Boolean ;
procedure UpdateNumSamples ;
procedure UpdateSamplingInterval ;
procedure UpdateAmplifierGains ;
procedure ShowAmplifierGains ;
function UpdateVProgramFileName( NewName : String ) : String ;
function CheckNewDataFileNeeded : Boolean ;
procedure CreateProtocolListList( var cbList : TComboBox ) ;
function LoadProtocolList : Boolean ;
function SaveProtocolList( FileName : String ) : Boolean ;
function LoadStimulusProtocol(
Initialise : Boolean // True = Initialise protocol counter
) : Boolean ;
procedure DisplayClampMode ;
public
{ Public declarations }
TimerBusy : boolean ;
State : TState ; // Current operational state of module
procedure SetStoreMode( StorageMode : Boolean ) ;
procedure ChangeDisplayGrid ;
procedure ZoomOutAll ;
procedure ZoomIn( ChanNum : Integer ) ;
procedure ZoomOut( ChanNum : Integer ) ;
procedure StartSweep ;
procedure StopSweep ;
procedure UpdateStimulusProtocolList ;
procedure UpdateDisplay( Clear : Boolean ) ;
procedure StartRecording ;
procedure StopRecording ;
property RecordingMode : Integer read GetRecordingMode write SetRecordingMode ;
property StimulusProtocol : String
read GetStimulusProtocol write SetStimulusProtocol ;
property Running : Boolean read GetRunning ;
property Recording : Boolean read GetRecording ;
end;
var
RecordFrm: TRecordFrm;
implementation
{$R *.DFM}
uses MDiform, AmpModule, StimModule , RecPlotUnit, Sealtest,
TritonPanelUnit, DirectorySelectUnit, Replay, Zero, DCLAMPUnit;
procedure TRecordFrm.FormShow(Sender: TObject);
{ ------------------------------------
Initialisation when form is created
----------------------------------- }
var
i : Integer ;
begin
// Set position of form
Left := 20 ;
Top := 20 ;
Height := Main.StatusBar.Top - Top - 10 ;
Width := Main.ClientWidth - Left - 20 ;
if RawFH.FileHandle >= 0 then Caption := 'Record ' + RawFH.FileName
else Caption := 'Record No File' ;
{ Make sure event timer is off }
Timer.Enabled := False ;
// Exit if no interface selected
if (Main.SESLabIO.LabInterfaceType = NoInterface12) or
(Main.SESLabIO.LabInterfaceType = NoInterface16) then begin
ShowMessage( 'No laboratory interface selected!' ) ;
Close ;
Exit ;
end ;
// Display Triton control panel if it is not open
case Main.SESLabIO.LabInterfaceType of
Triton : begin
if not Main.FormExists( 'TritonPanelFrm' ) then begin
Main.mnTriton.Enabled := True ;
Main.mnTriton.Click ;
end ;
end ;
HekaEPC9,HekaEPC10,HekaEPC10Plus,HekaEPC10USB,HekaEPC9USB : begin
if not Main.FormExists( 'EPC9PanelFrm' ) then begin
Main.mnEPC9Panel.Enabled := True ;
Main.mnEPC9Panel.Click ;
end ;
end ;
end;
{ Allocate memory buffers }
{ Get locations of A/D, D/A and digital I/O buffers from lab. interface }
Main.SESLabIO.GetADCBuffer( ADC ) ;
Main.SESLabIO.GetDACBuffer( DAC ) ;
Main.SESLabIO.GetDIGBuffer( DigBuf ) ;
EmptyFlag := Main.SESLabIO.ADCEmptyFlag ;
{ Check that the selected hardware/software is available }
if not Main.SESLabIO.LabInterfaceAvailable then begin
ShowMessage( 'Laboratory interface not available!' ) ;
Close ;
Exit ;
end ;
{ Set size of window }
ClientHeight := TimerGrp.Top + TimerGrp.Height + 5 ;
//ClientWidth := scDisplay.Width + 5 ;
edIdent.text := RawFH.IdentLine ;
RH.dt := Main.SESLabIO.ADCSamplingInterval ;
DigActive := False ;
EraseScreen := False ;
ckNewFileOnRecord.Checked := Settings.OpenNewFileOnRecord ;
{ Fill combo box with list of available command voltage programs }
UpdateStimulusProtocolList ;
// Recording mode selection box
cbRecordingMode.Clear ;
cbRecordingMode.Items.Add('Stimulus protocol') ;
cbRecordingMode.Items.Add('Free run') ;
cbRecordingMode.Items.Add('Ext. triggered') ;
cbRecordingMode.Items.Add('Detect events') ;
cbRecordingMode.ItemIndex := Settings.RecordingMode ;
SetRecordingMode( cbRecordingMode.ItemIndex ) ;
// Set external trigger polarity
Main.SESLabIO.ADCExternalTriggerActiveHigh := Settings.ExternalTriggerActiveHigh ;
rbExtTriggerHigh.Checked := Main.SESLabIO.ADCExternalTriggerActiveHigh ;
{ Set event detector }
edDetectionThreshold.Value := Settings.EventDetector.Threshold ;
edPreTrigger.Value := Settings.EventDetector.PreTrigger ;
OldStepCounter := -1 ;
{ Initialise stopwatch }
Stopwatch.TickStart := GetTickCount ;
Stopwatch.OldSeconds := 0. ;
ShowAmplifierGains ;
{ If a CED 1902 is in use, update its settings }
for i := 0 to MaxAmplifiers-1 do begin
if Amplifier.AmplifierType[i] = amCED1902 then Amplifier.SetCED1902 ;
end ;
edNumRecords.Value := Settings.NumRecordsRequired ;
edRecordDuration.Value := Settings.RecordDuration ;
edNumSamples.Value := Settings.NumSamples ;
edNumChannels.Value := Settings.NumChannels ;
UpdateSamplingInterval ;
edStatus.font.color := clBlack ;
TimerBusy := False ;
State := Idle ;
EndofSweep := False ;
StartingPulseProgramIndex := 0 ;
// Set recording mode to force update of signal display to current settings
SetRecordingMode( cbRecordingMode.ItemIndex ) ;
Timer.Enabled := True ;
end ;
procedure TRecordFrm.ShowAmplifierGains ;
// --------------------------------------
// Show gain values for amplifiers in use
// --------------------------------------
begin
if Amplifier.AmplifierType[0] = amNone then panAmplifierGain0.Visible := False
else panAmplifierGain0.Visible := True ;
if Amplifier.AmplifierType[1] = amNone then panAmplifierGain1.Visible := False
else panAmplifierGain1.Visible := True ;
if Amplifier.AmplifierType[2] = amNone then panAmplifierGain2.Visible := False
else panAmplifierGain2.Visible := True ;
if Amplifier.AmplifierType[3] = amNone then panAmplifierGain3.Visible := False
else panAmplifierGain3.Visible := True ;
if PanAmplifierGain3.Visible then begin
DisplayGrp.ClientHeight := PanAmplifierGain3.Top
+ PanAmplifierGain3.Height + 5 ;
end
else if PanAmplifierGain2.Visible then begin
DisplayGrp.ClientHeight := PanAmplifierGain2.Top
+ PanAmplifierGain2.Height + 5 ;
end
else if PanAmplifierGain1.Visible then begin
DisplayGrp.ClientHeight := PanAmplifierGain1.Top
+ PanAmplifierGain1.Height + 5 ;
end
else begin
DisplayGrp.ClientHeight := PanAmplifierGain0.Top
+ PanAmplifierGain0.Height + 5 ;
end ;
end ;
procedure TRecordFrm.UpdateStimulusProtocolList ;
// ----------------------------------
// Update list of stimulus protocols
// ----------------------------------
begin
{ Fill combo box with list of available command voltage programs }
Stimulator.CreateProtocolList( cbPulseProgram ) ;
if (Settings.VProgramFileName = '') or
ANSIContainsText(Settings.VProgramFileName,'\ .') then begin
cbPulseProgram.ItemIndex := 0 ;
end
else begin
cbPulseProgram.ItemIndex := cbPulseProgram.Items.IndexOf(
ExtractFileNameOnly(Settings.VProgramFileName))
end ;
// Update list of protocol lists
CreateProtocolListList( cbProtocolList ) ;
if (Settings.ProtocolListFileName = '') or
ANSIContainsText(Settings.ProtocolListFileName,'\ .') then begin
cbProtocolList.ItemIndex := 0 ;
meProtocolList.Clear ;
end
else begin
cbProtocolList.ItemIndex := cbProtocolList.Items.IndexOf(
ExtractFileNameOnly(Settings.ProtocolListFileName)) ;
LoadProtocolList ;
end ;
end ;
procedure TRecordFrm.UpdateDisplay(
Clear : Boolean // True = clear display
) ;
{ -----------------------------------------------
Re-initialise display when data file is changed
----------------------------------------------- }
var
i,ch : Integer ;
BufferChanged : Boolean ;
begin
if cbRecordingMode.ItemIndex = rmProtocol then begin
// Stimulus protocol recording mode
if (ProtocolFileName <> '') and
FileExists(ProtocolFileName) then begin
Stimulator.LoadProtocol(ProtocolFileName) ;
end ;
end
else begin
// All other recording modes
Main.SESLabIO.ADCNumChannels := Settings.NumChannels ;
Main.SESLabIO.ADCNumSamples := Settings.NumSamples ;
Main.SESLabIO.ADCSamplingInterval := Settings.RecordDuration / Settings.NumSamples ;
Settings.RecordDuration := Main.SESLabIO.ADCSamplingInterval*Main.SESLabIO.ADCNumSamples ;
end ;
// Determine of buffer size has changed
if ((scDisplay.NumChannels <> Main.SESLabIO.ADCNumChannels) or
(scDisplay.MaxPoints <> Main.SESLabIO.ADCNumSamples)) or
(ADCDisplay = Nil) then BufferChanged := True
else BufferChanged := False ;
// Set detect events channel selection menu
cbDetectChannel.Clear ;
for ch := 0 to Main.SESLabIO.ADCNumChannels-1 do
cbDetectChannel.items.Add(format('Ch.%d %s',[ch,Main.SESLabIO.ADCChannelName[ch]])) ;
cbDetectChannel.ItemIndex := IntLimitTo(Settings.EventDetector.Channel,
0,Main.SESLabIO.ADCNumChannels-1) ;
{ Set up the number of channels/points in the 'scope display }
scDisplay.NumChannels := Main.SESLabIO.ADCNumChannels ;
scDisplay.ChanZeroAvg := Main.SESLabIO.ADCChannelNumZero ;
scDisplay.MaxADCValue := Main.SESLabIO.ADCMaxValue ;
scDisplay.MinADCValue := Main.SESLabIO.ADCMinValue ;
scDisplay.DisplayGrid := Settings.DisplayGrid ;
scDisplay.MaxPoints := Main.SESLabIO.ADCNumSamples ;
scDisplay.xMin := 0 ;
scDisplay.xMax := Main.SESLabIO.ADCNumSamples - 1 ;
{Set channel information }
for ch := 0 to scDisplay.NumChannels-1 do begin
scDisplay.ChanOffsets[ch] := Main.SESLabIO.ADCChannelOffset[ch] ;
scDisplay.ChanUnits[ch] := Main.SESLabIO.ADCChannelUnits[ch] ;
scDisplay.ChanName[ch] := Main.SESLabIO.ADCChannelName[ch] ;
scDisplay.ChanScale[ch] := Main.SESLabIO.ADCChannelUnitsPerBit[ch] ;
scDisplay.yMin[ch] := Main.SESLabIO.ADCChannelYMin[ch] ;
scDisplay.yMax[ch] := Main.SESLabIO.ADCChannelYMax[ch] ;
scDisplay.ChanVisible[ch] := Main.SESLabIO.ADCChannelVisible[ch] ;
end ;
{ Create a set of zero level cursors }
scDisplay.ClearHorizontalCursors ;
for ch := 0 to scDisplay.NumChannels-1 do begin
scDisplay.AddHorizontalCursor(ch,Settings.Colors.Cursors,True,'z' ) ;
scDisplay.HorizontalCursors[ch] := Main.SESLabIO.ADCChannelZero[ch] ;
Channel[ch].ADCZeroAt := Main.SESLabIO.ADCChannelZeroAt[ch] ;
Channel[ch].ADCZero := Main.SESLabIO.ADCChannelZero[ch] ;
scDisplay.ChanZeroAt[ch] := Main.SESLabIO.ADCChannelZeroAt[ch] ;
end ;
scDisplay.TScale := Main.SESLabIO.ADCSamplingInterval*Settings.TScale ;
scDisplay.TUnits := Settings.TUnits ;
SetStoreMode(Main.mnStoreTraces.Checked) ;
{ Set A/D buffer to zero }
EndofBuf := (Main.SESLabIO.ADCNumSamples*Main.SESLabIO.ADCNumChannels) - 1;
for i := 0 to EndOfBuf do ADC^[i] := 0 ;
// Re-allocate buffer if size has changed
if BufferChanged or Clear then begin
if ADCDisplay <> Nil then FreeMem( ADCDisplay ) ;
GetMem( ADCDisplay, scDisplay.NumChannels*scDisplay.MaxPoints*2 ) ;
scDisplay.SetDataBuf( ADCDisplay ) ;
scDisplay.NumPoints := 0 ;
end ;
{ Make sure event detector channel is within valid limits }
cbDetectChannel.Clear ;
for ch := 0 to Main.SESLabIO.ADCNumChannels-1 do
cbDetectChannel.items.Add(format('Ch.%d %s',[ch,Main.SESLabIO.ADCChannelName[ch]])) ;
cbDetectChannel.ItemIndex := IntLimitTo(Settings.EventDetector.Channel,
0,Main.SESLabIO.ADCNumChannels-1) ;
end ;
function TRecordFrm.UpdateVProgramFileName( NewName : String ) : String ;
begin
Result := Settings.VProtDirectory + NewName + '.xml' ;
if ANSIContainsText(Result,'\.') or
ANSIContainsText(Result,'\ .') then Result := '' ;
end ;
procedure TRecordFrm.TimerTimer(Sender: TObject);
{ ---------------------
Timed Event scheduler
---------------------}
var
i,i0,i1 : integer ;
Done : Boolean ;
begin
if not TimerBusy then begin
TimerBusy := True ;
Case State of
{ **** Update display during recording sweep **** }
SweepInProgress : Begin
{ Get new A/D samples }
Main.SESLabIO.ADCBufferRefresh ;
{ Check event detection buffer for signal }
if cbRecordingMode.ItemIndex = rmDetect then DetectEvent ;
{ Determine how many samples have been acquired }
Done := False ;
if NextBlock > EndOfBuf then Done := True ;
While not Done do begin
if Odd(Nextblock) then begin
// End of multi-channel sample block is odd
if (ADC^[NextBlock] = -EmptyFlag) and
(ADC^[NextBlock-1] = EmptyFlag) then Done := True
else begin
i0 := NumBlocksDone*Main.SESLabIO.ADCNumChannels ;
i1 := i0 + Main.SESLabIO.ADCNumChannels - 1 ;
for i := i0 to i1 do ADCDisplay^[i] := ADC^[i] ;
Inc(NumBlocksDone) ;
NextBlock := NextBlock + Main.SESLabIO.ADCNumChannels ;
end ;
end
else begin
// End of multi-channel sample block is even
if (ADC^[NextBlock] = EmptyFlag) and
(ADC^[NextBlock-1] = -EmptyFlag) then Done := True
else begin
i0 := NumBlocksDone*Main.SESLabIO.ADCNumChannels ;
i1 := i0 + Main.SESLabIO.ADCNumChannels - 1 ;
for i := i0 to i1 do ADCDisplay^[i] := ADC^[i] ;
Inc(NumBlocksDone) ;
NextBlock := NextBlock + Main.SESLabIO.ADCNumChannels ;
end ;
end ;
// Exit loop if at end of buffer
if NextBlock > EndOfBuf then Done := True ;
end ;
// Determine if all samples have been acquired
if ADC[EndOfBuf] <> -EmptyFlag then Inc(EndOfSweepCount) ;
if ADC[EndOfBuf-1] <> EmptyFlag then Inc(EndOfSweepCount) ;
if EndOfSweepCount >= 2 then EndOfSweep := True ;
{ Erase old display now that new sweep has started }
if (NextBlock >= (2*Main.SESLabIO.ADCNumChannels)) and NewSweep then begin
// Get time of start of sweep
RH.Time := GetTickCount*mStoSecs ;
if RawFH.NumRecords <= 0 then begin
RawFH.RecordingStartTime := Main.DateToStr(Now) ;
RawFH.RecordingStartTimeSecs := RH.Time ;
end;
{ Erase display once sweep has started (if required) }
scDisplay.SetDataBuf( ADCDisplay ) ;
scDisplay.NumPoints := 0 ;
// scDisplay.TScale := Main.SESLabIO.ADCSamplingInterval*Settings.TScale ;
// 1.3.17 Removed because it was changing display scaling slightly between
// and during sweeps with some lab. interfaces
if (not scDisplay.StorageMode) or
(Abs(Main.SESLabIO.ADCSamplingInterval - rH.dt) > 1E-5) or
EraseScreen then scDisplay.Invalidate ;
EraseScreen := False ;
NewSweep := False ;
end ;
{ Display sample record on screen}
if NumBlocksDone > (2*Main.SESLabIO.ADCNumChannels) then
scDisplay.DisplayNewPoints( NumBlocksDone-1 ) ;
{ End of sweep processing }
if EndOfSweep then begin
// t0 := timegettime ;
{ Stop A/D conversion ... unless in detect event mode }
if cbRecordingMode.ItemIndex <> rmDetect then begin
Main.SESLabIO.ADCStop ;
end ;
{ Save sweep data to file }
SaveSweep ;
scDisplay.RecordNumber := scDisplay.RecordNumber + 1 ;
// If Dynamic clamp in use, inform it that a record has been collected
if Main.FormExists('DCLAMPFrm') then DCLAMPAction := DCL_NextRecord ;
{ If in repeated sweep modes request another sweep
(unless recording aborted by user) }
if Main.SESLabIO.DACActive then begin
Main.SESLabIO.DACStop ;
end ;
if AbortRecording then begin
StopSweep ;
end
else if cbRecordingMode.ItemIndex = rmProtocol then begin
{ Voltage pulse mode }
// Increment to next waveform
Stimulator.NextWaveform ;
if Stimulator.EndOfProtocol then begin
// If Dynamic clamp in use, update to next Gmax value
if Main.FormExists('DCLAMPFrm') then DCLAMPAction := DCL_NextProtocol ;
// Update channel scaling factors in case amplifier gain has changed
UpdateChannelSettings ;
{ If at the end of the current set of voltage pulses
for the current protocol either load and begin
the next protocol in a linked sequence or list OR
stop recording if no new protocol available }
if LoadStimulusProtocol(False) then begin
if CheckNewdataFileNeeded then StartSweep
else StopSweep ;
end
else StopSweep ;
end
else StartSweep ;
end
else begin
{ Other modes }
Inc(NumRecordsDone) ;
if (NumRecordsDone >= Settings.NumRecordsRequired) then
StopSweep
else StartSweep ; ;
end ;
end ;
End ;
Idle : begin
{ ** Procedures when recording is in idle mode ** }
if (edNumRecords.text = '')
and (cbRecordingMode.ItemIndex <> rmProtocol ) then
edNumRecords.text := IntToStr( Settings.NumRecordsRequired ) ;
// Update channel scaling factors in case amplifier gain has changed
UpdateChannelSettings ;
if not Main.FormExists( 'RecPlotFrm' ) and bCloseOLAWindow.Enabled then begin
bCloseOLAWindow.Click ;
end ;
end ;
end ;
case DCLAMPAction of
DCL_Initialise : begin
DCLAMPFrm.Initialise ;
DCLAMPAction := DCL_None ;
end;
DCL_NextRecord : Begin
DCLAMPFrm.NextRecord ;
DCLAMPAction := DCL_None ;
end;
DCL_NextProtocol : Begin
DCLAMPFrm.NextProtocol ;
DCLAMPAction := DCL_None ;
end;
end;
UpdateStopwatch ;
TimerBusy := False ;
if (Main.SESLabIO.StimulusStartFlag = True) and
(not Main.SESLabIO.DACActive) and
(not bRecord.enabled) then begin
outputdebugstring(pchar('stimulus started'));
Main.SESLabIO.StartStimulus ;
end ;
if not bOpenOLAWindow.Enabled then begin
if not Main.FormExists( 'RecPlotFrm' ) then bCloseOLAWindow.Click ;
end ;
// Display stimulus protocol status
if NOT bRecord.Enabled and (Main.SESLabIO.ADCTriggerMode = tmWaveGen) then begin
Inc(StatusCounter) ;
if (StatusCounter >= 10) and (Main.SesLabIO.TimerPeriod >= 0.0) then begin
edStatus.text := format( 'Rec %d %s (Next stim. %.1f/%.1fs)',
[RawFH.NumRecords,
Stimulator.ProtocolStatus,
Main.SesLabIO.TimerTime,Main.SesLabIO.TimerPeriod ]) ;
StatusCounter := 0 ;
end;
end ;
end ;
end ;
procedure TRecordFrm.StartSweep ;
// ----------------------------
// Start A/D acquisition sweep
// ----------------------------
var
ch : Integer ;
begin
{ Start recording sweep }
// Set A/D acquisition mode
case cbRecordingMode.ItemIndex of
// Detect events mode
rmDetect : begin
InitialiseEventDetector ;
RawFH.NumSamples := Settings.NumSamples ;
end ;
// Pulse generation mode
rmProtocol : begin
Main.SESLabIO.GetADCBuffer( ADC ) ;
if Main.SESLabIO.ADCActive then Main.SESLabIO.ADCStop ;
Main.SESLabIO.ADCTriggerMode := tmWaveGen ;
Main.SESLabIO.ADCExternalTriggerActiveHigh := False ;
// Set A/D and D/A update intervals
Main.SESLabIO.ADCNumChannels := Stimulator.Prot.NumADCChannels ;
Main.SESLabIO.ADCNumSamples := Stimulator.Prot.NumADCSamplesPerChannel ;
Stimulator.SetADCDACUpdateIntervals( Stimulator.Prot ) ;
//Stimulator.Prot.RecordDuration := Main.SESLabIO.ADCNumSamples*Main.SESLabIO.ADCSamplingInterval ;
// Removed 15.6.12
Main.SESLabIO.ADCCircularBuffer := False ;
EndofBuf := (Main.SESLabIO.ADCNumSamples*Main.SESLabIO.ADCNumChannels) - 1;
RawFH.NumSamples := Main.SESLabIO.ADCNumSamples ;
Main.SESLabIO.ADCStart ;
end ;
// Wait for external trigger
rmExtTrig : begin
Main.SESLabIO.GetADCBuffer( ADC ) ;
if Main.SESLabIO.ADCActive then Main.SESLabIO.ADCStop ;
Main.SESLabIO.ADCTriggerMode := tmExtTrigger ;
Main.SESLabIO.ADCExternalTriggerActiveHigh := rbExtTriggerHigh.checked ;
Main.SESLabIO.ADCNumChannels := Settings.NumChannels ;
Main.SESLabIO.ADCNumSamples := Settings.NumSamples ;
Main.SESLabIO.ADCSamplingInterval := Settings.RecordDuration / Settings.NumSamples ;
Main.SESLabIO.ADCCircularBuffer := False ;
EndofBuf := (Main.SESLabIO.ADCNumSamples*Main.SESLabIO.ADCNumChannels) - 1;
RawFH.NumSamples := Main.SESLabIO.ADCNumSamples ;
Main.SESLabIO.ADCStart ;
end ;
// Free run mode
else begin
Main.SESLabIO.GetADCBuffer( ADC ) ;
if Main.SESLabIO.ADCActive then Main.SESLabIO.ADCStop ;
Main.SESLabIO.ADCTriggerMode := tmFreeRun ;
Main.SESLabIO.ADCNumChannels := Settings.NumChannels ;
Main.SESLabIO.ADCNumSamples := Settings.NumSamples ;
Main.SESLabIO.ADCSamplingInterval := Settings.RecordDuration / Settings.NumSamples ;
RawFH.NumSamples := Main.SESLabIO.ADCNumSamples ;
Main.SESLabIO.ADCCircularBuffer := False ;
EndofBuf := (Main.SESLabIO.ADCNumSamples*Main.SESLabIO.ADCNumChannels) - 1;
RawFH.NumSamples := Main.SESLabIO.ADCNumSamples ;