mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 12:57:08 +00:00
189 lines
9.0 KiB
YAML
189 lines
9.0 KiB
YAML
# QC definition file
|
||
|
||
-
|
||
name: "Missing shots"
|
||
iterate: "sequences"
|
||
labels: [ "QCFail" ]
|
||
check: |
|
||
const sequence = currentItem;
|
||
const sp0 = Math.min(sequence.fsp, sequence.lsp);
|
||
const sp1 = Math.max(sequence.fsp, sequence.lsp);
|
||
const missing = preplots.filter(r => r.line == sequence.line &&
|
||
r.point >= sp0 && r.point <= sp1 &&
|
||
!sequence.shots.find(s => s.point == r.point)
|
||
);
|
||
|
||
missing.length == 0 || missing.map(r => `Missing shot: ${r.point}`).join("\n")
|
||
-
|
||
name: "Gun QC"
|
||
disabled: false
|
||
labels: [ "QCFail", "QCGuns" ]
|
||
children:
|
||
-
|
||
name: "Missing gun data"
|
||
check: |
|
||
!!currentItem._("raw_meta.smsrc.guns") || "Missing gun data"
|
||
|
||
-
|
||
name: "No fire"
|
||
check: |
|
||
const currentShot = currentItem;
|
||
const gunData = currentItem._("raw_meta.smsrc");
|
||
(gunData && gunData.num_nofire != 0)
|
||
? `Source ${gunData.src_number}: No fire (${gunData.num_nofire} guns)`
|
||
: true;
|
||
|
||
-
|
||
name: "Pressure errors"
|
||
check: |
|
||
const gunData = currentItem._("raw_meta.smsrc");
|
||
(gunData && Math.abs(gunData.manifold/parameters.gunPressureNominal - 1) > parameters.gunPressureToleranceRatio)
|
||
? `Source ${gunData.src_number}: Manifold pressure out of specification – ${gunData.manifold} / ${parameters.gunPressureNominal} = ${(Math.abs(gunData.manifold/parameters.gunPressureNominal - 1)*100).toFixed(1)}% > ${(parameters.gunPressureToleranceRatio*100).toFixed(1)}%`
|
||
: true;
|
||
|
||
-
|
||
name: "Single gun / cluster"
|
||
children:
|
||
-
|
||
name: "Source depth"
|
||
check: |
|
||
const currentShot = currentItem;
|
||
let _result_;
|
||
_depth=10;
|
||
const gunData = currentShot._("raw_meta.smsrc.guns");
|
||
if (!gunData) {
|
||
// We check for missing data elsewhere, so don't fail this test
|
||
_result_ = true
|
||
} else if (gunData.every(gun => Math.abs(gun[_depth]-parameters.gunDepth) <= parameters.gunDepthTolerance)) {
|
||
_result_ = true;
|
||
} else {
|
||
const bad_guns = gunData.filter(gun => Math.abs(gun[_depth]-parameters.gunDepth) > parameters.gunDepthTolerance).map(gun => {
|
||
return `source ${gun[2]}, string ${gun[0]}, gun ${gun[1]}, depth: ${gun[10]}`;
|
||
});
|
||
_result_ = `Depth error: ${bad_guns.join("; ")}`;
|
||
}
|
||
_result_
|
||
|
||
-
|
||
name: "Synchronisation (error)"
|
||
check: |
|
||
currentItem._ = (k) => k.split(".").reduce((a, b) => typeof a != "undefined" ? a[b] : a, currentItem);
|
||
const currentShot = currentItem;
|
||
const gunData = currentShot._("raw_meta.smsrc");
|
||
let result = [];
|
||
if (gunData && gunData.num_nofire == 0) {
|
||
|
||
// These are the indices into the gun array for the different
|
||
// values of interest.
|
||
const subarray = 0;
|
||
const aimpoint = 7;
|
||
const firetime = 8;
|
||
|
||
// We only care about the source which actually fired (or was supposed to)
|
||
const sourceFired = gunData.guns.filter(g => g[2] == gunData.src_number);
|
||
|
||
// Let us check if the average delta for each string is within spec
|
||
let subarrayAverages = [];
|
||
sourceFired.forEach(g => {
|
||
const idx = g[subarray]-1;
|
||
const delta = g[firetime]-g[aimpoint];
|
||
if (!subarrayAverages[idx]) {
|
||
subarrayAverages[idx] = [];
|
||
}
|
||
subarrayAverages[idx].push(delta);
|
||
});
|
||
subarrayAverages = subarrayAverages.map(s => s.reduce( (a, b) => a+b, 0 ) / s.length);
|
||
|
||
subarrayAverages.forEach((value, idx) => {
|
||
if (value > parameters.gunTimingSubarrayAverage) {
|
||
result.push(`Average delta error: string ${idx+1}: ${value.toFixed(2)} > ${parameters.gunTimingSubarrayAverage}`);
|
||
}
|
||
});
|
||
|
||
// Let us see about individual guns
|
||
sourceFired
|
||
.filter(gun => Math.abs(gun[firetime]-gun[aimpoint]) > parameters.gunTiming)
|
||
.forEach(gun => {
|
||
const value = Math.abs(gun[firetime]-gun[aimpoint]);
|
||
result.push(`Delta error: source ${gun[2]}, string ${gun[0]}, gun ${gun[1]}: ${value.toFixed(2)} > ${parameters.gunTiming}`);
|
||
});
|
||
}
|
||
if (result.length) {
|
||
result.join("; ");
|
||
} else {
|
||
// Either there were no error or gun data was missing, which we take care of elsewhere
|
||
true;
|
||
}
|
||
|
||
-
|
||
name: "Autofire"
|
||
check: |
|
||
const currentShot = currentItem;
|
||
let _result_;
|
||
_autofire=5;
|
||
const gunData = currentShot._("raw_meta.smsrc.guns");
|
||
if (!gunData) {
|
||
// We check for missing data elsewhere, so don't fail this test
|
||
_result_ = true;
|
||
} else if (gunData.every(gun => gun[_autofire] == false)) {
|
||
_result_ = true;
|
||
} else {
|
||
const bad_guns = gunData.filter(gun => gun[_autofire]).map(gun => {
|
||
return `source ${gun[2]}, string ${gun[0]}, gun ${gun[1]}, depth: ${gun[10]}`;
|
||
});
|
||
_result_ = `Depth error: ${bad_guns.join(";\n")}`;
|
||
}
|
||
_result_
|
||
|
||
-
|
||
name: "Centre of source preplot deviation (single shots)"
|
||
labels: [ "QCFail", "QCNav" ]
|
||
disabled: false
|
||
children:
|
||
-
|
||
name: "Crossline"
|
||
check: |
|
||
const currentShot = currentItem;
|
||
Math.abs(currentShot.error_i) <= parameters.crosslineError
|
||
|| `Crossline error: ${currentShot.error_i.toFixed(1)} > ${parameters.crosslineError}`
|
||
|
||
-
|
||
name: "Centre of source preplot deviation (moving average)"
|
||
labels: [ "QCFail", "QCNav" ]
|
||
children:
|
||
-
|
||
name: "Crossline"
|
||
iterate: "sequences"
|
||
parameters: [ "crosslineErrorAverage" ]
|
||
check: |
|
||
const currentSequence = currentItem;
|
||
const i_err = currentSequence.shots.filter(s => s.error_i != null).map(a => a.error_i);
|
||
if (i_err.length) {
|
||
const avg = i_err.reduce( (a, b) => a+b)/i_err.length;
|
||
avg <= parameters.crosslineErrorAverage ||
|
||
`Average crossline error: ${avg.toFixed(1)} > ${parameters.crosslineErrorAverage}`
|
||
} else {
|
||
`Sequence ${currentSequence.sequence} has no shots within preplot`
|
||
}
|
||
|
||
-
|
||
name: "Inline"
|
||
iterate: "sequences"
|
||
parameters: [ "inlineErrorRunningAverageShots" ]
|
||
check: |
|
||
const currentSequence = currentItem;
|
||
const n = parameters.inlineErrorRunningAverageShots; // For brevity
|
||
const results = currentSequence.shots.slice(n/2, -n/2).map( (shot, index) => {
|
||
const shots = currentSequence.shots.slice(index, index+n).map(i => i.error_j).filter(i => i !== null);
|
||
if (!shots.length) {
|
||
// We are outside the preplot
|
||
// Nothing to see here, move along
|
||
return true;
|
||
}
|
||
const mean = shots.reduce( (a, b) => a+b ) / shots.length;
|
||
return Math.abs(mean) <= parameters.inlineErrorRunningAverageValue ||
|
||
`Running average inline error: shot ${shot.point}, ${mean.toFixed(1)} > ${parameters.inlineErrorRunningAverageValue}`
|
||
}).filter(i => i !== true);
|
||
|
||
results.length == 0 || results.join("\n");
|