-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathexample.js
More file actions
359 lines (295 loc) · 15 KB
/
example.js
File metadata and controls
359 lines (295 loc) · 15 KB
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
const specutils = require('./SpecUtilsJS.node');
const fs = require('fs');
/* This script demonstrates parsing a spectrum file, printing out information about
the file, then looping over all records in the file and printing out information
about them.
Usage:
node example.js <input_spectrum_file> [output.n42]
If an output path is given and the file does not already exist, writes an N42-2012 file.
*/
const args = process.argv.slice(2);
if (args.length < 1) {
console.error("Usage: node example.js <input_spectrum_file> [output.n42]");
process.exit(1);
}
const inputfile = args[0];
const outputPath = args.length >= 2 ? args[1] : null;
if (!fs.existsSync(inputfile)) {
console.error("Error: File not found: " + inputfile);
process.exit(1);
}
let spec;
try
{
spec = new specutils.SpecFile( inputfile );
}catch(e)
{
//If the file couldnt be parsed as a spectrum file will always throw exception.
console.error( "Couldnt open file '" + inputfile + "' as a spectrum file." );
process.exit(1);
}
console.log( "Opened '" + spec.filename() + "' as a spectrum file." );
/* Sum of live time over all gamma measurements (SpecRecord objects) in the file */
console.log( "Sum Gamma Live Time = " + spec.gammaLiveTime() + " seconds" );
/* Sum of real time over all gamma measurements (SpecRecord objects) in the file */
console.log( "Sum Gamma Real Time = " + spec.gammaRealTime() + " seconds" );
/* Sum of all gamma counts in the file */
console.log( "Gamma Count Sum = " + spec.gammaCountSum() );
/* Print out if spectrum file contained neutron information, and if so the total number of counts. */
if( spec.containedNeutrons() )
console.log( "Neutron Count Sum = " + spec.neutronCountSum() );
else
console.log( "Did not contain neutron data" );
/* The number of gamma channels in the data. Some files may have measurements with
different number of channels, in which case this function returns the first
measurement found with non-zero number of channels.
*/
console.log( "Num Gamma Channels = " + spec.numGammaChannels() );
/* The instrument type as infered from the spectrum file format and meta-information.
This is the first function you should use to determine detector type, as it is
generally the most reliable, at least for the detectors the SpecUtils library
knows about (that is to say the models wcjohns has bothered to explicitly
identify so far)
Currently this function returns one of a fixed number of strings, but in the
future would like to make it an enum. Current possible strings:
[ "Unknown", "GR135", "IdentiFINDER", "IdentiFINDER-NG", "IdentiFINDER-LaBr3",
"Detective", "Detective-EX", "Detective-EX100", "Detective-EX200", "Detective X",
"SAIC8", "Falcon 5000", "MicroDetective", "MicroRaider", "SAM940", "SAM940LaBr3",
"SAM945", "SRPM-210", "RS-701", "RS-705", "RadHunterNaI", "RadHunterLaBr3",
"RSI-Unspecified", "RadEagle NaI 3x1", "RadEagle CeBr3 2x1", "RadEagle CeBr3 3x0.8",
"RadEagle LaBr3 2x1" ]
*/
console.log( "Inferred Instrument Type = " + spec.inferredInstrumentModel() );
/* Instrument Type defined by N42 spec. This function will return value specified
by the file itself, or if value can be infered from other info in the file.
Some example strings it may return are: "PortalMonitor", "SpecPortal",
"RadionuclideIdentifier", "PersonalRadiationDetector", "SurveyMeter",
"Spectrometer", "Other" or empty string.
*/
console.log( "Instrument Type = " + spec.instrumentType() );
/* The manufacturer of the instrument. Either as reported by the file, or infered
from the file format. May be blank if could not be determined.
*/
console.log( "Manufacturer = " + spec.manufacturer() );
/* The instrument model, either as reported by the file, or infered from the format.
May be blank if could not be determined. Different file formats output from the same
detector may give different values for this field, or for some formats but not others.
Similarly differnt firmware versions of the same detector may output different values.
*/
console.log( "Instrument Model = " + spec.instrumentModel() );
/* Serial number reported in file. Blank if not recorded in file. */
console.log( "Serial Number = '" + spec.serialNumber() + "'" );
/* If data was recorded in a successesion of short time intervals. Function will
return true for portals since they record 0.1 second time slices one after another.
Search detector systems will also often times record time slices continually one
after another.
*/
if( spec.isSearchMode() )
console.log( "Was either search-mode data, or portal data." );
/* Some files hold data from multiple gamma detection elements, in which case each
detection element will get a different name, either indicated in the file, or
assigned during file parsing. Examples are "Aa1", "Ba2", "gamma" etc.
*/
const detNames = spec.detectorNames();
if( detNames.length == 1 )
console.log( "There was a single detector" );
else
console.log( "There was " + detNames.length + " detectors, with names: " + detNames );
//Remarks are an array of strings.
//const remarks = spec.remarks();
/* Print if file contained GPS information, and if so the mean location averaged
over all measurements that ha GPS data.
*/
if( spec.hasGpsInfo() )
console.log( "Median GPS lat,lon: " + spec.meanLatitude() + "," + spec.meanLongitude() );
else
console.log( "No GPS information available." );
/* Get RIID analysis results, if included in the file. */
let ana = spec.riidAnalysis();
if( ana ){
console.log( "RIID Analysis Results Info:" );
/* String giving algorithm name, or null if not provided in file. */
let algoName = ana.algorithmName();
if( algoName )
console.log( "\tAlgorthm Name: " + algoName );
/* Creator of analysis algorithm. Null if not provided in the file. */
let creator = ana.algorithmCreator();
if( creator )
console.log( "\tAlgorithm Creator: " + creator );
/* Algorithm description (as String) if provided in file, null otherwise. */
let algoDescrip = ana.algorithmDescription();
if( algoDescrip )
console.log( "\tAlgorthm Description: " + algoDescrip );
/** Result description (as String) if provided in file, null otherwise. */
let resultDescrip = ana.algorithmResultDescription();
if( resultDescrip )
console.log( "\tResult Description: " + resultDescrip );
/* Array of Strings representing remarks about RIID analysis given in spectrum
file, or null if none are provided.
*/
let remarks = ana.remarks();
if( remarks )
console.log( "\tRemarks: " + remarks );
/* Returns array of RiidAnaResult objects contained in this analysis. */
let nuclides = ana.results();
for( let i = 0; i < nuclides.length; ++i)
{
/* Get the i'th RiidAnaResult */
let result = nuclides[i];
/* Get nuclide (as String); may be null if not provided in the file (ex. if
dose rate is provided instead)
*/
let nuc = result.nuclide();
/* Get String giving type of nuclide, usually somethign like "Industrial",
"Medical", etc. Will be null when not provided in the spectrum file.
*/
let nuc_type = result.nuclideType();
/* String describing nuclide confidence. May be a number (ex. "9"),
a word (ex "High"), a letter (ex 'L'), or a phrase. Will be null if not
provided in file
*/
let id_conf = result.idConfidence();
/* String giving remark, or null if one was not provided in spectrum file. */
let remark = result.remark();
/* Returns Number giving dose rate in micro-sievert, or null if not avaialble. */
let doseRate = result.doseRate();
/* Get the name (as a String) of the detector this result corresponds to.
If null or blank then you should assum it is for all detectors in the file.
*/
let detName = result.detector();
console.log( "\t\tResult " + i + ":"
+ " nuc=" + (nuc ? nuc : "NotSpecified")
+ ", nuc_type=" + (nuc_type ? nuc_type : "NotSpecified")
+ (id_conf ? ", Confidence="+id_conf : "NotSpecified")
+ (remark ? ", Remark="+remark : "")
+ (doseRate ? ", DoseRate="+doseRate+"uSv" : "")
+ (detName ? ", Detector="+detName : "")
);
}//for( let i = 0; i < nuclides.length; ++i)
}else{
console.log( "No RIID analysis results given in file." );
}
console.log( "Total Number Spectrum Records = " + spec.numSpecRecords() );
/* Lets get an array of SpecRecord objects and loop over them.
We can optionally filter what SpecRecord's are returned. We can filter
by detector names, sample numbers, and source types. For any of these three
quantities if null is passed in, no filtering will be done on that quantity.
*/
let wantedDetectors = spec.detectorNames(); //Equivalent to passing null for wantedDetectors
let wantedSamples = spec.sampleNumbers(); //Equivalent to passing null for wantedSamples
let wantedSourceTypes = ["Background", "Calibration", "Foreground", "IntrinsicActivity", "UnknownSourceType"]; //Equivalent to null
/* The SourceType strings are also defined as static class values like: */
//wantedSourceTypes = [specutils.SourceType.Background, specutils.SourceType.Calibration, ...];
let records = spec.records( wantedDetectors, wantedSamples, wantedSourceTypes );
for( let i = 0; i < records.length; ++i)
{
/* A SpecRecord object represents a measurement from a single detection element; e.g., a spectrum, or a neutron count.
If a gamma and neutron detector are unambigously grouped together, the record may contain both gamma spectrum and neutron counts.
*/
let record = records[i];
console.log( "\tRecord " + i + " (SampleNumber " + record.sampleNumber() + " DetectorName '" + record.detectorName() + "'):" );
/* sourceType() will be one of the following strings: ["IntrinsicActivity",
"Calibration", "Background", "Foreground", "UnknownSourceType"]
(Defined by static members of the SourceType class)
occupied() will be one of the following strings: ["NotOccupied", "Occupied",
"UnknownOccupancyStatus"]
(Defined by static members of the OccupancyStatus class)
*/
console.log( "\t\tSource Type=" + record.sourceType() + ", OccStatus=" + record.occupied() );
/* The sample number together with either the detector name or number is a unique combination
that identifies this record within the spectrum file.
All records with the same sample number represent data from the same time period.
A file may contain data from multiple detector elements.
*/
console.log( "\t\tDetName=" + record.detectorName() + ", DetNum=" + record.detectorNumber() + ", SampleNum=" + record.sampleNumber() );
console.log( "\t\tLT=" + record.liveTime() + " s, RT=" + record.realTime() + ", StartTime=" + (new Date(record.startTime())) );
console.log( "\t\tHasNeutron=" + record.containedNeutron() + ", SumNeutrons=" + record.neutronCountsSum() );
if( record.hasGpsInfo() )
console.log( "\t\tLatitude=" + record.latitude() + ", Longitude=" + record.longitude() + ", fix at " + (new Date(record.positionTime())) );
/* If you care about the underlying energy calibration, you can access the
info given in the file using:
*/
console.log( "\t\tEnergy calibration of type " + record.energyCalibrationModel()
+ " with coefficients: " + record.energyCalibrationCoeffs()
+ " and deviation pairs: " + record.deviationPairs() );
/* If you only care about the actual energies of the gamma channels, you can
instead get an array of Numbers giving the lower energies for each channel.
Will return null if record does not have gamma data (e.g., only neutron)
*/
console.log( "\t\tGamma Channel Energies: " + record.gammaChannelEnergies() );
/* Get array of Numbers giving the channel counts for each channel.
Will return null if record does not have gamma data (e.g., only neutron)
*/
console.log( "\t\tGamma Channel Counts: " + record.gammaChannelContents() );
}//for( loop over records )
/* You can also sum specified measurements into a SpecRecord.
This is useful, for example, to display a single spectrum to users of all
foreground measurements in a file.
*/
try
{
/* Filtering for SpecFile.sumRecords() is same as for SpecFile.records() */
let summedForeground = spec.sumRecords(null,null,["Foreground", "UnknownSourceType"]);
console.log( "All foreground (and unknown SourceType) spectrum have summed LiveTime=" + summedForeground.liveTime() + "s" );
}catch(e)
{
console.log( "Couldnt sum foreground records: " + e );
}
try
{
let summedBackground = spec.sumRecords(null,null,"Background");
console.log( "All background spectra have summed LiveTime=" + summedBackground.liveTime() + "s" );
}catch(e)
{
console.log( "Couldnt sum background records (likely there were no background): " + e );
}
/* If you are looking to make some plots to show to users as a preview of file
contents, you should handle a different possible types of input files.
*/
if( spec.isSearchMode() ){
/* The detector(s) took data in successive time intervals; for example there
are many 0.5 second spectra, one taken immediately after the other.
*/
let backgroundSampleNums = spec.sampleNumbers('Background');
let foregroundSampleNums = spec.sampleNumbers(['Foreground','UnknownSourceType']);
try{
let sumFore = spec.sumRecords(null,foregroundSampleNums);
console.log( 'Would plot search-mode data as all foreground spectra plotted together');
//plotForeground( sumFore.gammaChannelContents(), sumFore.gammaChannelEnergies() );
}catch(e){
console.log( 'Would not plot search-mode datas foreground - this probably shouldnt happen' );
}
try{
let sumBack = spec.sumRecords(null,backgroundSampleNums);
console.log( 'Would plot search-mode data background summed together');
//plotBackground( sumBack.gammaChannelContents(), sumBack.gammaChannelEnergies() );
}catch(e){
console.log( 'Would not plot search-mode datas background - maybe file didnt specify this.' );
}
} else {
//If we are here, we may have:
// - File contained a single spectrum.
// - File contained a foreground, a background, maybe an intrinsic activity spectrum
// - File contained multiple foregrounds, and zero, one, or many background samples.
// - You probably shouldnt sum the foregrounds together for plotting - they may be distinctly different measurements
let foregroundSampleNums = spec.sampleNumbers(['Foreground','UnknownSourceType']);
let backgroundSampleNums = spec.sampleNumbers('Background');
console.log( 'Would give user option to scroll through ' + foregroundSampleNums.length
+ ' foreground spectra, and would sum ' + backgroundSampleNums.length + ' spectra for the background' );
}
// Write N42-2012 if an output path was provided
if( outputPath ){
if( fs.existsSync(outputPath) ){
console.error( "\nOutput file already exists, not overwriting: " + outputPath );
process.exit(1);
}
try
{
spec.writeToFile( outputPath, "N42-2012" );
console.log( "\nWrote N42-2012: " + outputPath );
}catch( err )
{
console.error( "\nError writing N42 file: " + err );
process.exit(1);
}
}