# Signal Processor¶

After running a simulation, you may need to apply some post-simulation signal processing. For example, you may need to scale the simulated spectrum to match experimental intensities, or you may want to convolve the spectrum with a Lorentzian, Gaussian, or other line-broadening
function. For this reason, **mrsimulator** offers some frequently used NMR signal processing tools within the `mrsimulator.signal_processor`

module.

See also

Signal Processing Gallery for notebooks using common processing functions.

## CSDM object¶

The simulated spectrum is held in a CSDM [1] object, which supports multi-dimensional scientific datasets (NMR, EPR, FTIR, GC, etc.). For more information, see the csdmpy documentation.

`SignalProcessor`

class¶

Signal processing is a series of operations sequentially applied to the dataset.
In mrsimulator, the `SignalProcessor`

object is
used to apply operations. Here we create a new SignalProcessor object

```
# Import the signal_processor module
from mrsimulator import signal_processor as sp
# Create a new SignalProcessor object
processor = sp.SignalProcessor()
```

Each signal processor object holds a list of operations under the *operations* attribute. Below we add operations to apply Gaussian line broadening and a scale factor.

```
processor.operations = [
sp.IFFT(),
sp.apodization.Gaussian(FWHM="50 Hz"),
sp.FFT(),
sp.Scale(factor=120),
]
```

First, an inverse Fourier transform is applied to the dataset. Then,
a Gaussian apodization with a full-width-at-half-maximum of 50 Hz
in the frequency domain is applied. The unit used for the `FWHM`

attribute corresponds to the dimensionality of the dataset. By
choosing Hz, we imply the dataset is in units of frequency.
Finally, a forward Fourier transform is applied to the apodized
dataset, and all points are scaled up by 120 times.

Note

Convolutions in **mrsimulator** are performed using the
Convolution Theorem. A spectrum is
Fourier transformed, and apodizations are performed in the time domain before being transformed back into the frequency domain.

Let’s create a CSDM object and then apply the operations to visualize the results.

```
import csdmpy as cp
import numpy as np
# Create a CSDM object with delta function at 200 Hz
test_data = np.zeros(500)
test_data[200] = 1
csdm_object = cp.CSDM(
dependent_variables=[cp.as_dependent_variable(test_data)],
dimensions=[cp.LinearDimension(count=500, increment="1 Hz")],
)
```

To apply the previously defined signal processing operations to the above CSDM object, use
the `apply_operations()`

method of the
`SignalProcessor`

instance as follows

```
processed_dataset = processor.apply_operations(dataset = csdm_object)
```

The variable `processed_dataset`

is another CSDM object holding the dataset
after the list of operations has been applied to `csdm_object`

. Below is a
plot comparing the unprocessed and processed dataset

```
import matplotlib.pyplot as plt
_, ax = plt.subplots(1, 2, figsize = (8, 3), subplot_kw = {"projection":"csdm"})
ax[0].plot(csdm_object, color="black", linewidth=1)
ax[0].set_title("Unprocessed")
ax[1].plot(processed_dataset.real, color="black", linewidth=1)
ax[1].set_title("Processed")
plt.tight_layout()
plt.show()
```

## Applying Operations along a Dimension¶

Multi-dimensional NMR simulations may need different operations applied along different dimensions. Each operation has the attribute `dim_index`

, which is used to apply operations along a certain dimension.

By default, `dim_index`

is `None`

and is applied along the 1st dimension. An integer or list of integers can be passed to `dim_index`

, specifying the dimensions. Below are examples of specifying the dimensions

```
# Gaussian apodization along the first dimension (default)
sp.apodization.Gaussian(FWHM="10 Hz")
# Constant offset along the second dimension
sp.baseline.ConstantOffset(offset=10, dim_index=1)
# Exponential apodization along the first and third dimensions
sp.apodization.Exponential(FWHM="10 Hz", dim_index=[0, 2])
```

## Applying Apodizations to specific Dependent Variables¶

Each dimension in a simulated spectrum can hold multiple dependent variables (a.k.a. contributions from multiple spin systems). Each spin system may need different convolutions applied to match an experimental spectrum. The
`Apodization`

sub-classes have the `dv_index`

attribute, specifying which dependent variable (spin system) to apply the operation on. By default, `dv_index`

is `None`

and will apply the convolution to all dependent variables
in a dimension.

Note

The index of a dependent variable (spin system) corresponds to the order of spin systems in the
`spin_systems`

list.

```
processor = sp.SignalProcessor(
operations=[
sp.IFFT(),
sp.apodization.Gaussian(FWHM="25 Hz", dv_index=0),
sp.apodization.Gaussian(FWHM="70 Hz", dv_index=1),
sp.IFFT(),
]
)
```

The above list of operations will apply 25 and 70 Hz of Gaussian line broadening to dependent variables at index 0 and 1, respectively.

Let’s add another dependent variable to the previously created CSDM object to target specific dependent variables.

```
test_data = np.zeros(500)
test_data[300] = 1
csdm_object.add_dependent_variable(cp.as_dependent_variable(test_data))
```

Now, we again apply the operations with the
`apply_operations()`

method.
The comparison of the unprocessed and processed dataset is also shown below.

```
processed_dataset = processor.apply_operations(dataset = csdm_object)
```

Below is a plot of the dataset before and after applying the operations

```
_, ax = plt.subplots(1, 2, figsize=(8, 3), subplot_kw={"projection":"csdm"})
ax[0].plot(csdm_object, linewidth=1)
ax[0].set_title("Unprocessed")
ax[1].plot(processed_dataset.real, linewidth=1)
ax[1].set_title("Processed")
plt.tight_layout()
plt.show()
```