Last active
March 13, 2024 09:23
-
-
Save ZGainsforth/7b05bf0b0409ae57ce7f to your computer and use it in GitHub Desktop.
Read an SPA file from Omnic
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Created 2015, Zack Gainsforth | |
import matplotlib | |
import matplotlib.pyplot as plt | |
import numpy as np | |
import struct | |
from numpy.fft import fft, fftfreq | |
def LoadSPAInterferogram(FileName): | |
# Open the SPA file. | |
with open(FileName, 'rb') as f: | |
# Go to offset 294 in the header which tells us the number of sections in the file. | |
f.seek(294) | |
NumSections = struct.unpack('h', f.read(2))[0] | |
print('This spa file has ' + str(NumSections) + ' sections.') | |
# We will process each section in the spa file. | |
for n in range(NumSections): | |
# Go to the section start. Each section is 16 bytes, and starts after offset 304. | |
f.seek(304+16*n) | |
SectionType = struct.unpack('h', f.read(2))[0] | |
SectionOffset = struct.unpack('i', f.read(3)+b'\x00')[0] | |
SectionLength = struct.unpack('i', f.read(3)+b'\x00')[0] | |
print('Section #%d, Type: %d, Offset: %d, Length %d' % (n, SectionType, SectionOffset, SectionLength)) | |
# If this is section type 3, then it contains the result spectrum (interferogram, single beam, etc.) Note where it is in the file. | |
if SectionType==3: | |
print('Found raw spectrum section.') | |
Section3Offset = SectionOffset | |
Section3Length = SectionLength | |
# If this is section type 102, then it contains a processed spectrum. | |
if SectionType==102: | |
print('Found processed spectrum section.') | |
Section102Offset = SectionOffset | |
Section102Length = SectionLength | |
# We will default to using section 102 (processed data) if it exists. Otherwise, we read section 3 (raw data). | |
if 'Section102Length' in locals(): | |
print('Using processed spectrum.') | |
SectionOffset = Section102Offset | |
SectionLength = Section102Length | |
else: | |
print('Using raw spectrum.') | |
SectionOffset = Section3Offset | |
SectionLength = Section3Length | |
# Read in the data! | |
f.seek(SectionOffset) | |
DataText = f.read(SectionLength) | |
Data = np.fromstring(DataText, dtype='float32')[:-5] | |
return Data | |
def PlotInterferogram(Interferogram): | |
plt.figure() | |
plt.plot(Interferogram) | |
plt.title('Interferogram') | |
# Apply the hamming window to smooth out the FT (we don't want ripples.) | |
H = np.hamming(len(Interferogram)) | |
I = H*Interferogram | |
plt.figure() | |
plt.plot(I) | |
plt.plot(H) | |
plt.title('Hamminged Interferogram') | |
# Compute the FFT | |
F = fft(I) | |
# Make the magnitude spectrum. | |
Mag = np.abs(F) | |
Mag = Mag[:int(len(Mag)/2)] | |
LaserFreq = 15798.3 # cm-1, this is the sampling distance for the interferogram. | |
#dE = 1/(2*LaserFreq) | |
E = fftfreq(len(Interferogram), 1/(2*LaserFreq)) | |
E=E[:int(len(E)/2)] | |
plt.figure() | |
plt.plot(E, Mag) | |
plt.title('Magnitude spectrum') | |
# Make the phase spectrum. | |
Phase = np.unwrap(np.angle(F)) | |
Phase = Phase[:int(len(Phase)/2)] | |
plt.figure() | |
plt.plot(E, Phase) | |
plt.title('Phase spectrum') | |
return | |
def GetMagAndPhaseFromInterferogram(Interferogram, SamplingLaserFreq=15798.3): | |
# Returns: | |
# Wavenumbers is the energy axis in cm-1 | |
# Mag is the magnitude spectrum | |
# Phase is the phase spectrum. | |
# Apply the hamming window to smooth out the FT (we don't want ripples.) | |
H = np.hamming(len(Interferogram)) | |
I = H*Interferogram | |
# Compute the FFT | |
F = fft(I) | |
# Make the magnitude spectrum. | |
Mag = np.abs(F) | |
Mag = Mag[:int(len(Mag)/2)] | |
LaserFreq = SamplingLaserFreq #15798.3 # cm-1, this is the sampling distance for the interferogram. | |
#dE = 1/(2*LaserFreq) | |
E = fftfreq(len(Interferogram), 1/(2*LaserFreq)) | |
E=E[:int(len(E)/2)] | |
# Make the phase spectrum. | |
Phase = np.unwrap(np.angle(F)) | |
Phase = Phase[:int(len(Phase)/2)] | |
return E, Mag, Phase | |
if __name__ == '__main__': | |
# FileName = 'Nuth_FeO_smoke_DriftSpot2_Au_0006.spa' | |
FileName = 'background/bkg_au_harmonic2.spa' | |
# FileName = 'section5/scan2/spec0040.spa' | |
Interferogram = LoadSPAInterferogram(FileName) | |
PlotInterferogram(Interferogram) | |
plt.show() |
Hi,
The SPA format is not well documented. As far as I know, there is only my own and some others’ codes on github and elsewhere which give an idea what is in the file. You can figure it out by opening up the file with a binary editor or hex editor (for example 0xED is a program on OS X) and trying to guess what is going on. Generally the structure of such files is as sequence of headers and data. The headers will have info about the kind of data, and how big it is until the next block of data — which will start with a header.
I suspect that the problem may be that your SPA is slightly different than the SPA’s I used. I do notice that if I simply truncate the tail end of the interferogram, it is happier and I get the following:
Apparently the tail end of the data has exponentially huge numbers (did the data get divided by zero?) and this destroys the FFT.
Also, this looks more like a spectrum than an interferogram — which is part of why I suspect your instrument is writing out a slightly different format than what I had.
For comparison there is also a matlab code: https://www.mathworks.com/matlabcentral/fileexchange/57904-loadspectra?s_tid=mwa_osa_a <https://www.mathworks.com/matlabcentral/fileexchange/57904-loadspectra?s_tid=mwa_osa_a>
And a C++ code: https://github.com/pricebenjamin/SPA-file-reader <https://github.com/pricebenjamin/SPA-file-reader>
You can take a look at those and see if they better duplicate your file structure — and feel free to submit an updated gist if you want to bring some of that info back to python!
Hopefully this helps?
Zack
… On May 4, 2021, at 7:06 AM, TRAN ***@***.***> wrote:
@tungcua66 commented on this gist.
Hello,
I'm using your code to read the spa file and draw it. The problem is when i print the Interferogram, the output is not expected result.
You can test with files in this linklink <https://drive.google.com/drive/folders/1YW39JO1sz7_Pmla7-x8xUenKyfk5rhpb?usp=sharing>
How can you know the spa format file ?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub <https://gist.github.com/7b05bf0b0409ae57ce7f#gistcomment-3731124>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/ABUL52ZGLWZFTBABRJSI6WLTL75OJANCNFSM44CY6Y5A>.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello,
I'm using your code to read the spa file and draw it. The problem is when i print the Interferogram, the output is not expected result(csv file).
You can test with files in this link
How can you know the spa format file ?