nmr-processing 9.3.7 → 9.3.8-pre.1681135907
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/apodization/apodization.d.ts +9 -4
- package/lib/apodization/apodization.js +3 -1
- package/lib/apodization/apodization.js.map +1 -1
- package/lib/apodization/applyWindow.js.map +1 -1
- package/lib/apodization/compose.js.map +1 -1
- package/lib/apodization/getFunction.js +3 -0
- package/lib/apodization/getFunction.js.map +1 -1
- package/lib/apodization/shapes/WindowFunctions.d.ts +6 -1
- package/lib/apodization/shapes/sineBell.d.ts +27 -0
- package/lib/apodization/shapes/sineBell.js +17 -0
- package/lib/apodization/shapes/sineBell.js.map +1 -0
- package/lib/apodization/utils/getData.js.map +1 -1
- package/lib/signals/signalsToFID.d.ts +3 -0
- package/lib/utilities/checkData2DFid.d.ts +3 -0
- package/lib/utilities/checkData2DFid.js +10 -0
- package/lib/utilities/checkData2DFid.js.map +1 -0
- package/lib/xyz/Data2D.d.ts +21 -0
- package/lib/xyz/Data2D.js +3 -0
- package/lib/xyz/Data2D.js.map +1 -0
- package/lib/xyz/quadrature.d.ts +8 -0
- package/lib/xyz/quadrature.js +54 -0
- package/lib/xyz/quadrature.js.map +1 -0
- package/lib/xyz/util/fft2d/digitalFilter.d.ts +7 -0
- package/lib/xyz/util/fft2d/digitalFilter.js +20 -0
- package/lib/xyz/util/fft2d/digitalFilter.js.map +1 -0
- package/lib/xyz/util/fft2d/fftDirectDimension.d.ts +24 -0
- package/lib/xyz/util/fft2d/fftDirectDimension.js +57 -0
- package/lib/xyz/util/fft2d/fftDirectDimension.js.map +1 -0
- package/lib/xyz/util/fft2d/fftIndirectDimension.d.ts +23 -0
- package/lib/xyz/util/fft2d/fftIndirectDimension.js +72 -0
- package/lib/xyz/util/fft2d/fftIndirectDimension.js.map +1 -0
- package/lib/xyz/util/fft2d/removeDCOffset.d.ts +5 -0
- package/lib/xyz/util/fft2d/removeDCOffset.js +19 -0
- package/lib/xyz/util/fft2d/removeDCOffset.js.map +1 -0
- package/lib/xyz/util/fft2d/zeroFilling.d.ts +10 -0
- package/lib/xyz/util/fft2d/zeroFilling.js +29 -0
- package/lib/xyz/util/fft2d/zeroFilling.js.map +1 -0
- package/lib/xyz/xyzBidimensionalFFT.d.ts +41 -0
- package/lib/xyz/xyzBidimensionalFFT.js +79 -0
- package/lib/xyz/xyzBidimensionalFFT.js.map +1 -0
- package/lib-esm/apodization/apodization.js +3 -1
- package/lib-esm/apodization/apodization.js.map +1 -1
- package/lib-esm/apodization/applyWindow.js.map +1 -1
- package/lib-esm/apodization/compose.js.map +1 -1
- package/lib-esm/apodization/getFunction.js +3 -0
- package/lib-esm/apodization/getFunction.js.map +1 -1
- package/lib-esm/apodization/shapes/sineBell.js +13 -0
- package/lib-esm/apodization/shapes/sineBell.js.map +1 -0
- package/lib-esm/apodization/utils/getData.js.map +1 -1
- package/lib-esm/utilities/checkData2DFid.js +6 -0
- package/lib-esm/utilities/checkData2DFid.js.map +1 -0
- package/lib-esm/xyz/Data2D.js +2 -0
- package/lib-esm/xyz/Data2D.js.map +1 -0
- package/lib-esm/xyz/quadrature.js +50 -0
- package/lib-esm/xyz/quadrature.js.map +1 -0
- package/lib-esm/xyz/util/fft2d/digitalFilter.js +16 -0
- package/lib-esm/xyz/util/fft2d/digitalFilter.js.map +1 -0
- package/lib-esm/xyz/util/fft2d/fftDirectDimension.js +50 -0
- package/lib-esm/xyz/util/fft2d/fftDirectDimension.js.map +1 -0
- package/lib-esm/xyz/util/fft2d/fftIndirectDimension.js +65 -0
- package/lib-esm/xyz/util/fft2d/fftIndirectDimension.js.map +1 -0
- package/lib-esm/xyz/util/fft2d/removeDCOffset.js +15 -0
- package/lib-esm/xyz/util/fft2d/removeDCOffset.js.map +1 -0
- package/lib-esm/xyz/util/fft2d/zeroFilling.js +25 -0
- package/lib-esm/xyz/util/fft2d/zeroFilling.js.map +1 -0
- package/lib-esm/xyz/xyzBidimensionalFFT.js +75 -0
- package/lib-esm/xyz/xyzBidimensionalFFT.js.map +1 -0
- package/package.json +17 -17
- package/src/apodization/apodization.ts +6 -4
- package/src/apodization/applyWindow.ts +0 -1
- package/src/apodization/compose.ts +1 -0
- package/src/apodization/getFunction.ts +3 -0
- package/src/apodization/shapes/WindowFunctions.ts +7 -1
- package/src/apodization/shapes/sineBell.ts +41 -0
- package/src/apodization/utils/getData.ts +0 -1
- package/src/utilities/checkData2DFid.ts +10 -0
- package/src/xyz/Data2D.ts +25 -0
- package/src/xyz/quadrature.ts +69 -0
- package/src/xyz/util/fft2d/digitalFilter.ts +24 -0
- package/src/xyz/util/fft2d/fftDirectDimension.ts +111 -0
- package/src/xyz/util/fft2d/fftIndirectDimension.ts +123 -0
- package/src/xyz/util/fft2d/removeDCOffset.ts +19 -0
- package/src/xyz/util/fft2d/zeroFilling.ts +39 -0
- package/src/xyz/xyzBidimensionalFFT.ts +123 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nmr-processing",
|
|
3
|
-
"version": "9.3.
|
|
3
|
+
"version": "9.3.8-pre.1681135907",
|
|
4
4
|
"description": "Pure functions allowing to process NMR spectra.",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"module": "./lib-esm/index.js",
|
|
@@ -38,20 +38,20 @@
|
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://github.com/cheminfo/nmr-processing#readme",
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@types/jest": "^29.
|
|
42
|
-
"cheminfo-build": "^1.
|
|
41
|
+
"@types/jest": "^29.5.0",
|
|
42
|
+
"cheminfo-build": "^1.2.0",
|
|
43
43
|
"cheminfo-types": "^1.4.0",
|
|
44
|
-
"eslint": "^8.
|
|
45
|
-
"eslint-config-cheminfo-typescript": "^11.
|
|
46
|
-
"jest": "^29.
|
|
44
|
+
"eslint": "^8.36.0",
|
|
45
|
+
"eslint-config-cheminfo-typescript": "^11.3.1",
|
|
46
|
+
"jest": "^29.5.0",
|
|
47
47
|
"jest-matcher-deep-close-to": "^3.0.2",
|
|
48
48
|
"md5": "^2.3.0",
|
|
49
49
|
"nmr-xy-testdata": "^0.5.1",
|
|
50
|
-
"openchemlib": "^8.0
|
|
51
|
-
"prettier": "^2.8.
|
|
52
|
-
"rimraf": "^4.1
|
|
53
|
-
"ts-jest": "^29.0.
|
|
54
|
-
"typescript": "^
|
|
50
|
+
"openchemlib": "^8.2.0",
|
|
51
|
+
"prettier": "^2.8.7",
|
|
52
|
+
"rimraf": "^4.4.1",
|
|
53
|
+
"ts-jest": "^29.0.5",
|
|
54
|
+
"typescript": "^5.0.2"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@lukeed/uuid": "^2.0.0",
|
|
@@ -60,12 +60,12 @@
|
|
|
60
60
|
"form-data": "^4.0.0",
|
|
61
61
|
"gyromagnetic-ratio": "^1.0.0",
|
|
62
62
|
"is-any-array": "^2.0.0",
|
|
63
|
-
"linear-sum-assignment": "^1.0.
|
|
63
|
+
"linear-sum-assignment": "^1.0.5",
|
|
64
64
|
"ml-array-mean": "^1.1.6",
|
|
65
65
|
"ml-array-rescale": "^1.3.7",
|
|
66
66
|
"ml-array-sum": "^1.1.6",
|
|
67
|
-
"ml-direct": "^0.1.
|
|
68
|
-
"ml-gsd": "^12.1.
|
|
67
|
+
"ml-direct": "^0.1.3",
|
|
68
|
+
"ml-gsd": "^12.1.3",
|
|
69
69
|
"ml-hclust": "^3.1.0",
|
|
70
70
|
"ml-levenberg-marquardt": "^4.1.0",
|
|
71
71
|
"ml-matrix": "^6.10.4",
|
|
@@ -74,11 +74,11 @@
|
|
|
74
74
|
"ml-peak-shape-generator": "^4.1.2",
|
|
75
75
|
"ml-simple-clustering": "^0.1.0",
|
|
76
76
|
"ml-sparse-matrix": "^2.1.0",
|
|
77
|
-
"ml-spectra-processing": "^
|
|
77
|
+
"ml-spectra-processing": "^12.0.0",
|
|
78
78
|
"ml-tree-set": "^0.1.1",
|
|
79
79
|
"nmr-correlation": "^2.3.3",
|
|
80
80
|
"nmredata": "^0.9.2",
|
|
81
|
-
"openchemlib-utils": "^2.
|
|
82
|
-
"spectrum-generator": "^8.0.
|
|
81
|
+
"openchemlib-utils": "^2.4.0",
|
|
82
|
+
"spectrum-generator": "^8.0.7"
|
|
83
83
|
}
|
|
84
84
|
}
|
|
@@ -10,23 +10,25 @@ export interface DataReIm {
|
|
|
10
10
|
im: DoubleArray;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
interface ApodizationOptions {
|
|
13
|
+
export interface ApodizationOptions {
|
|
14
14
|
/**
|
|
15
15
|
* number of points at the end of the shape to apply the window function inverted
|
|
16
16
|
* @default 0
|
|
17
17
|
*/
|
|
18
18
|
pointsToShift?: number;
|
|
19
|
-
compose
|
|
19
|
+
compose?: ShapeOptions;
|
|
20
20
|
}
|
|
21
|
-
export function apodization(data: DataReIm, options: ApodizationOptions) {
|
|
21
|
+
export function apodization(data: DataReIm, options: ApodizationOptions = {}) {
|
|
22
22
|
const { compose: composeOptions, pointsToShift } = options;
|
|
23
|
+
|
|
24
|
+
if (!composeOptions) return { ...data, windowData: [] };
|
|
25
|
+
|
|
23
26
|
const windowData = compose(composeOptions);
|
|
24
27
|
|
|
25
28
|
const applyWindowOptions = {
|
|
26
29
|
windowData,
|
|
27
30
|
pointsToShift,
|
|
28
31
|
};
|
|
29
|
-
|
|
30
32
|
const re = applyWindow(data.re, applyWindowOptions);
|
|
31
33
|
const im = applyWindow(data.im, applyWindowOptions);
|
|
32
34
|
|
|
@@ -42,7 +42,6 @@ export function applyWindow(data: DoubleArray, options: ApplyWindowOptions) {
|
|
|
42
42
|
for (let i = start, j = 0; i < firstEndPoint; i++) {
|
|
43
43
|
output[i] *= windowData[j++];
|
|
44
44
|
}
|
|
45
|
-
|
|
46
45
|
for (let i = dataLength - 1, j = 0; i > dataLength - pointsToShift - 1; i--) {
|
|
47
46
|
output[i] *= windowData[j++];
|
|
48
47
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { WindowFunctions } from './shapes/WindowFunctions';
|
|
2
2
|
import { exponential } from './shapes/exponential';
|
|
3
3
|
import { lorentzToGauss } from './shapes/lorentzToGauss';
|
|
4
|
+
import { sineBell } from './shapes/sineBell';
|
|
4
5
|
|
|
5
6
|
export function getFunction(shape: WindowFunctions) {
|
|
6
7
|
const { kind, options } = shape;
|
|
@@ -9,6 +10,8 @@ export function getFunction(shape: WindowFunctions) {
|
|
|
9
10
|
return exponential(options);
|
|
10
11
|
case 'lorentzToGauss':
|
|
11
12
|
return lorentzToGauss(options);
|
|
13
|
+
case 'sineBell':
|
|
14
|
+
return sineBell(options);
|
|
12
15
|
default:
|
|
13
16
|
throw Error(`Unknown distribution ${kind as string}`);
|
|
14
17
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExponentialOptions } from './exponential';
|
|
2
2
|
import type { LorentzToGaussOptions } from './lorentzToGauss';
|
|
3
|
+
import { SineBellOptions } from './sineBell';
|
|
3
4
|
|
|
4
5
|
interface Exponential {
|
|
5
6
|
kind: 'exponential';
|
|
@@ -11,4 +12,9 @@ interface LorentToGauss {
|
|
|
11
12
|
options: LorentzToGaussOptions;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
interface SineBell {
|
|
16
|
+
kind: 'sineBell';
|
|
17
|
+
options: SineBellOptions;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type WindowFunctions = Exponential | LorentToGauss | SineBell;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface SineBellOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Specifies the exponent of the sine-bell; Non-integer values are allowed.
|
|
4
|
+
* Common values are 1.0 (for ordinary sine-bell) and 2.0 (for squared-bell functions).
|
|
5
|
+
* @default 1
|
|
6
|
+
*/
|
|
7
|
+
exponent?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Specifies the starting point of the sine-bell in units of pi radians.
|
|
10
|
+
* Common values are 0.0 (for a sine window which starts height at 0.0) and
|
|
11
|
+
* 0.5 (for a cosine window, which starts at height 1.0).
|
|
12
|
+
* @default 0
|
|
13
|
+
*/
|
|
14
|
+
offset?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Specifies the ending point of the sine-bell in units of pi radians.
|
|
17
|
+
* Common values are 1.0 (for a window which goes to 0.0 height at the last point) and
|
|
18
|
+
* 0.95 (for a window which doesn't go all the way to 0.0).
|
|
19
|
+
* @default 1
|
|
20
|
+
*/
|
|
21
|
+
end?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Specifies the number of points in the window function.
|
|
24
|
+
*/
|
|
25
|
+
length: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function sineBell(options: SineBellOptions) {
|
|
29
|
+
const { exponent = 1, offset = 0, end = 1, length } = options;
|
|
30
|
+
|
|
31
|
+
if (length === undefined) {
|
|
32
|
+
throw new Error('length options should exists for sineBell shape');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (end < 0 || end > 1) {
|
|
36
|
+
throw new Error(`the end parameter should be [0-1]`);
|
|
37
|
+
}
|
|
38
|
+
const c1 = Math.PI * offset;
|
|
39
|
+
const c2 = (Math.PI * (end - offset)) / (length - 1);
|
|
40
|
+
return (i: number) => Math.pow(Math.sin(c1 + c2 * i), exponent);
|
|
41
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Data2D, Data2DFid } from '../xyz/Data2D';
|
|
2
|
+
|
|
3
|
+
export type Data2DFidComplete = Required<Data2DFid>;
|
|
4
|
+
export function checkData2DFid(
|
|
5
|
+
data: Data2D,
|
|
6
|
+
): asserts data is Data2DFidComplete {
|
|
7
|
+
if (!('re' in data && 'im' in data)) {
|
|
8
|
+
throw new Error('imaginary (im) data should exists');
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { DoubleArray } from 'cheminfo-types';
|
|
2
|
+
|
|
3
|
+
export interface MinMaxContent {
|
|
4
|
+
z: DoubleArray[];
|
|
5
|
+
minX: number;
|
|
6
|
+
minY: number;
|
|
7
|
+
minZ: number;
|
|
8
|
+
maxX: number;
|
|
9
|
+
maxY: number;
|
|
10
|
+
maxZ: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type Data2D = Data2DFid | Data2DFt;
|
|
14
|
+
|
|
15
|
+
export interface Data2DFid {
|
|
16
|
+
re: MinMaxContent;
|
|
17
|
+
im?: MinMaxContent;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Data2DFt {
|
|
21
|
+
rr: MinMaxContent;
|
|
22
|
+
ri?: MinMaxContent;
|
|
23
|
+
ir?: MinMaxContent;
|
|
24
|
+
ii?: MinMaxContent;
|
|
25
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { DoubleArray } from 'cheminfo-types';
|
|
2
|
+
import { xAdd, xMultiply, xSubtract } from 'ml-spectra-processing';
|
|
3
|
+
|
|
4
|
+
import { checkData2DFid, Data2DFidComplete } from '../utilities/checkData2DFid';
|
|
5
|
+
import { Data2D } from './Data2D';
|
|
6
|
+
|
|
7
|
+
export function quadrature(data: Data2D, options: { fnMode: string }) {
|
|
8
|
+
checkData2DFid(data);
|
|
9
|
+
|
|
10
|
+
const { fnMode } = options;
|
|
11
|
+
|
|
12
|
+
switch (fnMode) {
|
|
13
|
+
case 'ttpi':
|
|
14
|
+
return ttpiQuadrature(data);
|
|
15
|
+
case 'states':
|
|
16
|
+
return shrQuadrature(data);
|
|
17
|
+
case 'echo':
|
|
18
|
+
return echoQuadrature(data);
|
|
19
|
+
default:
|
|
20
|
+
return {
|
|
21
|
+
re: data.re.z,
|
|
22
|
+
im: data.im.z,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function echoQuadrature(data: Data2DFidComplete) {
|
|
28
|
+
const {
|
|
29
|
+
re: { z: reMatrix },
|
|
30
|
+
im: { z: imMatrix },
|
|
31
|
+
} = data;
|
|
32
|
+
|
|
33
|
+
const newRe: DoubleArray[] = [];
|
|
34
|
+
const newIm: DoubleArray[] = [];
|
|
35
|
+
for (let i = 0; i < reMatrix.length; i += 2) {
|
|
36
|
+
let reCosine = reMatrix[i];
|
|
37
|
+
let imCosine = imMatrix[i];
|
|
38
|
+
let reSine = reMatrix[i + 1];
|
|
39
|
+
let imSine = imMatrix[i + 1];
|
|
40
|
+
|
|
41
|
+
newRe.push(xSubtract(xMultiply(imSine, -1), imCosine));
|
|
42
|
+
newRe.push(xSubtract(reSine, reCosine));
|
|
43
|
+
newIm.push(xAdd(reSine, reCosine));
|
|
44
|
+
newIm.push(xSubtract(imSine, imCosine));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { re: newRe, im: newIm };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function shrQuadrature(data: Data2DFidComplete) {
|
|
51
|
+
const { re, im } = data;
|
|
52
|
+
return { re: re.z, im: im.z };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function ttpiQuadrature(data: Data2DFidComplete) {
|
|
56
|
+
const newRe: DoubleArray[] = [];
|
|
57
|
+
const newIm: DoubleArray[] = [];
|
|
58
|
+
|
|
59
|
+
const {
|
|
60
|
+
re: { z: reMatrix },
|
|
61
|
+
im: { z: imMatrix },
|
|
62
|
+
} = data;
|
|
63
|
+
for (let i = 0; i < reMatrix.length; i++) {
|
|
64
|
+
newRe.push(i % 2 ? reMatrix[i].slice() : xMultiply(reMatrix[i], -1));
|
|
65
|
+
newIm.push(i % 2 ? imMatrix[i].slice() : xMultiply(imMatrix[i], -1));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { re: newRe, im: newIm };
|
|
69
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { DataReIm } from 'ml-spectra-processing';
|
|
2
|
+
|
|
3
|
+
export function digitalFilter(
|
|
4
|
+
data: DataReIm,
|
|
5
|
+
options: { digitalFilterValue: number },
|
|
6
|
+
) {
|
|
7
|
+
let { digitalFilterValue = 0 } = options;
|
|
8
|
+
let re = new Float64Array(data.re);
|
|
9
|
+
let im = new Float64Array(data.im);
|
|
10
|
+
|
|
11
|
+
let pointsToShift = Math.floor(digitalFilterValue);
|
|
12
|
+
|
|
13
|
+
const skip = 0;
|
|
14
|
+
pointsToShift += 0;
|
|
15
|
+
|
|
16
|
+
const newRe = new Float64Array(re.length);
|
|
17
|
+
const newIm = new Float64Array(im.length);
|
|
18
|
+
newRe.set(re.slice(pointsToShift));
|
|
19
|
+
newRe.set(re.slice(skip, pointsToShift), re.length - pointsToShift);
|
|
20
|
+
newIm.set(im.slice(pointsToShift));
|
|
21
|
+
newIm.set(im.slice(skip, pointsToShift), im.length - pointsToShift);
|
|
22
|
+
|
|
23
|
+
return { re: newRe, im: newIm };
|
|
24
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { DoubleArray } from 'cheminfo-types';
|
|
2
|
+
import Matrix from 'ml-matrix';
|
|
3
|
+
import {
|
|
4
|
+
reimAutoPhaseCorrection,
|
|
5
|
+
reimFFT,
|
|
6
|
+
reimPhaseCorrection,
|
|
7
|
+
} from 'ml-spectra-processing';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
apodization,
|
|
11
|
+
ApodizationOptions,
|
|
12
|
+
} from '../../../apodization/apodization';
|
|
13
|
+
|
|
14
|
+
import { digitalFilter } from './digitalFilter';
|
|
15
|
+
import { removeDCOffset } from './removeDCOffset';
|
|
16
|
+
import { zeroFilling, ZeroFillingOptions } from './zeroFilling';
|
|
17
|
+
|
|
18
|
+
interface PhaseCorrection {
|
|
19
|
+
mode?: string;
|
|
20
|
+
ph0?: number;
|
|
21
|
+
ph1?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface DirectDimensionOptions {
|
|
25
|
+
fnMode?: string;
|
|
26
|
+
digitalFilterValue?: number;
|
|
27
|
+
zeroFilling?: ZeroFillingOptions;
|
|
28
|
+
apodization?: ApodizationOptions;
|
|
29
|
+
phaseCorrection?: PhaseCorrection;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function fftDirectDimension(
|
|
33
|
+
data: { re: DoubleArray[]; im: DoubleArray[] },
|
|
34
|
+
options: DirectDimensionOptions,
|
|
35
|
+
) {
|
|
36
|
+
const { re: reData, im: imData } = data;
|
|
37
|
+
const nbRows = reData.length;
|
|
38
|
+
const nbColumns = reData[0].length;
|
|
39
|
+
|
|
40
|
+
const { zeroFilling: zeroFillingF2, digitalFilterValue = 0 } = options;
|
|
41
|
+
|
|
42
|
+
const { factor: f2Factor = 2 } = zeroFillingF2 || {};
|
|
43
|
+
const nbPointsF2 =
|
|
44
|
+
zeroFillingF2?.nbPoints || 2 ** Math.round(Math.log2(nbColumns * f2Factor));
|
|
45
|
+
|
|
46
|
+
const reFFT = new Matrix(nbRows, nbPointsF2);
|
|
47
|
+
const imFFT = new Matrix(nbRows, nbPointsF2);
|
|
48
|
+
|
|
49
|
+
const { apodization: apodizationF2 } = options;
|
|
50
|
+
let {
|
|
51
|
+
ph0: ph0F2,
|
|
52
|
+
ph1: ph1F2,
|
|
53
|
+
mode: pcModeF2 = 'no',
|
|
54
|
+
} = options?.phaseCorrection || {};
|
|
55
|
+
|
|
56
|
+
for (let i = 0; i < nbRows; i++) {
|
|
57
|
+
const newData = digitalFilter(
|
|
58
|
+
{ re: reData[i], im: imData[i] },
|
|
59
|
+
{ digitalFilterValue },
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
Object.assign(newData, removeDCOffset(newData, { digitalFilterValue }));
|
|
63
|
+
|
|
64
|
+
if (apodizationF2) {
|
|
65
|
+
const { re: aRe, im: aIm } = apodization(newData, apodizationF2);
|
|
66
|
+
Object.assign(newData, { re: aRe, im: aIm });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
Object.assign(
|
|
70
|
+
newData,
|
|
71
|
+
zeroFilling(newData, { digitalFilterValue, nbPoints: nbPointsF2 }),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
Object.assign(newData, reimFFT(newData, { applyZeroShift: true }));
|
|
75
|
+
|
|
76
|
+
const dfResidual =
|
|
77
|
+
(digitalFilterValue - Math.round(digitalFilterValue)) * Math.PI * 2;
|
|
78
|
+
if (dfResidual > 0) {
|
|
79
|
+
Object.assign(newData, reimPhaseCorrection(newData, 0, -dfResidual));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (
|
|
83
|
+
i === 0 &&
|
|
84
|
+
pcModeF2 === 'pk' &&
|
|
85
|
+
ph0F2 === undefined &&
|
|
86
|
+
ph1F2 === undefined
|
|
87
|
+
) {
|
|
88
|
+
const phased = reimAutoPhaseCorrection(newData, { magnitudeMode: false });
|
|
89
|
+
ph0F2 = phased.ph0;
|
|
90
|
+
ph1F2 = phased.ph1;
|
|
91
|
+
}
|
|
92
|
+
if (pcModeF2 === 'pk' && ph0F2 !== undefined && ph1F2 !== undefined) {
|
|
93
|
+
Object.assign(
|
|
94
|
+
newData,
|
|
95
|
+
reimPhaseCorrection(
|
|
96
|
+
newData,
|
|
97
|
+
(ph0F2 * Math.PI) / 180,
|
|
98
|
+
(ph1F2 * Math.PI) / 180,
|
|
99
|
+
),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
reFFT.setRow(i, newData.re);
|
|
104
|
+
imFFT.setRow(i, newData.im);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
re: reFFT,
|
|
109
|
+
im: imFFT,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { DoubleArray } from 'cheminfo-types';
|
|
2
|
+
import Matrix from 'ml-matrix';
|
|
3
|
+
import {
|
|
4
|
+
reimAbsolute,
|
|
5
|
+
reimAutoPhaseCorrection,
|
|
6
|
+
reimFFT,
|
|
7
|
+
reimPhaseCorrection,
|
|
8
|
+
} from 'ml-spectra-processing';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
apodization,
|
|
12
|
+
ApodizationOptions,
|
|
13
|
+
} from '../../../apodization/apodization';
|
|
14
|
+
|
|
15
|
+
import { zeroFilling, ZeroFillingOptions } from './zeroFilling';
|
|
16
|
+
|
|
17
|
+
interface PhaseCorrection {
|
|
18
|
+
mode?: string;
|
|
19
|
+
ph0?: number;
|
|
20
|
+
ph1?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface IndirectDimensionOptions {
|
|
24
|
+
fnMode?: string;
|
|
25
|
+
zeroFilling?: ZeroFillingOptions;
|
|
26
|
+
apodization?: ApodizationOptions;
|
|
27
|
+
phaseCorrection?: PhaseCorrection;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function fftIndirectDimension(
|
|
31
|
+
data: { re: DoubleArray[] | Matrix; im: DoubleArray[] | Matrix },
|
|
32
|
+
options: IndirectDimensionOptions,
|
|
33
|
+
) {
|
|
34
|
+
const reData = Matrix.checkMatrix(data.re);
|
|
35
|
+
const imData = Matrix.checkMatrix(data.im);
|
|
36
|
+
|
|
37
|
+
const nbRows = reData.rows;
|
|
38
|
+
const nbColumns = reData.columns;
|
|
39
|
+
|
|
40
|
+
const { zeroFilling: zeroFillingF1 } = options;
|
|
41
|
+
const { factor: f1Factor = 2 } = zeroFillingF1 || {};
|
|
42
|
+
const nbPointsF1 =
|
|
43
|
+
zeroFillingF1?.nbPoints || 2 ** Math.round(Math.log2(nbRows * f1Factor));
|
|
44
|
+
const { apodization: apodizationF1 } = options;
|
|
45
|
+
let {
|
|
46
|
+
ph0: ph0F1,
|
|
47
|
+
ph1: ph1F1,
|
|
48
|
+
mode: pcModeF1,
|
|
49
|
+
} = options.phaseCorrection || {};
|
|
50
|
+
|
|
51
|
+
const reResult = new Matrix(nbPointsF1, nbColumns);
|
|
52
|
+
const imResult = new Matrix(nbPointsF1, nbColumns);
|
|
53
|
+
for (let i = 0; i < nbColumns; i++) {
|
|
54
|
+
const { re: aRe, im: aIm } = apodization(
|
|
55
|
+
{
|
|
56
|
+
re: reData.getColumn(i),
|
|
57
|
+
im: imData.getColumn(i),
|
|
58
|
+
},
|
|
59
|
+
apodizationF1,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const newRe = [];
|
|
63
|
+
const newIm = [];
|
|
64
|
+
if (options.fnMode === 'echo') {
|
|
65
|
+
for (let j = 0; j < aRe.length; j += 2) {
|
|
66
|
+
newRe.push(aRe[j]);
|
|
67
|
+
newIm.push(aRe[j + 1]);
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
for (let j = 0; j < aRe.length; j++) {
|
|
71
|
+
newRe.push(aRe[j]);
|
|
72
|
+
newIm.push(-aIm[j]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let transformed = reimFFT(
|
|
77
|
+
zeroFilling(
|
|
78
|
+
{
|
|
79
|
+
re: newRe,
|
|
80
|
+
im: newIm,
|
|
81
|
+
},
|
|
82
|
+
{ digitalFilterValue: 0, nbPoints: nbPointsF1 },
|
|
83
|
+
),
|
|
84
|
+
{ applyZeroShift: true },
|
|
85
|
+
);
|
|
86
|
+
let { re, im } = transformed;
|
|
87
|
+
|
|
88
|
+
if (options.phaseCorrection?.mode === 'mc') {
|
|
89
|
+
re = reimAbsolute({ re, im });
|
|
90
|
+
im = new Float64Array(im.length);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (pcModeF1 === 'pk') {
|
|
94
|
+
if (i === 0 && ph0F1 === undefined && ph1F1 === undefined) {
|
|
95
|
+
//here we can improve it by checking if the experiment is phase sensitive.
|
|
96
|
+
const phased = reimAutoPhaseCorrection(
|
|
97
|
+
{ re, im },
|
|
98
|
+
{ magnitudeMode: false },
|
|
99
|
+
);
|
|
100
|
+
ph0F1 = phased.ph0;
|
|
101
|
+
ph1F1 = phased.ph1;
|
|
102
|
+
}
|
|
103
|
+
if (ph0F1 !== undefined && ph1F1 !== undefined) {
|
|
104
|
+
Object.assign(
|
|
105
|
+
transformed,
|
|
106
|
+
reimPhaseCorrection(
|
|
107
|
+
transformed,
|
|
108
|
+
(ph0F1 * Math.PI) / 180,
|
|
109
|
+
(ph1F1 * Math.PI) / 180,
|
|
110
|
+
),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
reResult.setColumn(i, re);
|
|
116
|
+
imResult.setColumn(i, im);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
re: reResult,
|
|
121
|
+
im: imResult,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DataReIm, xMean } from 'ml-spectra-processing';
|
|
2
|
+
|
|
3
|
+
export function removeDCOffset(data: DataReIm, options: any) {
|
|
4
|
+
let { digitalFilterValue = 0 } = options;
|
|
5
|
+
const nbPoints = data.re.length;
|
|
6
|
+
const newRe = new Float64Array(data.re);
|
|
7
|
+
const newIm = new Float64Array(data.im);
|
|
8
|
+
const averageRe = xMean(
|
|
9
|
+
data.re.slice((nbPoints * 0.75) >> 0, nbPoints - digitalFilterValue),
|
|
10
|
+
);
|
|
11
|
+
const averageIm = xMean(
|
|
12
|
+
data.im.slice((nbPoints * 0.75) >> 0, nbPoints - digitalFilterValue),
|
|
13
|
+
);
|
|
14
|
+
for (let i = digitalFilterValue; i < nbPoints - digitalFilterValue; i++) {
|
|
15
|
+
newRe[i] -= averageRe;
|
|
16
|
+
newIm[i] -= averageIm;
|
|
17
|
+
}
|
|
18
|
+
return { re: newRe, im: newIm };
|
|
19
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DataReIm } from 'ml-spectra-processing';
|
|
2
|
+
|
|
3
|
+
export interface ZeroFillingOptions {
|
|
4
|
+
nbPoints?: number;
|
|
5
|
+
factor?: number;
|
|
6
|
+
digitalFilterValue?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function zeroFilling(data: DataReIm, options: ZeroFillingOptions) {
|
|
10
|
+
let { nbPoints, factor = 2, digitalFilterValue: grpdly = 0 } = options;
|
|
11
|
+
|
|
12
|
+
if (!nbPoints) {
|
|
13
|
+
nbPoints = 2 ** Math.round(Math.log2(data.re.length * factor));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let pointsToShift;
|
|
17
|
+
if (grpdly > 0) {
|
|
18
|
+
pointsToShift = Math.floor(grpdly);
|
|
19
|
+
} else {
|
|
20
|
+
pointsToShift = 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { re, im } = data;
|
|
24
|
+
|
|
25
|
+
let newRE = new Float64Array(nbPoints);
|
|
26
|
+
let newIM = new Float64Array(nbPoints);
|
|
27
|
+
|
|
28
|
+
const length = Math.min(nbPoints, re.length);
|
|
29
|
+
|
|
30
|
+
newRE.set(re.slice(0, length - pointsToShift));
|
|
31
|
+
newIM.set(im.slice(0, length - pointsToShift));
|
|
32
|
+
|
|
33
|
+
if (pointsToShift > 0 && pointsToShift < nbPoints) {
|
|
34
|
+
newRE.set(re.slice(re.length - pointsToShift), nbPoints - pointsToShift);
|
|
35
|
+
newIM.set(im.slice(re.length - pointsToShift), nbPoints - pointsToShift);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { re: newRE, im: newIM };
|
|
39
|
+
}
|