Introduction

Brüel & Kjær have a line of electrodynamic shaker tables that use their LDS Dactron signal analyzer which produce proprietary .sig files.

Example of a .sig file’s header:

1
2
3
4
5
6
7
8
9
10
$ xxd input1\(t\).sig  | head -n 10
00000000: 44f7 0000 0744 4143 5452 4f4e 5804 0000  D....DACTRONX...
00000010: 0100 0000 0004 0000 0100 0000 0004 0000  ................
00000020: 0100 0000 0100 0000 0004 0000 0969 6e70  .............inp
00000030: 7574 3128 7429 0969 6e70 7574 3128 7429  ut1(t).input1(t)
00000040: 0969 6e70 7574 3128 7429 0156 0156 0000  .input1(t).V.V..
00000050: 1253 6570 7465 6d62 6572 2032 362c 2032  .September 26, 2
00000060: 3030 3608 3130 3a35 373a 3338 0856 5055  006.10:57:38.VPU
00000070: 2d32 3232 3200 0000 0000 0000 00a5 e2ec  -2222...........
00000080: c367 d805 3f00 0000 0000 0000 0000 0000  .g..?...........

As shown in the user manual for their PC software, they provide an ActiveX Signal Reader which provides some functions to enable the .sig file to be opened in “Visual Basic, MATLAB, LabView, C++ and so on”.

Tutorial on how to use in Python

  1. Download Shaker Control. While a license key is required to use the software, we only need the DactronSignal.dll file.
  2. Install the software to a directory of your choosing, I think it installs in C:\Program Files (x86) by default but I am not sure since I chose to install it to D:\Programs.
  3. Go to Bruel and Kjaer\LASER and COMET Vibration Control\Shaker Control LaserUSB\bin and copy DactronSignal.dll to a directory of your choosing.
  4. Create a command prompt and cd to the directory containing the DLL and register the DLL:
1
regsvr32 .\DactronSignal.dll

After copying the DactronSignal.dll file to another directory you may uninstall the program.

The Bruel and Kjaer\LASER and COMET Vibration Control\Signal Reader directory contains examples for Visual C++ and MATLAB. There are also some sample .sig files.

  1. Create a 32-bit Python virtual environment and activate the virtual environment OR use a 32-bit Python installation. It must be 32-bit since the DLL is 32-bit, if you use 64-bit it will not find the class, and you will get this error:
1
2
3
4
5
6
7
8
9
10
11
12
PS C:\<path>\signal_reader> python
Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)] on win32
--- 
PS C:\<path>\signal_reader> python .\test.py
--- 
pywintypes.com_error: (-2147221021, 'Operation unavailable', None, None)

During handling of the above exception, another exception occurred:
--- 
pywintypes.com_error: (-2147221164, 'Class not registered', None, None)
PS C:\<path>\signal_reader>

  1. Install pywin32 (and numpy and matplotlib for plotting)
1
2
python -m pip install pywin32
python -m pip install numpy matplotlib
  1. Create a script to see if loading the DLL works. Use the provided FFT1(f).sig file for testing:

⬇ Download FFT1(f).sig for testing this script

1
2
3
4
5
6
7
8
9
10
11
12
13
import win32com.client
import numpy as np
import matplotlib.pyplot as plt

dac_signal = win32com.client.Dispatch("SignalReader.DacSignal.1")

success = dac_signal.LoadSignal("FFT1(f).sig")

if success:
    print("Signal loaded successfully")
else:
    print("Error loading signal")
    exit(1)

⬇ Additional file: G1,1(f).sig
⬇ Additional file: input1(t).sig

  1. If it loaded successfully, you may try the following script which was adapted to Python from the example MATLAB script given in the documentation Recall_and_plot_G11.m. This should provide the following plot:

FFT1(f).sig plot

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
import win32com.client
import numpy as np
import matplotlib.pyplot as plt

dac_signal = win32com.client.Dispatch("SignalReader.DacSignal.1")

success = dac_signal.LoadSignal("FFT1(f).sig")

if success:
    print("Signal loaded successfully")
else:
    print("Error loading signal")
    exit(1)

# Get slice number of the signal, for G1,1(f) signal, the value is 1.
nSliceNum = dac_signal.NumofSlice
# Get number of samples.
nNumofSamples = dac_signal.NumOfSamples

# slice index is 0 based
indexSlice = 0
# Sample index is 0 based
indexFirstSmp = 0

# Get X Begin
xBegin = dac_signal.XaxisBegin
# Get X Delta
xDelta = dac_signal.XaxisDelta
xSpacingEven = dac_signal.XaxisSpacingEven

varX = np.zeros(nNumofSamples)

if xSpacingEven == 1:
    # When X-axis is evenly spaced, generate values with X(i+1) = X(i) + XDelta
    varX = np.arange(xBegin, xBegin + xDelta * nNumofSamples, xDelta)
else:
    # When X-axis is unevenly spaced, generate values with X(i+1) = X(i) * XDelta
    for i in range(nNumofSamples):
        varX[i] = xBegin * (xDelta**i)

# Get Y data (first slice)
varY = dac_signal.GetData(indexSlice, indexFirstSmp, nNumofSamples - 1)

# Convert varY from a COM array to a numpy array
varY = np.array(varY)

# Get data type (1: real, 2: complex)
DataType = dac_signal.DataType

# Plot the data based on the data type
if DataType == 1:  # Real data
    plt.figure()
    plt.loglog(varX, varY)
    plt.title("Real")
    plt.xlabel("X")
    plt.ylabel("Y")
    plt.grid(True)
    plt.show()
else:  # Complex data
    varRealY = varY[::2]  # Real part
    varImgY = varY[1::2]  # Imaginary part

    plt.figure()
    plt.subplot(2, 1, 1)
    plt.semilogy(varX, varRealY)
    plt.title("Complex - Real Part")
    plt.xlabel("X")
    plt.ylabel("Real Part")
    plt.grid(True)

    plt.subplot(2, 1, 2)
    plt.semilogy(varX, varImgY)
    plt.title("Complex - Imaginary Part")
    plt.xlabel("X")
    plt.ylabel("Imaginary Part")
    plt.grid(True)

    plt.tight_layout()
    plt.show()
  1. You may unregister the DLL after use like so if you don’t trust an old dll lying around on your system.
1
regsvr32 /u .\DactronSignal.dll