aboutsummaryrefslogtreecommitdiff
path: root/sys/qpoe/QPOE.hlp
blob: cbb0ee5bfbc339701c7120c59af6aeeecd1ef5ac (plain) (blame)
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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
.help QPOE Jun90 "Quick POE Design"
.ce
\fBQuick-POE (Position Ordered Event File) Interface Design\fR
.ce
Doug Tody
.ce
July, 1988

.NH
Introduction

    The POE (Position Ordered Event file) facility is an interface and file
structure used to store and access the event (photon) lists generated
by event counting detectors.  Each event is described by a unique position,
time, energy, and possibly other parameters (e.g., polarization, position in
other coordinate systems, or instrument related parameters).  In the case of
an imaging event counting detector, the "image" generated consists of this list
of discrete events, rather than the regular matrix produced by a conventional
sampling detector.  Both types of detectors are fundamental to astronomy.

The POE interface is a stand alone interface built upon the standard VOS
interfaces DFIO (in a future release), PLIO, SYMTAB, FIO, and other lower
level interfaces.  The POE interface may be called directly by applications
code to create and access POE datafiles, for event file specific processing.
In addition, an IMIO image kernel is provided so that POE files may be
accessed as (read only) images, allowing existing IRAF image tasks to be
used to access POE files.  The main function of the POE image kernel is to
filter and sample the event list in real time, returning a conventional
sampled grid (image matrix) to the high level applications code.
The parameters controlling the filtering and sampling operations may be
specified by the user when the image (POE file) is accessed, making runtime
filtering of events possible in connection with any general image processing
task.

.NH 2
Important Concepts
    
    The primary object dealt with by this interface is the \fIevent file\fR,
consisting of a free format \fIfile header\fR and the main \fIevent list\fR.
The event list is a collection of \fIevent structures\fR, e.g., photons
hitting an imaging detector during the period of observation recorded by the
event file.  Each event is characterized by a standard set of attributes such
as the position of the event in detector, sky, or other coordinates, the time
at which the event was recorded, the energy of the event, and so on, plus
optionally additional instrument dependent attributes (in general the event
structure cannot be fixed, and the nomenclature may vary depending upon the
science being performed).

The events may appear in the event list in any order, but since most access
to image data tends to be spatial in nature, access will be most efficient if
the event list is position ordered.  This is the convention chosen by QPOE
and hence the name \fIpoe\fRfile, or \fIp\fRosition \fIo\fRrdered \fIe\fRvent
file.  An important alternative ordering is time ordering, which preserves
the order in which the events were originally recorded, but which requires
a complete scan of the event list to accumulate the events in a region of
interest during analysis.  There are cases where time ordering might be
preferable to position ordering, e.g., for time series analysis of a long
observation.

Of fundamental importance to the analysis of data from event counting
detectors is the concept of \fIfiltering\fR.  It is in the nature of event
counting detectors that they are often used to observe very faint objects
for very long periods of integration.  The total amount of data (number of
events) may be limited, so one wants to preserve all events, but since the
quality of the data may vary both with time and with position, it is common
to want to reject a portion of the data.  Conversely, the analysis being
performed may require one to examine only a portion of the data, e.g., only
events with a certain range of energies or arrival times, occurring within a
given region of the image.  Often the analysis will be repeated many times
wtih different filters.

Hence, most analysis of event counting data typically involves both
\fIrejection\fR and \fIregion of interest\fR filtering.  Rejection filtering
depends mostly upon the data itself, hence the rejection filter for an image
is a part of the image and should be in effect by default whenever the image
is accessed, although we would like to physically record all data and be able
to change the rejection filter either temporarily or indefinitely if desired.
Region of interest filtering, on the other hand, depends upon the scientific
analysis being performed rather than upon the data, hence is highly variable
and should be controlled by the user, independently of the data.

.NH 2
Interface Requirements

    Given the description of the problem to be solved presented in the previous
section, we can make the following observations regarding the POE interface:
.ls 4 o
A flexible binary file header supporting both scalar and variable length vector
fields is essential.  Examples of vector fields include the aspect and temporal
records (actually arrays of records, or subtables), the processing history
(probably stored as a single variable length text buffer), and the rejection
and region of interest filters (PLIO external format byte sequences stored as
opaque binary arrays).
.le
.ls o
In the general case the details of the event structure depend upon the
instrument for which data is being stored.  The minimum requirement is that
the event structure consist of a set of standard fields (x, y, time, energy)
followed by a variable length, instrument dependent area (hence the size of
the event structure, while fixed for a given datafile, should be allowed to
vary depending upon the data).  Ideally all fields should be named and
accessible for filtering, the names chosen should be variables rather than
constants, and the set of fields used to describe an event should be allowed
to vary depending upon the data.
.le
.ls o
Runtime access to the event list, including event-attribute and spatial
filtering, should be as efficient as possible since this is likely to be
by far the most time consuming part of the interface.  Header access
efficiency is much less important and is not expected to be a problem.
.le
.ls o
For most efficient access the event list should be stored sorted upon some
primary key, with an index maintained by the interface for that key,
and used for efficient retrieval.  The minimum requirement is that the primary
or sort key be the Y coordinate (corresponding to image lines).  Ideally it
should be possible for the sort key to be any field of the event structure,
or any combination of fields (e.g., Y+X, or T).  Ideally the interface itself
should be responsible for maintaining the event list in sort order; this is
not a requirement since writing to event files is much less common than
reading.  Ideally it should be possible for the event list to be unordered,
and it should be possible to transparently access the event list regardless
of the ordering.
.le
.ls o
Rejection filtering is typically required by the data, yet we wish to retain
all the data and be able to override or replace the default rejection mask.
The rejection filter is logically associated with the data and should be
stored with the data.  The minimum requirement is for the interface to be able
to store all the data plus the rejection mask, and be able to return only
the "good" data at runtime.  The interface is not required to perform rejection
filtering at runtime, although it would be desirable to be able to do so if
the efficiency penalty were not too great.  Alternatively, the event list
could be prefiltered, and the "good" and "bad" events stored in different
places in the datafile, requiring that the entire file be rebuilt to change
the rejection filter.
.le
.ls o
Region of interest filtering is a common operation for event data, and can be
difficult to implement efficiently, hence should be supported directly by the
interface.  At a minimum it should be possible to filter events by defining a
range of acceptable or unacceptable values for each of some subset of the event
attributes (e.g., energy or time).  Ideally it should also be possible to
specify an arbitrarily long list of ranges of acceptable values.  Ideally
spatial filtering should be supported as well; this is required for rejection
filtering, but is at most a desirable option for spatial region of interest
filtering (there is nothing about spatial filtering which is unique to event
data, hence it might be more appropriate to implement it at a higher level,
but on the other hand it might be more efficient to implement it at the event
i/o level since the event list is position ordered and can be very large).
.le
.ls o
It should be possible to specify the various filtering options both
transparently to applications programs (via a symbolic expression passed
into the interface by the user as part of the file specification),
or procedurally, via \fIset\fR-parameter calls issued by the client program.
.le

The above issues need to be adequately addressed in order to have a useful
interface.  In the longer term there are many other considerations, e.g.,
it is also desirable for the data format to be machine independent, and the
data format should be flexible, to accommodate the inevitable evolutionary
revisions as well as to accommodate data from a variety of instruments or
projects.  The event files can be very large, hence efficiency is a major
consideration for event i/o and filtering.

.NH 
The QPOE Interface
.NH 2
Implementation Strategy

    A two step operation is planned for implementing the POE interface within
IRAF.  The first step is the so called quick-POE interface.  The objective of
quick-POE is to provide the necessary functionality so that applications
development can proceed immediately, without waiting for the general interface
to be developed.  Once quick-POE is in place development of the fully general
POE interface can proceed at a more leisurely pace consistent with the plan to
provide most of the functionality of the generalized POE interface with other
standard IRAF facilities currently under development, most notably the datafile
i/o interface (DFIO), a general purpose binary file record manager to be used
in the new images structures project and elsewhere in IRAF.

The main facilities provided by POE are for access to general header fields,
access to the variable length aspect and temporal records, event list i/o,
and event filtering.  The POE header is a type of record, and the aspect and
temporal "records" are arrays of records (tables), as is the event list itself.
Any record access problem involving large records commonly involves the
related problems of indexing and selection based on a user supplied predicate
(boolean expression or filter in our case).

In the general case, we would like the POE interface to be able to support
data from a variety of detectors or instruments, not only those used in high
energy astrophysics but those used in optical and radio observatories as well,
hence the details of the data structures must be allowed to vary without
affecting the interface itself.  Ideally the POE files should be maintained
in a machine independent format, and the applications programs using POE
should not be affected by changes to the data format, and indeed should be
usable directly with any of several similar data formats, or with data formats
that evolve over time.

All these observations lead us to conclude that a general implementation of
the POE interface has much in common with the general record access problem,
hence the association of POE with DFIO.  Quick-POE will provide much the same
functionality at the applications level but will be less general, i.e., the
binary record structures as seen by applications will be mapped directly onto
external storage (the main contribution of DFIO is data independence and
flexibility).  This means that initially the applications will be tied to
a specific format datafile, changes to applications structures will require
reformatting the data, and the datafiles will probably be machine dependent.

This approach would be unacceptable in the long run, as the need arises to
support a variety of instruments, but is viable for initial applications
development provided a DFIO based implementation eventually replaces the
initial interface.  There should be little difference in terms of functionality
and efficiency between QPOE and POE; in fact QPOE may have the edge over POE
in terms of efficiency, since it will be less general.  It is likely that
applications developed for QPOE will be usable with POE with few if any
changes, e.g., by reimplementing QPOE as a layer on top of the more general
POE interface.  The main motivation for implementing a DFIO based POE will be
to provide increased data independence, a machine independent binary data
format, and the ability to support a variety of instruments with a single
interface.

Although some throw away code will have to be written to implement the QPOE
interface, most of the complexity of the interface lies in the event filtering
code, which should be reusable in the final interface.  Low level, custom
(non-DFIO) selection code is required for POE due to the unusual requirements
for region and temporal filtering, and the potentially extremely high data
volume (>10**6 event records).  This implies that DFIO itself will have to be
a layered interface, supporting low level access to the packed data records
for applications with unusual efficiency requirements (it will be).  Finally,
it is already evident that the low level file manager required by QPOE has
much in common with the access method code planned for DFIO, hence can serve
as a prototype for the DFIO file manager.

The remainder of this document will deal only with the details of the
QPOE interface.  The DFIO interface has already been specified,
including a sketch of a data definition for a POE file.  See \fIThe IRAF
Datafile I/O Interface\fR, February 1988.  The region filtering
code in POE will make use of the Pixel List I/O (PLIO) interface, described
in \fIThe IRAF Pixel List Package\fR, February 1988.  The latter interface
has already been implemented, as have all other interfaces (e.g., SYMTAB)
required by QPOE.

.NH 2
Architecture

    The architecture of IRAF as it pertains to the QPOE (and POE) interface
is summarized in the figure below.

.ks
.nf
        IMIO
                IKI
                        IK-POE
                                POE
                                        PLIO
                                        [DFIO]
                                        SYMTAB
                                        FIO
.fi
.ke

As indicated in the figure, QPOE depends most heavily on the VOS interfaces
PLIO (pixel list i/o), used for spatial filtering, SYMTAB (the general symbol
table package), used to manage the file header and aspect and temporal records,
and FIO (file i/o), used to access the binary file in
which the QPOE data is stored (low level, unbuffered asynchronous i/o is
used by the QPOE file manager).  The QPOE interface is accessed both directly
by applications code, and by the IMIO interface via an image kernel, shown
as IK-POE in the figure.  IK-POE and QPOE comprise the code to be written to
implement the QPOE interface.

The QPOE interface consists of the POE file itself, a binary data structure
[largely] private to the QPOE interface, and a set of procedures for creating,
writing into, and reading from POE files.  The procedures fall into several
categories, i.e.,
.ls 4
.ls o
General QPOE file management procedures.  These include routines for creating,
deleting, renaming, opening, and closing POE files, plus set/stat routines
for setting and querying the file parameters and interface options.
.le
.ls o
General header access procedures.  These include a conventional set of keyword
driven typed scalar get/put routines, plus get/put routines for accessing
variable length typed and opaque binary arrays (e.g., history records and the
aspect and temporal records).
.le
.ls o
Event i/o procedures.  These are routines for initially preparing and
subsequently reading (sequentially with seek) the main event list, e.g.,
the raw "get next photon" routine.
.le
.ls o
The selection subpackage.  Included are routines for opening and incrementally
compiling a user supplied selection predicate (filter) input as a formatted
text string, and for testing individual event records to see whether they
satisfy the given expression.
.le
.le

All binary data structures other than simple scalar variables, e.g., the
aspect and temporal records and the event structure, are described in QPOE
by compile time bound SPP binary data structure definitions, provided in a
standard interface include file (\fI<qpset.h>\fR) referenced by both QPOE and
selected applications.  When the interface is layered upon DFIO these
structures could continue to be used, since DFIO will have the ability to
define runtime mappings of conventional application defined structures onto
the physical data datafile (POE file) structures.

.NH 2
Interface Specification

    The quick-POE interface (QPOE, package prefix `qp') is a set of procedures
for accessing \fIpoefiles\fR, or position ordered event files.  Each poefile
consists of a \fBheader\fR of arbitrary size and content containing zero or
more named scalar or variable length (opaque, typeless) fields, plus an
\fBevent list\fR consisting of zero or more event structures.
The event structure is fixed at compile time via a conventional SPP structure
declaration in the include file \fB<qpset.h>\fR.

.NH 3
Interface Procedures

    The QPOE procedures fall into three main categories, the primary user
interface procedures (general datafile management, header access, and filtered
event i/o), the low level or raw event i/o procedures, and the low level
selection expression compile and evaluate procedures.

.NH 4
Header Access Procedures

    The routines described in this section are used to create, open, or
otherwise manipulate poefiles, to define new header parameters or query the
existing parameter set, and to read and write the values of both scalar and
vector parameters of various standard and poefile-specific datatypes.
These operators are summarized in the figure below.

The function of most of these procedures should be obvious.
The \fIqp_access\fR, \fIqp_delete\fR, \fIqp_rename\fR, and \fIqp_copy\fR
operators perform the implied operation on the named poefile.
The poefile may be rebuilt with \fIqp_rebuild\fR, recovering any unused
space and rendering storage for the internal data structures (logically)
contiguous in the process (a rebuild is just a copy/rename/delete).

The \fIqp_open\fR procedure must be called to open or create a poefile,
before it can be accessed.  The NEW_FILE and NEW_COPY modes are supported
for creating new files.  If NEW_COPY mode is specified, a reference file
may be specified (via the descriptor \fIo_qp\fR) from which the new file
is to inherit the header but no data (no event list).
The \fIqp_seti\fR and \fIqp_stati\fR procedures are used to set and stat
any parameters affecting QPOE i/o, and \fIqp_sync\fR updates an opened
poefile on disk.

The \fIqp_get\fR and \fIqp_put\fR scalar functions behave as for the other
VOS interfaces, e.g., they will abort if the named parameter does not exist,
or if the implied datatype conversion is illegal.  The \fIqp_add\fR 
procedures are equivalent to the \fIqp_put\fR procedures except that they
will create the named parameter if it does not already exist (see also
\fIqp_addf\fR, discussed below).

.nf
         yes|no = qp_access (poefile, mode)
                    qp_copy (poefile, newfile)
                  qp_rename (poefile, newfile)
                 qp_rebuild (poefile)
                  qp_delete (poefile)

               qp = qp_open (poefile, mode, o_qp)
                    qp_seti (qp, param, ival)
            ival = qp_stati (qp, param)
                    qp_sync (qp)
                   qp_close (qp)
           
     val = qp_get[bcsilrdx] (qp, param)
                    qp_gstr (qp, param, outstr, maxch)
           qp_put[bcsilrdx] (qp, param, val)
                    qp_pstr (qp, param, strval)
           qp_add[bcsilrdx] (qp, param, defval, comment)
                    qp_astr (qp, param, strval, comment)

              fd = qp_popen (qp, param, mode, type)
            nelem = qp_read (qp, param, buf, nelem, first, dtype)
                   qp_write (qp, param, buf, nelem, first, dtype)

        yes|no = qp_accessf (qp, param)
                 qp_deletef (qp, param)
                 qp_renamef (qp, param, newname)
                    qp_addf (qp, param, dtype, maxelem, comment, flags)
	  nelem = qp_queryf (qp, param, dtype, maxelem, comment, flags)

         list = qp_ofnl[su] (qp, template)
          nch|EOF = qp_gnfn (list, outstr, maxch)
                    qp_cfnl (list)
.fi

Array valued parameters may be randomly read with \fIqp_read\fR and
written with \fIqp_write\fR; arrays may be any length, and will be
automatically extended in a write.  The only way to shorten an array
parameter is to copy it and delete the old parameter.  The typed read and
write functions allow automatic type conversions, and external storage of
the data in a machine independent form (should the interface choose to do so).
In addition to the standard SPP types, QPOE supports the special types
TY_EVENT, TY_ASPECT, and TY_TEMPORAL.  Finally, the type TY_OPAQUE denotes
an array of element size SZ_CHAR, which will be copied to and from external
storage without the data being modified in any way (note that opaque data
is machine independent only if the application encodes it that way).

Alternatively, an array valued parameter may be opened as a random access
\fIfile\fR with \fIqp_popen\fR, and then read or written with conventional
FIO calls.  The value of the \fItype\fR parameter must be TEXT_FILE or
BINARY_FILE, as for a conventional file.  If the type is TEXT_FILE then
only text data may be stored in the file, and text data will be byte packed
on disk.  The BINARY_FILE type is equivalent to the QPOE type TY_OPAQUE.
File i/o to a QPOE parameter is equivalent to file i/o to a conventional
binary file in terms of both efficiency and semantics, i.e., the data is not
modified in any way, and the "files" may be any size (the main semantic
difference is that deleting the parameter does not immediately free the space).
A parameter opened as a file with \fIqp_popen\fR is closed with the FIO
\fIclose\fR routine.

Although new parameters may be defined when first written to by calling one of
the typed \fIqp_add\fR functions, the most general procedure for adding new
parameters is \fIqp_addf\fR, which allows the datatype and vector length of
the parameter to be explicitly specified, along with a comment describing the
new parameter.  The procedure \fIqp_accessf\fR tests if the named parameter
exists, and \fIqp_deletef\fR and \fIqp_renamef\fR make it possible to delete
and rename parameters, e.g., for implementing array copy procedures.
The \fIqp_queryf\fR procedure returns the datatype, allocated vector length,
current vector length, and comment field of the named parameter.

The field name list procedures (\fIqp_ofnl[su]\fR etc.) are used to obtain
the names of all header parameters matching the given template; a null or "*"
template returns the names of all header parameters.  This is the only way by
which an application without apriori knowledge of the field names can determine
what is in the header, e.g., to list or copy the header.

.NH 4
Filtered Event I/O Procedures

    The \fBevent i/o\fR subpacke provides sequential i/o facilities for the
main event list of the poefile.  These procedures, known as the QPIO (QPOE
event i/o) package, provide read or write (append) access to the event list,
optionally filtered when reading to select events spatially or by event
attribute.

Under QPOE, an event list is stored as a variable length array (i.e., as a
named header parameter) of type \fIevent\fR.  The QPIO package takes this
basic object and adds additional structure for more efficient i/o, e.g., events
are blocked into large, fixed size \fIbuckets\fR of N events, the first two
events of each bucket containing the minimum and maximum event values for that
bucket.  If the event list is sorted an \fIindex\fR may be maintained for the
list; this index, plus the min/max event values maintained for a bucket, are
used to optimize basic event i/o and filtering.  During event i/o the raw
event list may be filtered spatially or by event attribute.  All this is
transparent to the application, which merely opens the event list parameter
and begins reading (or writing) blocks of events.

Before i/o can take place the named event list parameter is opened with
\fIqpio_open\fR.  The selection filter to be used by QPIO may be specified
via a selection expression passed in by the user at poefile open time (as
part of the poefile name), at QPIO open time (as part of the parameter name),
or in subsequent calls to \fIqp_addfilter\fR (each call incrementally modifies
the current filter) or to \fIqp_setfilter\fR (each call replaces the affected
portion of the current filter).  A region mask may also be specified with
\fIqp_setmask\fR; if no mask is specified, the default rejection mask is used
(or more precisely, its inverse).

.nf
           qpio = qpio_open (qp, param, mode)
               qpio_mkindex (qpio, key, nelem)
              qpio_setrange (qpio, vs, ve, ndim)
       qpio_[add|set]filter (qpio, selexpr)
    nchars = qpio_getfilter (qpio, outstr, maxch)
               qpio_setmask (qpio, pl)
          pl = qpio_getmask (qpio)
   nev|EOF = qpio_getevents (qpio, ev, maskval, maxevents)
             qpio_putevents (qpio, ev, nevents)
               qpio_readpix (qpio, obuf, vs, ve, dxim, xblock, yblock)
                 qpio_close (qpio)
.fi

Events are read sequentially with \fIqp_getevents\fR, which fills in the
pointer array \fIev[maxevents]\fR with one or more pointers to event structs,
returning the number of events read as the function value, or EOF when the
event list is exhausted.  Events are returned in the order in which they are
stored in the main event list.  If a region mask is used for spatial
filtering, the mask value associated with the output events is returned in 
\fImaskval\fR.  Filtering and subranges are supported only for reading;
\fIqp_putevents\fR may only be used to append to the output poefile.
At present, poefiles are not randomly updatable, as this would require
runtime editing of the compressed event lists and it is not clear how useful
such a feature would be.

If less then the entire image is to be accessed then \fIqp_setrange\fR
may be called to specify the region of the poe image from which events are
to be read (the vector coordinates \fIvs\fR and \fIve\fR are specified
relative to the predefined primary event coordinate system, i.e., the PO
coordinates).  Repeated calls to \fIqp_setrange\fR may be made to access
multiple regions of the image, or to rewind the i/o pointer for a region.

An alternative to event i/o is provided by \fIqp_readpix\fR, which samples
the event list using the current filters and blocking factor,
generating \fInpix\fR pixels beginning with the pixel at the image coordinates
specified by the vector \fIv\fR.  This is the routine used by the POE IMIO
image kernel to read from a poefile.  Only integer pixels are supported.
On output, each pixel value is a count of the number of filtered events
mapping into that pixel.  A region mask may be used to filter the event list,
but the ability to discriminate between different regions by the mask value
is lost.

A poefile may contain any number of event list parameters, although most
files are expected to store only the main event list.
As an alternative to QPIO, event-array parameters may be accessed directly
via the normal header access parameters, e.g., \fIqp_read\fR, but i/o may be
somewhat less efficient (due to the copyout), the bucket structure will be
visible, and no filtering is possible.  In short, the raw event list will
be accessed as an array, returning the min/max events in each bucket along
with the data events.

In the current implementation, the event structure is a fixed, predefined
binary structure, and all event i/o is expressed in terms of pointers to event
structures.  The event structure is defined in the include file
\fB<qpset.h>\fR, discussed in the appendix.

.NH 4
Selection Subsystem Procedures

    The \fBselection subsystem\fR is a facility used to perform runtime
filtering of the event list, returning to the calling program only those
events satisfying some user defined selection criteria.  The selection
subsystem is driven by a selection expression provided by the user as a
formatted string, normally at image (poefile) open time.  The selection
expression syntax itself is independent of the procedural interface and is
described separately in section 2.3.2.  The selection procedures are
summarized in the figure below.

.nf
	     ex = qpex_open (qp, expr)
    ok|err = qpex_modfilter (ex, exprlist)
    nchars = qpex_getfilter (ex, outstr, maxch)
	nev = qpex_evaluate (ex, i_ev, o_ev, nev)
		 qpex_close (ex)
.fi

A selection expression, input as the string \fIexpr\fR, is compiled with
\fIqpex_open\fR, which returns a pointer to the runtime descriptor (filter)
for the given compiled selection expression.  If \fIexpr\fR is the null
string the filter returned will pass all events.

An active filter may be modified with \fIqpex_modfilter\fR, which combines
the expression \fIexpr\fR into the current filter, allowing complex
filters to be built up in several calls, or allowing an application to modify
a base filter without knowledge of the base filter.  The expression consists
of a list of comma delimited "attribute = exprlist" terms.  If the assignment
is specified as "=" the term for the given attribute is replaced; if the
assignment is "+=" the term for the given attribute is further qualified.
A text representation for the current filter may be obtained at any time with
\fIqpex_getfilter\fR.  The boolean function \fIqpex_evaluate\fR is called to
test whether a specific event meets the selection criteria, i.e., to test
whether or not \fIexpr\fR is true for the given event.

Selection is normally performed transparently to the application by the QPIO
interface, which calls the routines described in this section to create and
apply event-attribute filters.

.NH 3
Spatial and Event Attribute Filtering
.NH 4
Selection Syntax

    Selection expressions are used to construct \fIfilters\fR to specify the
rejection mask and region of interest filters before reading the event list
via QPIO.  Due to the complexity of event attribute selection, selection
predicates are specified syntactically, i.e., as an expression input as a
text string.  These filters may be input by the user as part of the image
or poefile specification (object name), transparently to applications code,
or they may be constructed by the application via calls to the QPIO or
selection routines.  Complex or frequently referenced filters may be stored
in text files and referenced by filename if desired.  It does not matter
whether a filter is input all at once, or compiled incrementally.

An event attribute filter consists of a set of filters for each event
attribute.  By default, i.e., if no filter is specified, all attribute values
are passed.  If a filter is specified for an attribute, the filter specifies
either a bitwise mask value, or a list of acceptable values or ranges of values
for the attribute.  An event is passed if and only if all event attribute
filters pass the event.  If a list of acceptable values are specified, the
list may be any length, with little impact on filtering efficiency.

The basic event attribute filter syntax consists of a list of attribute
filters, e.g.:

        attribute = values [, attribute = values ...]

where \fIattribute\fR is the attribute name, e.g., a position attribute,
time, energy, and so on, and \fIvalues\fR is a mask or list of values.
Mask values are integers prefixed by `%', e.g.,

        attribute = %1003B

Note that the mask may be specified in decimal (the default), octal (`b' or
`B' suffix), or hex (`x' or `X' suffix), in accord with the usual IRAF
conventions.  The meta-characters used in selection expressions have been
selected to avoid or at least minimize the need to quote such expressions
in CL commands.

A specific value or list of values may be specified as a
simple integer constant, or comma delimited list of constants, e.g.:

.nf
        attribute = 3
or
        attribute = 3, 5, 20X
.fi

Ranges are specified using the `:' notation, e.g.,

        attribute = 3, 5, 8:11

A `!' may be prepended to indicate the opposite, i.e., "everything but":

.nf
        attribute = !%14B
or
        attribute = 3, !1:10
.fi

Open ended ranges may be used to indicate that the range includes all values
less than or equal to or greater than or equal to the given value, e.g.,

        attribute = :100

denotes all values less than or equal to 100.

File inclusion or \fImacro expansion\fR is denoted by a C-like function call
notation, e.g.,

.nf
        macro()
or
        macro(a,b)
.fi

Any arguments are expanded via string substitution when the text of the macro
or include file is expanded.  Include files should have the extension ".qpm".
Macros are permitted only if the variable \fBqpinit\fR is defined in the user's
environment, the string value consisting if the filename of the user's QPOE
macro file.  In a reference to a macro \fImacro\fR, QPOE will look first in
the macro definitions file pointed to by \fIqpinit\fR for the named macro,
then it will look for the file "\fImacro\fR.qpm" in the current directory.

Parenthesis are optional and may be included to, for example, make attribute
value lists more easily identifiable.  If a line ends in a comma or backslash
continuation is assumed; blank lines and comment lines are ignored.
The syntax for attributes with floating point values is identical to that
for integers except that mask values are not allowed.

.NH 4
Region Specification

    QPOE does not itself contain any syntax-level support for specifying
region masks, e.g., via a list of include and exclude circles and other
shapes.  The reason for this is that it is too difficult to come up with
a sufficiently general scheme at the level of an interface like QPOE;
there are too many ways to specify regions, hence in general such region
specification must be done at the applications level.  QPOE does however
include very general and efficient support for region analysis provided the
region mask is input already encoded into a PLIO binary mask.  Since PLIO
includes high level primitives for defining masks in terms of include and
exclude circles, boxes, lines, polygons, etc., it is easy to extend QPOE at
the applications level to include support for a region specification language
tailored to the specific application.

While QPOE cannot itself process a user defined region description to create
new region masks, it is possible to \fIselect\fR from any number of region
masks if these are prepared in advance using other systems facilities or
applications programs.  PLIO region masks may be stored in the QPOE header
as named parameters of type opaque binary array, or they may be stored in
external binary files.  The region mask to be used may be specified by
including an assignment of the form

        mask=[\fIparam\fR|\fIfile\fR.pl]

in the selection expression.  Unless otherwise specified, this region mask 
will be combined with the default rejection mask for the poefile (the final
mask will be the region mask \fIand\fR-ed with the \fInot\fR of the rejection
mask).

.NH 4
Predefined Selection Keywords

    While the syntax of a selection expression is an inherent part of the
QPOE interface, the names of the event attributes used in selection
expressions are logically part of the event structure, and ideally should
be stored with the data and used by the interface only to determine the
attribute datatypes and offsets into the event structure when the selection
expression is compiled at runtime.  We should not really be documenting the
specifics of the POE external data structures here, but in QPOE these data
structures are wired into the interface, so it is appropriate to do so.

The following \fIstandard event attributes\fR are defined.  Minimum match
abbreviations are of course permitted.  The keyword \fIpi\fR is an acceptable
alias for \fIenergy\fR (\fIpi\fR and \fIpha\fR are examples of discipline
dependent terminology which should be associated with the data and not the
interface).

.nf
        X               short           range in X (PO coords)
        Y               short           range in Y (PO coords)
        TIME            real            time event was recorded
        ENERGY,PI       int             energy of event
        PHA             int             pulse height
.fi

The event attributes may also be referred to using the generic notation
[\fIsir\fR]\fIN\fR, which refers to each attribute by its datatype and
struct offset (byte units, zero indexed) rather than by name.  For example,
if the event attributes shown above are assumed to be shown in the order
in which the fields are stored in the event struct, the \fItime\fR field
could also be referred to as \fIR4\fR, and \fIpha\fR as \fII10\fR.  This
crude but effective technique may be used to reference any private
(nonstandard) fields of the event struct in selection expressions.

The following additional, non-event keywords are defined:

.nf
        BLOCK           int             \fIqp_readpix\fR blocking factor
        MASK            string          region mask to be used
        FILTER          string          region filter to be used
        REJMASK         string          rejection mask to be used
        REJFILTER       string          rejection filter to be used
.fi

The default values for these parameters are taken from datafile header
parameters of the same name, if such are found.  Masks are specified either
by the name of a header parameter of type opaque binary array (containing
an encoded PLIO mask), or by the name of a PLIO mask file, extension ".pl".
Named filters are specified by the name of a header parameter of type char
array, or by the name of a text file (extension ".qpf"), where in either
case the named object contains the selection expression text.

.NH 3
Interface Set/Stat Parameters

    The internal parameters for the QPOE interface, and all user accessible
data structures, e.g., the event and other structures, are defined in the
global system include file \fB<qpset.h>\fR.  This file should be referred to
for up to date documentation on these definitions and structures; the
discussion which follows may not be kept up to date.

The following interface parameters may be accessed via the \fIqp_seti\fR and
\fIqp_stati\fR procedures:

.nf
        QP_XRESOLUTION          resolution of an event x-coordinate
        QP_YRESOLUTION          resolution of an event y-coordinate
        QP_LENEVENT             length of an event structure
        QP_LENINDEX             resolution of the event list index
        QP_BUCKETSIZE           event list bucket size, nevents
        QP_PAGESIZE             datafile page size, bytes
        QP_CACHESIZE            number of buffers in data buffer cache
        QP_MAXFILES             max lfiles in datafile (fixed)
        QP_NFILES               query number of lfiles in datafile
        QP_NPAGES               query number of pages in datafile
        QP_FREEPAGES            query number of free pages in datafile
.fi

The parameters shown above may be set only at datafile creation time.
The X and Y resolution parameters define the range of event x,y coordinates.
The resolution of the event list index is set by QP_LENINDEX, and may be less
than the full resolution of the event pixel Y-coordinate.
The remaining parameters control how storage is physically allocated in the
datafile and in any event lists.

.NH 2
Detailed Design
.NH 3
Event Attribute Filtering

    The point of event-attribute filtering is to test an event to see if it
satisfies a user defined event selection expression.  A selection expression
may be decomposed into a list of simple, independent expressions for the
individual event attributes; the event satisfies the full expression only
if the value of each event attribute satisfies the associated attribute
expression.  Currently, attribute expressions are limited to bitmasks, lists
of acceptable values, or lists of ranges (inclusive) of acceptable values.

        attr1=expr, attr2=expr, ...

The highly constrained nature of event-attribute expressions makes expression
evaluation straightforward and fast.  Expression evaluation is implemented
by logically negating each attribute expression and testing each in turn;
expression evaluation ends either when an attribute test fails, in which case
the event is rejected, or when the end of the attribute expression list is
reached, in which case the event is passed.

The obvious way to implement such an expression evaluator is with a simple
interpreter.  The expression is parsed and compiled to produce a simple
interpreter program, using the instructions shown in the figure below.

.ks
.nf
        \fIinstruction     arguments\fR

        MSK[sir]        offset  maskval         bitwise mask test
        EQL[sir]        offset  value           equality test
        LEQ[sir]        offset  value           less than or equal
        GEQ[sir]        offset  value           greater than or equal
        RNG[sir]        offset  lowval highval  range test (inclusive)
        LUT[sir]        offset  lut             lookup table
        NOT                                     invert test
        RET                                     return, pass event
.fi
.ke

An interpreter program consists of a series of these instructions.  The
\fIoffset\fR argument gives the offset of the event attribute (field) to
be tested; the datatype of this field must match that of the instruction
and of the data argument or arguments, if any.  Only event attributes for
which restricted values were specified are tested, hence the cost of the
evaluator depends only upon the complexity of the expression to be evaluated.
An interpreter of this type can be coded very efficiently as a switch-case
statement (jump table) within an optimized DO-loop.

Simple attribute value tests are most efficiently coded as several \fIMSK\fR,
\fIEQL\fR, etc., instructions.  The only case of any complexity is where the
attribute has a long list of acceptable values or ranges of values.  This is
most efficiently coded using a lookup table, using the \fILUT\fR instruction
shown in the figure.  A lookup table test may be preceded by a range test to
limit the size of the lookup table required.  For an integer or short integer
attribute, the lookup table will be a \fIboolean\fR table containing one entry
for each possible value of the attribute in the range spanned by the table.

Use of a lookup table for floating point attributes is more difficult since
an enormous lookup table might be required to preserve the resolution of
the floating point numbers used to define ranges.  The solution is to employ
an \fIinteger\fR (rather than boolean) lookup table of \fIreduced resolution\fR.
The floating point value of the attribute to be tested is mapped into a bin of
the lookup table.  The integer value of the table entry has one of the
following values:

.ks
.nf
        0               Reject all FP numbers mapping to this bin.
        1               Accept all FP numbers mapping to this bin.

        N               Some of the FP numbers mapping to this bin are
                        legal, and some are not.  The value N is the
                        address of a segment of interpreter code to be
                        executed to test a FP value mapped to this bin.
.fi
.ke

The performance of this algorithm for floating point table lookup depends
upon the frequency with which 0 or 1 is encountered as the table value during
lookup; if 0 or 1 is encountered most of the time, then a floating point LUT
test is comparable in expense to an integer LUT test.  But since we already
have to map a floating point number into an integer space of reduced
resolution, we can easily vary the resolution of the lookup table,
increasing the resolution of the table until the desired level of efficiency
is reached (the interpreter execution time for case N is pretty fast in any
case, so this is not critical).

In summary, the expense of event-attribute filtering is directly proportional
to the number of attribute tests to be performed.  An arbitrary number of
values or ranges of values may be specified for an attribute with little if
any affect on performance, even for floating point attributes (e.g., for
time-tagged quality filtering).

.NH 3
Region Filtering

    Region filtering is implemented in QPOE by the PLIO interface, which is
documented elsewhere.  PLIO permits regions of arbitrary complexity to be
described and used for event filtering, with little overhead beyond that
already present for i/o on a large event list with no region filtering.
This assumes only that the event list is position ordered, and that the
region mask is specified in the PO (position ordered) coordinate system.
This makes it possible for QPIO to use the mask to reduce the number of
events to be examined; event attribute filtering is performed only on those
events read through the region mask (this is similar to masked image i/o,
i.e., the MIO package).

If the event structure supports multiple coordinate systems and the region
mask refers to a non-PO coordinate system, then the only approach is to first
perform event-attribute filtering on the non-positional event attributes,
then for each event passing the event-attribute filter, fetch the mask value
corresponding to the x,y coordinates of the event.  This is still an efficient
technique since only mask pixel lookup is required (no complicated region
list traversal is involved), but it will be significantly less efficient than
PO region filtering since we cannot take advantage of position ordering to
reduce the number of events to be examined, and the overhead of accessing
the region mask will be greater.

.NH 3
Datafile Layout and Access
.NH 4
File Structure

    The QPOE file structure is private to the QPOE interface and is discussed
here only for the purpose of detailing (and documenting) the design of the
interface.  The QPOE file is a random access, dynamically extendable, binary
file.  Under QPOE these files will be partially, but not completely, machine
independent, hence file sharing by machines of different architectures will
not be provided initially.  This will be rectified when management of the
datafile is later turned over to DFIO.

To provide a reasonable degree of flexibility, QPOE contains many
variable length data structures, e.g., there may be any number of header
parameters, including array valued parameters of arbitrary size.  New header
parameters may be added at any time, and new data may be appended to array
parameters at any time.  This flexibility places certain demands upon the
low level file manager used to maintain these data structures in the datafile.

All access to the physical datafile is via a low level binary
\fIfile manager\fR.  The purpose of the file manager is to implement a
restricted implementation of the binary file abstraction upon a single host
level binary file.  This provides the "lightweight" binary file mechanism we
need for QPOE.  Since the file manager is a low level facility, it is
implemented using only the low level asynchronous i/o facilities provided
by FIO to read and write file pages, once the file has been opened.

The file manager provides routines for creating new datafiles, and for
creating, deleting, etc., \fIlightweight files\fR (lfiles) within a datafile.
Storage for lfiles is allocated in units of datafile \fIpages\fR.  For each
data page in the datafile there is an entry in the datafile \fIpage table\fR.
The page table itself is stored as an lfile (\fIlfile zero\fR) in the data
pages.  Files at the file manager level are known only by their file number;
association of these file numbers with file names is left up to the higher
level code, and in the case of QPOE is done with the symbol table (which is
also stored as an lfile).

.ks
.nf
         +-----------------+
           datafile header              fixed size
         +-----------------+
              file table                fixed size
         +-----------------+
                  |
              data pages                data and page table pages
                  |                        (arbitrarily large)
                  v
.fi
.ke

The page table is a vector mapping datafile pages by file offset onto lfile
file numbers; the value of each page table entry is the file number of the
lfile to which the page is assigned.  When the file manager opens an lfile
it scans the page table, extracting the page numbers of the pages assigned
to the lfile, to form a vector mapping lfile page offsets directly onto
datafile page offsets.  New pages are always allocated at the end of the
datafile, and new lfiles are always allocated at the end of the file table,
hence lfile deletion will leave "holes" (unused storage) in both the datafile
pages and file table.  A \fIrebuild\fR operation is required to reclaim the
space occupied by these holes.  Deleted files are recoverable by merely
revalidating their file table entries.

Every variable size object managed by QPOE is stored in the datafile as a
distinct lfile.  Since storage for lfiles is allocated in units of file pages,
the minimum amount of storage used by a variable length object is 0 or 1 page.
Examples of variable size objects are the SYMTAB symbol table
used to describe the contents of the datafile header (and any other symbols
used by QPOE), the static data storage area (used to store the values of
scalar and static array valued header parameters), and individual variable
length arrays.  Note that each variable length array is stored in the datafile
as a separate lfile; if the maximum size of an array is less then the page
size, it will be more efficient to store it as a static array.

The most important example of a variable length array is the main event list
of the poefile.  To improve i/o efficiency and speed selection, the event
structs stored in an event list are grouped together into \fIbuckets\fR,
as discussed earlier in section 2.3.1.2.  Each bucket will always occupy an
integral number of file pages.  Storage for buckets is allocated contiguously
in the datafile, and buckets are always read and written to disk in a single
i/o transfer.

The most important physical datafile parameters are hence the page size and
the bucket size.  A larger page size can improve i/o efficiency and reduce the
size of the page table, but can lead to significant wasted space if there are
many variable length arrays.  Since the i/o system will move entire large
blocks of pages to and from disk whenever possible, use of a small page is
normally preferred.  A large bucket size improves i/o efficiency for event
lists, but if the bucket size is too large then bucket searching takes longer,
and selection efficiency may decrease.

.NH 4
File Manager

    The function of the file manager is to map a set of lfiles onto a single
random access host binary file.  The file manager must keep track of the size
and type of each file, and whether or not it has been deleted.
In addition, the file manager must maintain a page table for the entire
datafile, noting the lfile to which each page is assigned.  While an lfile
is open the file manager must maintain the page vector for that lfile so that
lfile offsets may be mapped directly onto datafile offsets.

The number of lfiles is fixed at datafile creation time, and lfiles are
referred to by file number.  File number zero is the datafile page table;
the first user lfile is number one.  A datafile with a max file count of one
would actually contain two lfiles, counting the page table.

The file manager interface is summarized in the figure below.  A new datafile
may be created or an existing datafile opened with \fIfm_open\fR.
If a new datafile is being created the page size and max file count may be
changed from their default values with calls to \fIfm_seti\fR, and the values
of these and other parameters may be queried at any time with \fIfm_stati\fR.  
An opened datafile may be copied with \fIfm_copyo\fR, omitting deleted lfiles
and rendering file segments contiguous.  The page size and max file count
may be changed in a copy operation if desired.

The \fIfm_access\fR, \fIfm_rename\fR, and \fIfm_delete\fR routines perform
the indicated operation upon the named datafile.  The \fIfm_rebuild\fR
routine rebuilds a datafile, discarding deleted structures and coalescing
storage for objects.  This routine, as well as \fIfm_copy\fR, are built upon
on the lower level routine \fIfm_copyo\fR,  which does the real work, and
which allows the structural attributes of the new datafile to be specified
in \fIfm_seti\fR cals.

All i/o to lfiles is via the six routines beginning with \fIfm_lfopen\fR in
the figure below.  These routines constitute a FIO binary file driver for
lfiles, and may be called directly, or passed to the FIO routine \fIfopnbf\fR
to open an lfile as a binary file (\fIfm_lfname\fR should be called first
to construct a pseudo-filename for the lfile so that \fIfm_lfopen\fR can
reconstruct the file manager descriptor, lfile number, and lfile type).
Note that the lfile driver routines are unbuffered and (potentially)
asynchronous, and that i/o must be in units of datafile pages.
(See the buffer cache routines described in the next section for a higher
level facility for i/o to lfiles).

.nf
        yes|no = fm_access (datafile, mode)
                  fm_rename (datafile, newname)
                    fm_copy (datafile, newname)
                  fm_delete (datafile)
                 fm_rebuild (datafile)

               fm = fm_open (datafile, mode)
                    fm_seti (fm, param, ival)
            ival = fm_stati (fm, param)
		   fm_debug (fm, out, what)
                   fm_copyo (fm, fm_to)
                    fm_sync (fm)
                   fm_close (fm)

       lfile = fm_nextlfile (fm)
                  fm_lfname (fm, lfile, type, lfname, maxch)

                  fm_lfopen (lfname, mode, lf)
                 fm_lfstati (lf, param, ival)
                 fm_lfaread (lf, buf, nbytes, offset, status)
                fm_lfawrite (lf, buf, nbytes, offset, status)
                 fm_lfawait (lf, status)
                 fm_lfclose (lf, status)

                  fm_lfstat (fm, lfile, statbuf)
                fm_lfdelete (fm, lfile)
              fm_lfundelete (fm, lfile)
.fi

In a sense, all lfiles exist as zero length files when the datafile is
created, since the lfile descriptors are preallocated and the files are
known only by number.  Lfiles become interesting when they are opened as
files with \fIfm_lfopen\fR, and data is written into the file.  An lfile
may be deleted with \fIfm_lfstat\fR.  All this does is set the delete bit
in the lfile descriptor, hence a deleted lfile may later be undeleted with
\fIfm_lfundelete\fR.  The data in a deleted lfile is not lost until the lfile
is again opened and written into, or the datafile is rebuilt.  Information
on a specific lfile (size, type, etc.) may be obtained with \fIfm_lfstat\fR.

There is nothing about the file manager which is specific to QPOE, so it is
implemented as a separate, standalone facility, and may be used in applications
other than QPOE.

.NH 4
Buffer Cache

    For reasons of efficiency, QPOE maintains portions of the datafile in
memory buffers while a datafile is open.  The main QPOE descriptor, symbol
table, and file manager descriptor and page table are maintained in special
runtime data structures internal to the respective interfaces.  All other
data is stored in lfiles and accessed only upon demand.  In particular,
storage for all static (non variable length) QPOE header parameters is
maintained in a single lfile, and storage for each variable length parameter
is allocated in a separate lfile.

Since most access to QPOE header parameters is via simple gets and puts to
named parameters, lfile access is handled by QPOE transparently to the client
applications program.  To avoid excessive disk i/o when randomly accessing
the datafile, it is desirable for QPOE to maintain a cache of several lfile
data buffers, e.g., so that accesses to a series of static parameters or
repeated accesses to read or write different parts of an array parameter
should incur minimal disk accesses.  This buffer cache is implemented in
QPOE by simply opening each lfile as a file under FIO, leaving it up to FIO to
manage the file buffer, and maintaining a LRU cache of open lfiles in QPOE.
The number of buffers (open lfiles) is controlled by the QP_CACHESIZE
parameter.  Since the lfile buffer cache is a general datafile related
facility, it is implemented by the file manager.

.ks
.nf
              fd = fm_getfd (fm, lfile, mode, type)
                   fm_retfd (fm, lfile)
                 fm_lockout (fm, lfile)
                 fm_debugfd (fm, out)
.fi
.ke

The \fIfm_getfd\fR routine maps an lfile onto a file descriptor.  A file
descriptor is opened on the lfile only when necessary.  Once opened, an lfile
remains in the cache until forced out by the LRU replacement algorithm,
or the datafile is closed.  Removal of an lfile from the cache (closing the
associated file descriptor) is permitted only after a call to \fIfm_retfd\fR;
calling this routine does not immediately close the file, it only permits it
to be closed.  Most calls to \fIfm_getfd\fR should return a file descriptor
immediately, with very little overhead, with an already active file buffer,
hence repeated calls to the cache manager and FIO may be made without
incurring any disk accesses.

Note that lfiles may be opened on file descriptors via direct calls to the
file manager, regardless of whether these lfiles are already open in the
buffer cache.  This allows two or more independent file buffers to be
simultaneously active on the same lfile, but opens the possibility of loss
of data if the buffers overlap.  If this is a problem, the routine
\fIfm_lockoutfd\fR may be called to prevent inadvertent use of an lfile by
the cache.  This should be followed by a call to \fIfm_retfd\fR to clear the
lockout bit once the reason for the lockout (usually a noncached lfile open)
is gone.  The routine \fIfm_debugfd\fR will print information on \fIout\fR
describing the contents of the buffer cache.

.tp 24
.NH 3
Interface Structure
.NH 4 1
Header Access Package (QP Routines)

    The structure of the general QPOE routines (mostly header access) is
illustrated in the figure below.

.ks
.nf
                                +--------+
                                |   QP   |
                                +--------+
                                 /      \
                                /        \
                        +--------+      +--------+
                        | SYMTAB |      | BCACHE |
                        +--------+      +--------+
                                             |
                                      +--------------+
                                      | FILE MANAGER |  
                                      +--------------+

              Figure 1.  Structure of the Header Access Routines
.fi
.ke

To fulfill a get or put header access, QPOE will access the symbol table
(SYMTAB) to lookup the symbol name and determine the symbol datatype, nelem,
lfile number, and lfile file offset where the value is stored.  The buffer
cache (BCACHE) and FIO are then called to access the value of the parameter
in the datafile.

.NH 4
Filtered Event I/O Package (QPIO)

    The structure of the filtered event i/o package (QPIO) is illustrated in
the figure below.

.ks
.nf
                                +--------+
                                |  QPIO  |
                                +--------+
                                 /   |  \       
                      __________/    |   \__________
                     /               |              \
                +--------+      +--------+      +--------+
                |  PLIO  |      |  QPEX  |      | BCACHE |
                +--------+      +--------+      +--------+
                                     |               |
                                +--------+    +--------------+
                                | SYMTAB |    | FILE MANAGER |  
                                +--------+    +--------------+

                     Figure 2.  Structure of QPIO Routines
.fi
.ke

In the typical \fIgetevents\fR call, QPIO will call PLIO to determine the
next region of the stored image (event list) to access, then if the event
data is not already in a data buffer, FIO is called to read the data (bucket),
using the event list index, an integer array valued parameter, to determine
what bucket to read.  The events in the bucket are then examined and optionally
filtered via calls to QPEX, returning pointers to the passed events in an
output argument.  This process terminates when either the mask value changes
or at least one event has been returned and a new bucket is required to 
continue reading.