1100 lines
42 KiB
Python
1100 lines
42 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Created on Wen Jun 08 2016
|
|
|
|
@author: Adrien Vergne
|
|
"""
|
|
|
|
###############################################################################
|
|
# #
|
|
# CLASSES and METHODS for loading AQUASCAT Data #
|
|
# #
|
|
###############################################################################
|
|
|
|
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
|
# Adrien VERGNE - Irstea Lyon - 2016
|
|
# Program Python 3.5
|
|
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
|
|
|
|
|
###############################################################################
|
|
# LOADING LIBRARIES #
|
|
###############################################################################
|
|
|
|
import numpy as np # Matrix scientific computing
|
|
import os # Path / folder access management
|
|
import datetime # for time management
|
|
# Loading binary data
|
|
import struct as st
|
|
import glob
|
|
import pickle
|
|
import time
|
|
|
|
# ---------------------------------------------------------------------------#
|
|
# ------------------------------ CLASSES -----------------------------------#
|
|
# ---------------------------------------------------------------------------#
|
|
|
|
class RawAquascatData:
|
|
""" This class loads Aquascat data from Aquascat text file (aqa.txt) or
|
|
Aquascat raw file (.aqa) located in <path>
|
|
|
|
Data file info
|
|
@ivar path: string, full file location
|
|
@ivar file_name: string, file name
|
|
@ivar folder_name : string, full folder location
|
|
|
|
Aquascat data
|
|
@ivar NumProfiles: int, number of profiles
|
|
@ivar ProfileRate: int, profile recording rate in Hz
|
|
@ivar Freq: array of float, frequencies in MHz
|
|
@ivar freqText: list of strings, frequencies in text format in MHz
|
|
@ivar At: array of float, transducer radii
|
|
@ivar NumCells: int, number of cells along one profile
|
|
@ivar V: 3 dimensional array of floats, ABS data
|
|
- dim. 0 : r (distance from the transducer)
|
|
- dim. 1 : freq (transducer frequency)
|
|
- dim. 2 : profile (over all the profiles recorded in a burst)
|
|
@ivar r: array, vector column of int, range of the cells in m from
|
|
the transducer
|
|
@ivar cellSize : float, size of the cells in m
|
|
@ivar TxPulseLength: float, length of the pulse in second
|
|
@ivar RxGain: array of float, gain at reception in dB
|
|
@ivar TxGain: array of float, gain at emission in dB
|
|
@ivar Average: int, number of pings averaged in one profile
|
|
@ivar BeamWidth: array of float, width of the acoustic beam in degree
|
|
@ivar Kt: array of float, Kt constant
|
|
@ivar PingRate: int, number of pings per second, in Hz
|
|
@ivar Regime: string, regime name given when using the Aquascat
|
|
"""
|
|
|
|
def __init__(self, p):
|
|
"""
|
|
@param p: string, full file location path
|
|
"""
|
|
|
|
# ___________________ File info _______________________________________#
|
|
|
|
# Source file full location path
|
|
self.path = p
|
|
# Extracting file name
|
|
self.file_name = os.path.basename(self.path)
|
|
# Extracting folder location
|
|
self.folder_name = os.path.dirname(self.path)
|
|
# Extracting beginning time info
|
|
self.date = datetime.datetime(1, 1, 1)
|
|
|
|
# ________________________ Data _______________________________________#
|
|
|
|
# Number of profiles
|
|
self.NumProfiles = 0
|
|
# Profile rate (Hz)
|
|
self.ProfileRate = 0
|
|
# Transducer frequencies (float)
|
|
self.Freq = np.array([])
|
|
# Transducer frequencies (string text in MHz)
|
|
self.freqText = []
|
|
# Transducer radii
|
|
self.At = np.array([])
|
|
# Number of cells
|
|
self.NumCells = 0
|
|
# ABS data
|
|
self.V = np.array([])
|
|
# Cell range data
|
|
self.r = np.array([])
|
|
# Cell size
|
|
self.cellSize = 0
|
|
# Pulse length in second
|
|
self.TxPulseLength = 0
|
|
# Reception gain in dB
|
|
self.RxGain = np.array([])
|
|
# Gain at emission
|
|
self.TxGain = np.array([])
|
|
# Averaging
|
|
self.Average = 0
|
|
# Beam width
|
|
self.BeamWidth = np.array([])
|
|
# Kt constant
|
|
self.Kt = np.array([])
|
|
# Ping rate
|
|
self.PingRate = 0
|
|
# Regime name
|
|
self.Regime = ''
|
|
|
|
# _____________ Reading and loading Aquascat Data _____________________#
|
|
if p:
|
|
self.date = datetime.datetime(year=int(self.file_name[0:4]),
|
|
month=int(self.file_name[4:6]),
|
|
day=int(self.file_name[6:8]),
|
|
hour=int(self.file_name[8:10]),
|
|
minute=int(self.file_name[10:12]),
|
|
second=int(self.file_name[12:14]))
|
|
|
|
if self.file_name[-7:] == 'aqa.txt':
|
|
self.load_txt_file()
|
|
|
|
if self.file_name[-4:] == '.aqa':
|
|
self.load_aqa_file()
|
|
|
|
def load_txt_file(self):
|
|
# Opening the file ("r" for "read")
|
|
src = open(self.path, "r")
|
|
|
|
# Line 1 : skipped
|
|
src.readline().rstrip('\n')
|
|
|
|
# Lines 2-3 : number of profiles
|
|
src.readline().rstrip('\n') # .rstrip('\n') for removing the "end line"
|
|
# character
|
|
|
|
temp = np.fromstring(src.readline().rstrip('\n'), dtype=float, sep=',')[
|
|
0] # string is converted
|
|
|
|
self.NumProfiles = int(temp)
|
|
|
|
# Lines 4-5 : profile rate in Hz
|
|
src.readline().rstrip('\n')
|
|
temp = np.fromstring(src.readline().rstrip('\n'), dtype=float, sep=',')[
|
|
0]
|
|
self.ProfileRate = temp
|
|
|
|
# Lines 6-7 : transducer frequencies
|
|
src.readline().rstrip('\n')
|
|
temp = np.fromstring(src.readline().rstrip('\n'), dtype=float, sep=',')
|
|
self.Freq = temp
|
|
# Creating also an array of strings with the frequencies in MHz
|
|
for i in range(len(self.Freq)):
|
|
self.freqText.append(str(self.Freq[i] / 1e6) + ' MHz')
|
|
|
|
# Lines 8-9 : transducer radii
|
|
src.readline().rstrip('\n')
|
|
temp = np.fromstring(src.readline().rstrip('\n'), dtype=float, sep=',')
|
|
self.At = temp
|
|
|
|
# Lines 10-14 : ABS data
|
|
src.readline().rstrip('\n')
|
|
|
|
# Reading the lines one by one
|
|
for f in range(len(self.Freq)):
|
|
temp = np.fromstring(src.readline().rstrip('\n'), dtype=float,
|
|
sep=',')
|
|
# Computing the number of cells
|
|
self.NumCells = int(len(temp) / self.NumProfiles)
|
|
|
|
# Converting the 1D numpy array to 2D array
|
|
# dim. 0 : r ; dim.1 : profile
|
|
# Have to take the transpose cause numpy reshape function
|
|
# fills the lines one by one, so one profile will fill one
|
|
# line and not one column as we want
|
|
temp = temp.reshape(self.NumProfiles, self.NumCells).T
|
|
|
|
# stacking the frequency to the already existing array so we get
|
|
# a 3D array where dim. 2 (last dimension) is corresponds to the
|
|
# frequency
|
|
if f == 0:
|
|
self.V = temp
|
|
else:
|
|
self.V = np.dstack((self.V, temp))
|
|
|
|
# Finally we exchange axes 1 and 2 so to get dim.1 = frequency and
|
|
# dim. 2 = profile
|
|
self.V = self.V.swapaxes(1, 2)
|
|
|
|
# Lines 15-19 : Mean ABS over the burst. Not very useful, skipped
|
|
src.readline().rstrip('\n')
|
|
|
|
# Reading the lines one by one
|
|
for f in range(len(self.Freq)):
|
|
src.readline().rstrip('\n')
|
|
|
|
# Lines 20-24 : cell range
|
|
src.readline().rstrip('\n')
|
|
# Only reading the first line, they are all the same
|
|
temp = np.fromstring(src.readline().rstrip('\n'), dtype=float, sep=',')
|
|
# Reshaping the array in a column vector
|
|
temp = temp.reshape(self.NumCells, 1)
|
|
self.r = temp
|
|
|
|
# Computing the cell size
|
|
self.cellSize = self.r[1, 0]-self.r[0, 0]
|
|
|
|
# Skipping the other r lines
|
|
for f in range(1, len(self.Freq)):
|
|
src.readline()
|
|
|
|
# Lines 25-26 : pulse length
|
|
src.readline().rstrip('\n')
|
|
temp = np.fromstring(src.readline().rstrip('\n'), dtype=float, sep=',')[
|
|
0]
|
|
self.TxPulseLength = temp
|
|
|
|
# Lines 27-28 : Gain at reception
|
|
src.readline().rstrip('\n')
|
|
temp = np.fromstring(src.readline().rstrip('\n'), dtype=float, sep=',')
|
|
self.RxGain = temp
|
|
|
|
# Lines 29-30 : Gain at emission
|
|
src.readline().rstrip('\n')
|
|
temp = np.fromstring(src.readline().rstrip('\n'), dtype=float, sep=',')
|
|
self.TxGain = temp
|
|
|
|
# Lines 31-32 : Averaging
|
|
src.readline().rstrip('\n')
|
|
temp = np.fromstring(src.readline().rstrip('\n'), dtype=float, sep=',')[
|
|
0]
|
|
self.Average = int(temp)
|
|
|
|
# Lines 33-34 : Beam width
|
|
src.readline().rstrip('\n')
|
|
temp = np.fromstring(src.readline().rstrip('\n'), dtype=float, sep=',')
|
|
self.BeamWidth = temp
|
|
|
|
# Lines 35-36 : Kt
|
|
src.readline().rstrip('\n')
|
|
temp = np.fromstring(src.readline().rstrip('\n'), dtype=float, sep=',')
|
|
self.Kt = temp
|
|
|
|
# Lines xxx : looking for ping rate
|
|
line = src.readline().rstrip('\n')
|
|
i = 0
|
|
while line != 'PingRate' and i < 100:
|
|
line = src.readline().rstrip('\n')
|
|
i += 1
|
|
temp = eval(src.readline().rstrip('\n'))
|
|
self.PingRate = temp
|
|
|
|
# Lines xxxx : session title
|
|
src.readline().rstrip('\n')
|
|
temp = src.readline().rstrip('\n')
|
|
# Formating the string
|
|
temp = temp.replace(',', '')
|
|
temp = temp.rstrip('\0')
|
|
self.Regime = temp
|
|
|
|
# Closing the file
|
|
src.close()
|
|
|
|
def load_aqa_file(self):
|
|
# -------- From G. Fromant 2017 - LEGI, Grenoble (France) ---------- #
|
|
|
|
# __________________ sub-functions ________________________________ #
|
|
|
|
def readNextAQUAscat1000Header(f, file_size):
|
|
# Reading packet type
|
|
pkt_type_ = f.read(1)
|
|
pkt_type, = st.unpack("B", pkt_type_)
|
|
# Packet version: skipped
|
|
pkt_version_ = f.read(1)
|
|
st.unpack("B", pkt_version_)
|
|
# Reading packet size
|
|
pkt_size_ = f.read(2)
|
|
pkt_size, = st.unpack("H", pkt_size_)
|
|
# Packet checksum: skipped
|
|
pkt_checksum_ = f.read(2)
|
|
st.unpack("H", pkt_checksum_)
|
|
|
|
if f.tell() == file_size:
|
|
status = 0
|
|
else:
|
|
status = 1
|
|
|
|
return status, pkt_type, pkt_size
|
|
|
|
def find_aquascat1000_packet(f, type, file_size):
|
|
f.seek(0)
|
|
while f.tell() != file_size:
|
|
status, pkt_type, pkt_size = readNextAQUAscat1000Header(
|
|
f, file_size)
|
|
if status == 0:
|
|
break
|
|
elif pkt_type == type:
|
|
break
|
|
else:
|
|
f.seek(2 * pkt_size, 1)
|
|
return status, pkt_size
|
|
|
|
# Opening the file
|
|
f = open(self.path, 'rb')
|
|
f.seek(0, 2)
|
|
file_size = f.tell()
|
|
f.seek(0)
|
|
ExpType = 'SINGLE'
|
|
|
|
# Read File Version from File
|
|
status, pkt_size = find_aquascat1000_packet(f, 19, file_size)
|
|
|
|
if status == 1:
|
|
sdata = [st.unpack("H", f.read(2))[0] for ui in
|
|
range(1, pkt_size + 1)]
|
|
FileVersionMajor = sdata[1]
|
|
FileVersionMinor = sdata[2]
|
|
else:
|
|
FileVersionMajor = 5
|
|
FileVersionMinor = 0
|
|
|
|
del sdata
|
|
|
|
# Read in the Burst Start Time Information
|
|
status, pkt_size = find_aquascat1000_packet(f, 54, file_size)
|
|
if status == 1:
|
|
sdata = [st.unpack("H", f.read(2))[0] for ui in range(1, 6 + 1)]
|
|
wake_source_ = f.read(2)
|
|
WakeSource, = st.unpack("H", wake_source_)
|
|
burst_number_ = f.read(4)
|
|
BurstNumber, = st.unpack("I", burst_number_)
|
|
junk_ = f.read(2)
|
|
Junk, = st.unpack("H", junk_)
|
|
BurstTime = datetime.datetime(sdata[0], sdata[1], sdata[2],
|
|
sdata[3], sdata[4], sdata[5])
|
|
else:
|
|
BurstTime = datetime.datetime(0, 0, 0, 0, 0, 0)
|
|
BurstNumber = 0
|
|
WakeSource = 0
|
|
|
|
del sdata
|
|
|
|
# Deal with reading the personality
|
|
status, pkt_size = find_aquascat1000_packet(f, 53, file_size)
|
|
|
|
if status == 0:
|
|
print('Error')
|
|
pass
|
|
else:
|
|
pkt_start_pos = f.tell()
|
|
skip_ = f.read(2)
|
|
st.unpack("H", skip_) # Size of Packet
|
|
st.unpack("H", f.read(2))
|
|
t1, = st.unpack("H", f.read(2))
|
|
t2, = st.unpack("H", f.read(2))
|
|
st.unpack("H", f.read(2))
|
|
LoggerType = [str(st.unpack("s", f.read(1)))[3] for ui in
|
|
range(1, 32 + 1)]
|
|
st.unpack("H", f.read(2))
|
|
NumAbsChannels, = st.unpack("H", f.read(2))
|
|
# Number of ABS channels the system supports
|
|
NumAuxChannels, = st.unpack("H", f.read(2))
|
|
# Number of AUX channels the system supports (8)
|
|
NumAbsTransducers, = st.unpack("H", f.read(2))
|
|
# Number of ABS Transducer Information that exist in personality table
|
|
|
|
# Battery capacity (not recorded at this time)
|
|
BatteryCapacity, = st.unpack("f", f.read(4))
|
|
StandbyPower, = st.unpack("f", f.read(4))
|
|
ActivePower, = st.unpack("f", f.read(4))
|
|
|
|
############
|
|
f.seek(pkt_start_pos + 112, 0)
|
|
# Offsets into the packet
|
|
PtrToAuxInfo, = st.unpack("H", f.read(2))
|
|
PtrToTransducerInfo, = st.unpack("H", f.read(2))
|
|
|
|
# Read in the important information for the Aux Channels
|
|
# First Need to assign the multiple dimension arrays
|
|
AuxChannelName = []
|
|
AuxChannelUnit = []
|
|
AuxFlags = []
|
|
AuxNumGain = []
|
|
AuxCalDate = []
|
|
AuxNumCoeff = []
|
|
AuxGainLabel = []
|
|
AuxGainCoeff = []
|
|
AuxGainMin = []
|
|
AuxGainMax = []
|
|
for i in range(1, NumAuxChannels + 1):
|
|
PtrToThisAux = pkt_start_pos + PtrToAuxInfo * 2 + 400 * (i - 1)
|
|
# Move to the start of the ABS information
|
|
f.seek(PtrToThisAux, 0)
|
|
skip = [st.unpack("H", f.read(2)) for ui in range(1, 2 + 1)]
|
|
TTmp = [str(st.unpack("s", f.read(1)))[3] for ui in
|
|
range(1, 16 + 1)]
|
|
sss = ''
|
|
TTmp2 = sss.join(TTmp)
|
|
TTmp2 = TTmp2.strip("\\")
|
|
AuxChannelName = np.append(AuxChannelName, [TTmp2], axis=0)
|
|
st.unpack("H", f.read(2))
|
|
TTmp = [str(st.unpack("s", f.read(1)))[3] for ui in
|
|
range(1, 8 + 1)]
|
|
sss = ''
|
|
TTmp2 = sss.join(TTmp)
|
|
TTmp2 = TTmp2.strip("\\")
|
|
AuxChannelUnit = np.append(AuxChannelUnit, [TTmp2], axis=0)
|
|
st.unpack("H", f.read(2))
|
|
AuxFlags = np.append(AuxFlags, st.unpack("H", f.read(2)))
|
|
skip = [st.unpack("H", f.read(2)) for ui in range(1, 2 + 1)]
|
|
AuxNumGain = np.append(AuxNumGain, st.unpack("H", f.read(2)),
|
|
axis=0)
|
|
st.unpack("H", f.read(2))
|
|
TTmp = [str(st.unpack("s", f.read(1)))[3] for ui in
|
|
range(1, 16 + 1)]
|
|
sss = ''
|
|
TTmp2 = sss.join(TTmp)
|
|
TTmp2 = TTmp2.strip("\\")
|
|
AuxCalDate = np.append(AuxCalDate, [TTmp2], axis=0)
|
|
st.unpack("H", f.read(2))
|
|
AuxNumCoeff = np.append(AuxNumCoeff, st.unpack("H", f.read(2)),
|
|
axis=0)
|
|
skip = [st.unpack("H", f.read(2)) for ui in range(1, 5 + 1)]
|
|
|
|
f.seek(PtrToThisAux + 80, 0) # ensures aligned
|
|
|
|
AuxGainLabel.append([])
|
|
AuxGainCoeff.append([])
|
|
AuxGainMin.append([])
|
|
AuxGainMax.append([])
|
|
for j in range(1, int(AuxNumGain[i - 1] + 1)):
|
|
AuxGainLabel[i - 1].append([])
|
|
AuxGainCoeff[i - 1].append([])
|
|
AuxGainMin[i - 1].append([])
|
|
AuxGainMax[i - 1].append([])
|
|
TTmp = [str(st.unpack("s", f.read(1)))[3] for ui in
|
|
range(1, 4 + 1)]
|
|
sss = ''
|
|
TTmp2 = sss.join(TTmp)
|
|
TTmp2 = TTmp2.strip("\\")
|
|
AuxGainLabel[i - 1][j - 1] = np.append(
|
|
AuxGainLabel[i - 1][j - 1], [TTmp2], axis=0)
|
|
[st.unpack("H", f.read(2)) for ui in range(1, 4 + 1)]
|
|
TTmp = [st.unpack("f", f.read(4))[0] for ui in
|
|
range(1, 5 + 1)]
|
|
AuxGainCoeff[i - 1][j - 1] = np.append(
|
|
AuxGainCoeff[i - 1][j - 1], TTmp, axis=0)
|
|
AuxGainMin[i - 1][j - 1] = st.unpack("f", f.read(4))[
|
|
0] # Minimum value used in calibration data
|
|
AuxGainMax[i - 1][j - 1] = st.unpack("f", f.read(
|
|
4)) # Maximum value used in calibration data
|
|
skip = [st.unpack("f", f.read(4)) for ui in
|
|
range(1, 10 + 1)]
|
|
|
|
# Now Jump to the Transducer Info
|
|
TransducerSerialNum = []
|
|
TransducerFrequency = []
|
|
TransducerRadius = []
|
|
TransducerBeamWidth = []
|
|
TransducerKt = []
|
|
for i in range(1, NumAbsTransducers + 1):
|
|
f.seek(pkt_start_pos + PtrToTransducerInfo * 2 + 200 * (i - 1),
|
|
0) # Move to the start of the ABS information
|
|
TTmp = [str(st.unpack("s", f.read(1)))[3] for ui in
|
|
range(1, 20 + 1)]
|
|
sss = ''
|
|
TTmp2 = sss.join(TTmp)
|
|
TTmp2 = TTmp2.strip("\\")
|
|
TransducerSerialNum = np.append(TransducerSerialNum, [TTmp2],
|
|
axis=0) # GV-10
|
|
st.unpack("H", f.read(2))
|
|
|
|
# Transducer frequency in Hz
|
|
TransducerFrequency = np.append(TransducerFrequency,
|
|
st.unpack("f", f.read(4)))
|
|
# Transducer radius in meters
|
|
TransducerRadius = np.append(TransducerRadius,
|
|
st.unpack("f", f.read(4)))
|
|
|
|
# Transducers beam width in Degrees (3dB beamdidth, derived
|
|
# from acoustic beam pattern)
|
|
TransducerBeamWidth = np.append(TransducerBeamWidth,
|
|
st.unpack("f", f.read(4))[0])
|
|
|
|
skip = [st.unpack("f", f.read(4)) for ui in range(1, 4 + 1)]
|
|
|
|
# Transducer Kt (only if set in the personality)
|
|
TransducerKt = np.append(TransducerKt,
|
|
st.unpack("f", f.read(4)))
|
|
|
|
# ----------------------------------------------------------------------
|
|
# Read in the Regime (Logger Set-Up) Information
|
|
# ----------------------------------------------------------------------
|
|
|
|
# regime details
|
|
status, pkt_size = find_aquascat1000_packet(f, 21, file_size)
|
|
if 0 == status:
|
|
print('Regime Packet not found abort')
|
|
|
|
else:
|
|
pkt_start_pos = f.tell()
|
|
# Session Information
|
|
# Not interested in session start time at the moment
|
|
SessionControl = [st.unpack("H", f.read(2))
|
|
for ui in range(1, 11 + 1)]
|
|
TTmp = [str(st.unpack("s", f.read(1)))[3] for ui in
|
|
range(1, 32 + 1)]
|
|
sss = ''
|
|
TTmp2 = sss.join(TTmp)
|
|
TTmp2 = TTmp2.strip("\\")
|
|
SessionTitle = TTmp2
|
|
# The Aux channels are sampled at the PingRate divided by
|
|
# the auxPingDiv
|
|
AuxPingDiv, = st.unpack("H", f.read(2))
|
|
# The serial pressure + Temperature sampling derived as above
|
|
SerialPingDiv, = st.unpack("H", f.read(2))
|
|
Junk = [st.unpack("H", f.read(2)) for ui in range(1, 2 + 1)]
|
|
# THIS IS INCORRECTLY SAVED BY THE SYSTEM, SHOULD BE 0 or 8
|
|
NumAuxChans, = st.unpack("H", f.read(2))
|
|
NumAuxSamples, = st.unpack("I", f.read(4))
|
|
|
|
# This is a temp trick to correct---------------------|
|
|
# if NumAuxSamples!=0:
|
|
# NumAuxChans=8 # for NumAuxChans always being 0
|
|
# # It should be eliminated when corrected by Aquatec---|
|
|
|
|
Junk = [st.unpack("H", f.read(2)) for ui in range(1, 4 + 1)]
|
|
# Ping rate (base profile rate before averaging)
|
|
PingRate, = st.unpack("H", f.read(2))
|
|
# This is number of profiles collected prior to averaging
|
|
NumPings, = st.unpack("I", f.read(4))
|
|
# Number of enabled ABS Channels
|
|
NumAbsTimeSlots, = st.unpack("H", f.read(2))
|
|
Junk = [st.unpack("H", f.read(2)) for ui in range(1, 3 + 1)]
|
|
|
|
# These are the offsets into the Packet
|
|
PtrToAuxInfo, = st.unpack("H", f.read(2))
|
|
PtrToAbsInfo, = st.unpack("H", f.read(2))
|
|
|
|
# Calculate Aux Specific Information
|
|
|
|
# if AuxPingDiv=0 then no aux channels are
|
|
# enabled and NumAuxChannels =0
|
|
AuxSampleRate = 0
|
|
if 0 == AuxPingDiv:
|
|
AuxNumSamples = 0
|
|
NumAuxChans = 0
|
|
else:
|
|
AuxSampleRate = PingRate / AuxPingDiv
|
|
AuxNumSamples = np.ceil(NumPings / AuxPingDiv) # GV-10
|
|
if NumAuxChans == 0:
|
|
NumAuxChans = 8
|
|
|
|
# Serial Pressure + Temperature information
|
|
if 0 == SerialPingDiv:
|
|
NumSerialSamples = 0
|
|
SerialSampleRate = 0
|
|
else:
|
|
NumSerialSamples = np.ceil(NumPings / SerialPingDiv)
|
|
SerialSampleRate = PingRate / SerialPingDiv
|
|
|
|
# Nothing useful in the channel
|
|
|
|
# Now read in the ABS Channel information
|
|
|
|
# Move to the start of the ABS information
|
|
f.seek(pkt_start_pos + PtrToAbsInfo * 2, 0)
|
|
|
|
AbsComplex = []
|
|
AbsAverage = []
|
|
AbsDecimation = []
|
|
AbsBinLengthMM = []
|
|
AbsBinLength = []
|
|
AbsTransducerName = []
|
|
AbsTransducerRadius = []
|
|
AbsTransducerBeamWidth = []
|
|
AbsTransducerKt = []
|
|
AbsTxFrequency = []
|
|
AbsRxFrequency = []
|
|
AbsTxPulseLength = []
|
|
AbsStartingGain = []
|
|
AbsTVG = []
|
|
AbsPowerLevelpc = []
|
|
AbsPowerLevel = []
|
|
AbsStartBin = []
|
|
AbsRxChan = []
|
|
AbsTxChan = []
|
|
AbsNumBins = []
|
|
AbsNumProfiles = []
|
|
AbsProfileRate = []
|
|
AbsBinRange = []
|
|
for j in range(1, NumAbsTimeSlots + 1):
|
|
AbsBinRange.append([])
|
|
# TransducerRadius = np.append(TransducerRadius,
|
|
# st.unpack("f",f.read(4))) # In meters
|
|
Tmp, = st.unpack("H", f.read(2))
|
|
# For magnitude=0,complex=2
|
|
AbsComplex = np.append(AbsComplex, Tmp & 2)
|
|
# No of bursts averaged before saving
|
|
AbsAverage = np.append(AbsAverage, st.unpack("H", f.read(2)))
|
|
# Raw sampling rate along a profile is 19.2MHz, AbsDecimation i
|
|
AbsDecimation = np.append(AbsDecimation, st.unpack("H",
|
|
f.read(2)))
|
|
# Converts to bin size in mm based on speed 1500ms-1
|
|
AbsBinLengthMM = np.append(
|
|
AbsBinLengthMM, 1.25 * 2 ** AbsDecimation[j - 1])
|
|
# Stored as time in seconds
|
|
AbsBinLength = AbsBinLengthMM[j - 1] / 1500
|
|
|
|
# Using the Trasnducer ID copy the relevant data across
|
|
# Used to look up transducer information from personality
|
|
TransducerId, = st.unpack("H", f.read(2))
|
|
TransducerId = TransducerId + 1
|
|
|
|
AbsTransducerName = np.append(
|
|
AbsTransducerName, TransducerSerialNum[TransducerId - 1])
|
|
# Transducer radius in m
|
|
AbsTransducerRadius = np.append(
|
|
AbsTransducerRadius, TransducerRadius[TransducerId - 1])
|
|
# Transducer beam width in degs
|
|
AbsTransducerBeamWidth = np.append(
|
|
AbsTransducerBeamWidth,
|
|
TransducerBeamWidth[TransducerId - 1])
|
|
AbsTransducerKt = np.append(AbsTransducerKt,
|
|
TransducerKt[TransducerId - 1])
|
|
|
|
AbsTxFrequency = np.append(AbsTxFrequency,
|
|
st.unpack("f", f.read(4))) # In Hz
|
|
AbsRxFrequency = np.append(AbsRxFrequency,
|
|
st.unpack("f", f.read(4))) # In Hz
|
|
|
|
# Pulse length in seconds
|
|
AbsTxPulseLength = np.append(
|
|
AbsTxPulseLength, st.unpack("f", f.read(4)))
|
|
Junk = st.unpack("f", f.read(4))
|
|
# Gain in dB with reference to default (built-in) Gain of system
|
|
AbsStartingGain = np.append(
|
|
AbsStartingGain, st.unpack("f", f.read(4)))
|
|
# In dB / bin where first bin has StartingGain (not used, =0)
|
|
AbsTVG = np.append(AbsTVG, st.unpack("f", f.read(4)))
|
|
powerlevel, = st.unpack("H", f.read(2))
|
|
# Power Level in % of Maximum
|
|
AbsPowerLevelpc = np.append(AbsPowerLevelpc,
|
|
100 / 2 ** (2 * powerlevel))
|
|
AbsPowerLevel = np.append(AbsPowerLevel, -20 * np.log10(
|
|
2 ** powerlevel)) # Power Level in dB relative to Maximum Power
|
|
# Number of Bins from Start of Tx pulse before recording
|
|
AbsStartBin = np.append(AbsStartBin, st.unpack("H", f.read(2)))
|
|
# Number of Bins recorded
|
|
AbsNumBins = np.append(AbsNumBins, st.unpack("H", f.read(2)))
|
|
# Indicates which channel on AQUAscat used for TX
|
|
AbsRxChan = np.append(AbsRxChan, st.unpack("B", f.read(1)))
|
|
# Indicates which channel on AQUAscat used for RX
|
|
AbsTxChan = np.append(AbsTxChan, st.unpack("B", f.read(1)))
|
|
|
|
Junk = [st.unpack("H", f.read(2)) for ui in range(1, 12 + 1)]
|
|
# Calculate the number of profiles that should be recorded
|
|
# for this channel
|
|
AbsNumProfiles = np.append(AbsNumProfiles,
|
|
NumPings / AbsAverage[j - 1])
|
|
# Calculate the stored profile rate
|
|
AbsProfileRate = np.append(AbsProfileRate,
|
|
PingRate / AbsAverage[j - 1])
|
|
|
|
AbsBinRange[j - 1] = np.append(
|
|
AbsBinRange[j - 1],
|
|
np.array([np.arange((
|
|
AbsStartBin[j - 1]),
|
|
(AbsStartBin[j - 1] +
|
|
AbsNumBins[j - 1]))]) *
|
|
AbsBinLengthMM[j - 1] / 1000) # in m --
|
|
|
|
# --------------------------------------------------------------------
|
|
# Now deal with reading in the data
|
|
# --------------------------------------------------------------------
|
|
|
|
# Allocate Memory for the Data
|
|
AuxData = np.zeros((int(AuxNumSamples), int(NumAuxChans)))
|
|
AbsData = np.zeros((int(
|
|
AbsNumBins[0]), int(
|
|
AbsNumProfiles[0]), int(
|
|
NumAbsTimeSlots)), dtype=float, order='C')
|
|
PressTempData = np.zeros((NumSerialSamples, 2))
|
|
|
|
AuxIndex = 0
|
|
SerIndex = 0
|
|
AbsIndex = np.zeros((NumAbsTimeSlots, 1))-1
|
|
f.seek(0, 0)
|
|
|
|
# Now Read in all the Data
|
|
while(f.tell() != file_size): # 0 == feof(fid))
|
|
status, pktType, pktSize = readNextAQUAscat1000Header(f, file_size)
|
|
if 1 == status:
|
|
if pktType == 22:
|
|
chan = st.unpack("H", f.read(2))[0]+1
|
|
# Increase the Index
|
|
AbsIndex[chan-1] = AbsIndex[chan-1] + 1
|
|
Tmp = [st.unpack(
|
|
"H", f.read(2))[0] for ui in np.arange(
|
|
1, AbsNumBins[chan-1]+1)]
|
|
newList = [x / 65536 for x in Tmp]
|
|
AbsData[0:int(AbsNumBins[0]), int(
|
|
AbsIndex[chan-1]-1), int(
|
|
chan-1)] = np.array(newList[:])
|
|
|
|
# Case 41: Data were saved as Complex values
|
|
# (normalize by 32768)
|
|
# case (41)
|
|
elif pktType == 41:
|
|
chan = st.unpack("H", f.read(2))[0] + 1
|
|
# Increase the Index
|
|
|
|
|
|
AbsIndex[chan-1] = AbsIndex[chan-1] + 1
|
|
Tmp = [st.unpack(
|
|
"h", f.read(2))[0] for ui in np.arange(
|
|
1, 2 * AbsNumBins[chan-1]+1)]
|
|
sss = [x / 32768 for x in Tmp]
|
|
|
|
AbsData[:, int(
|
|
AbsIndex[chan-1]), int(chan-1)] = np.abs(np.array(sss[0::2])+complex(0,1)*np.array(sss[1::2]))
|
|
|
|
|
|
# Case 46: Auxiliary Channel Data
|
|
|
|
# case (46)
|
|
elif pktType == 46:
|
|
# Gain settings
|
|
temp = st.unpack("H", f.read(2))[0]
|
|
AuxGain = [(temp & 3) + 1, ((temp >> 2) & 3)+1, (
|
|
(temp >> 4) & 3)+1, (
|
|
(temp >> 6) & 3)+1, 1, 1, 1, 1]
|
|
# Flags
|
|
Junk = st.unpack("H", f.read(2))[0]
|
|
AuxIndex = AuxIndex + 1
|
|
Tmp = [st.unpack(
|
|
"H", f.read(2))[0] for ui in np.arange(
|
|
1, NumAuxChans+1)]
|
|
|
|
if AuxIndex == AuxNumSamples+1:
|
|
AuxIndex = AuxIndex-1
|
|
pass
|
|
else:
|
|
AuxData[AuxIndex-1, 0:NumAuxChans] = Tmp[:]
|
|
# AuxData[AuxIndex-1,0:NumAuxChans] = Tmp[:]
|
|
|
|
# Case 55: External Pressure Channel for upgraded
|
|
# ABS system - details to be provided
|
|
|
|
# case (55)
|
|
elif pktType == 55:
|
|
chan = st.unpack("H", f.read(2))[0]+1
|
|
# Increase the Index
|
|
SerIndex = SerIndex + 1
|
|
Tmp = [st.unpack(
|
|
"f", f.read(4))[0] for ui in np.arange(1, 2+1)]
|
|
PressTempData[SerIndex-1, :] = Tmp[:]
|
|
|
|
else: # otherwise
|
|
f.seek(pktSize * 2, 1)
|
|
|
|
print('Summary of Data Imported')
|
|
|
|
for j in range(1, NumAbsTimeSlots+1):
|
|
print('Timeslot {} Total Profiles = {}'.format(j-1, AbsIndex[j-1]))
|
|
|
|
print('Total Aux Samples = {}'.format(AuxIndex))
|
|
|
|
for i in range(0, NumAuxChans):
|
|
if not AuxGainCoeff[:][i]:
|
|
coeff = np.array(AuxGainCoeff[:][0])
|
|
AuxGainCoeff[:][i].append(coeff[0] * 0)
|
|
|
|
# coeff = AuxGainCoeff(:,AuxGain(i),i)
|
|
# Matlab orders polynomial coeff opposite to Aquatec
|
|
coeff = np.array(AuxGainCoeff[:][i])
|
|
coeff = coeff.reshape(1, 5)
|
|
coeff = np.fliplr(coeff)
|
|
coeff = coeff.reshape(5)
|
|
AuxData[:, i] = np.polyval(coeff, AuxData[:, i])
|
|
|
|
# Need to apply calibration coefficients for the Aux Channels, now that the
|
|
# gain is fixed, auto gain is not supported. Therefore will use last value
|
|
|
|
if NumAuxChans == 0:
|
|
AuxData = []
|
|
|
|
# ---------------- Add by Adrien Vergne: filling the class ---------- #
|
|
# Number of profiles
|
|
self.NumProfiles = int(AbsNumProfiles[0])
|
|
# Profile rate
|
|
self.ProfileRate = AbsProfileRate[0]
|
|
# Frequencies
|
|
self.Freq = AbsTxFrequency
|
|
# Frequencies in text format
|
|
for i in range(len(self.Freq)):
|
|
self.freqText.append(str(self.Freq[i] / 1e6) + ' MHz')
|
|
# Transducer radius
|
|
self.At = AbsTransducerRadius
|
|
# Backscatter data
|
|
self.V = AbsData
|
|
# Changing axis order: axis 0 range, axis1 frequency, axis 2 profile
|
|
self.V = self.V.swapaxes(2, 1)
|
|
# Range cells
|
|
self.r = AbsBinRange[0]
|
|
self.r = np.reshape(self.r, (np.size(self.r), 1))
|
|
# Number of cells
|
|
self.NumCells = np.size(self.r)
|
|
# Cell size
|
|
self.cellSize = self.r[1, 0] - self.r[0, 0]
|
|
# Pulse length
|
|
self.TxPulseLength = AbsTxPulseLength[0]
|
|
# Received gain
|
|
self.RxGain = AbsStartingGain
|
|
# Emitted gain
|
|
self.TxGain = np.round(AbsPowerLevel, 3)
|
|
# Averaging
|
|
self.Average = AbsAverage[0]
|
|
# Beam width
|
|
self.BeamWidth = AbsTransducerBeamWidth
|
|
# Kt
|
|
self.Kt = AbsTransducerKt
|
|
# Ping rate
|
|
self.PingRate = PingRate
|
|
# Session title
|
|
self.Regime = SessionTitle
|
|
|
|
f.close # Close the *.aqa data file
|
|
|
|
|
|
class MeanAquascatProfile:
|
|
""" This class creates an object that contains several data of the mean
|
|
profile computed for one burst
|
|
@ivar file_name: string, name of the raw data file
|
|
@ivar Freq: list of float, frequencies
|
|
@ivar freqText: list of strings, freq in text in MHz
|
|
@ivar NumProfiles: int, number of profiles averaged
|
|
@ivar r: array of float, range cells for the profile
|
|
@ivar rMin: int, minimum range
|
|
@ivar rMax: int, maximum range
|
|
@ivar V: 2D array of float, ABS mean data
|
|
@ivar Average_method: string, 'simple' or 'quadratic'
|
|
@ivar Rx: list of received gain (one for each frequency)
|
|
@ivar Tx: list of emission gain (one for each frequency)
|
|
@ivar average : int, number of pings averaged per profile in the
|
|
initial burst
|
|
@ivar pulseLength: array of floats, length of the pulse in second
|
|
"""
|
|
|
|
def __init__(self):
|
|
|
|
# File name of the raw data
|
|
self.file_name = ''
|
|
# Frequencies
|
|
self.Freq = []
|
|
# Frequencies in text format
|
|
self.freqText = []
|
|
# Number of profiles averaged
|
|
self.NumProfiles = 0
|
|
# Range cells
|
|
self.r = np.array([])
|
|
# Minimum range
|
|
self.rMin = 0
|
|
# Maximum range
|
|
self.rMax = 0
|
|
# ABS data
|
|
self.V = np.array([])
|
|
# Averaging method
|
|
self.Average_method = ''
|
|
# Reception gain
|
|
self.Rx = []
|
|
# Emission gain
|
|
self.Tx = []
|
|
# number of pings averaged per profile in the intial burst
|
|
self.average = 0
|
|
# pulse length (s)
|
|
self.pulseLength = 0
|
|
|
|
def compute_mean(self, abs_raw, r_min=0, r_max=-1, mean='quadratic'):
|
|
"""This function computes a mean backscatter profile from the
|
|
raw data abs_raw, between r_min and r_max, using average method
|
|
'quadratic' or 'simple'"""
|
|
# Filling the profile medata
|
|
self.file_name = abs_raw.file_name
|
|
self.Freq = abs_raw.Freq
|
|
self.freqText = abs_raw.freqText
|
|
self.NumProfiles = abs_raw.NumProfiles
|
|
self.Rx = abs_raw.RxGain
|
|
self.Tx = abs_raw.TxGain
|
|
self.average = abs_raw.Average
|
|
self.pulseLength = abs_raw.TxPulseLength
|
|
|
|
# Filling r values
|
|
if r_min == 0:
|
|
ind_rmin = 0
|
|
else:
|
|
ind_rmin = np.where(abs_raw.r <= r_min)[0][-1]
|
|
if r_max == -1:
|
|
ind_rmax = abs_raw.r.size
|
|
else:
|
|
ind_rmax = np.where(abs_raw.r >= r_max)[0][0]
|
|
self.r = abs_raw.r[ind_rmin:ind_rmax + 1]
|
|
self.rMin = self.r[0][0]
|
|
self.rMax = self.r[-1][0]
|
|
|
|
# Filling V values
|
|
if mean == 'quadratic':
|
|
self.Average_method = 'quadratic'
|
|
self.V = np.sqrt(
|
|
np.nanmean(abs_raw.V[ind_rmin:ind_rmax + 1, :, :] ** 2,
|
|
axis=2))
|
|
elif mean == 'simple':
|
|
self.Average_method = 'simple'
|
|
self.V = np.nanmean(abs_raw.V[ind_rmin:ind_rmax + 1, :, :], axis=2)
|
|
else:
|
|
raise ValueError("mean should be 'quadratic' or 'simple'")
|
|
|
|
def write_txt_file(self, path):
|
|
""" This method allows to write the mean profile data in a
|
|
text file"""
|
|
|
|
# Opening the text file to write in
|
|
file = open(path, "w")
|
|
|
|
# Writing the parameters
|
|
file.write("AQUAscat mean ABS profile\n\n")
|
|
|
|
# Burst name
|
|
file.write("Initial burst name\n")
|
|
file.write(self.file_name + '\n\n')
|
|
|
|
# Frequencies
|
|
file.write('Freq\n')
|
|
for i in self.Freq[0:-1]:
|
|
file.write('%s, ' % i)
|
|
file.write(str(self.Freq[-1])+'\n\n')
|
|
|
|
# Frequencies in text format
|
|
file.write('Freq in text format\n')
|
|
for i in self.freqText[0:-1]:
|
|
file.write(i + ', ')
|
|
file.write(self.freqText[-1]+'\n\n')
|
|
|
|
# Number of profiles averaged
|
|
file.write('Number of profiles averaged\n')
|
|
file.write('%s \n\n' % self.NumProfiles)
|
|
|
|
# Range cells
|
|
file.write('r\n')
|
|
for i in self.r[0:-1]:
|
|
file.write('%s, ' % i[0])
|
|
file.write('%s' % self.r[-1][0])
|
|
file.write('\n\n')
|
|
|
|
# Minimum range
|
|
file.write('rMin\n')
|
|
file.write(str(self.rMin) + '\n\n')
|
|
|
|
# Maximum range
|
|
file.write('rMax\n')
|
|
file.write(str(self.rMax) + '\n\n')
|
|
|
|
# ABS data
|
|
file.write('V\n')
|
|
for f in range(len(self.Freq)):
|
|
for i in self.V[0:-1, f]:
|
|
file.write('%s, ' % i)
|
|
file.write(str(self.V[-1, f])+'\n')
|
|
file.write('\n')
|
|
|
|
# Reception gain
|
|
file.write('Rx gain\n')
|
|
for i in self.Rx[0:-1]:
|
|
file.write('%s, ' % i)
|
|
file.write(str(self.Rx[-1])+'\n\n')
|
|
|
|
# Transmission gain
|
|
file.write('Tx gain\n')
|
|
for i in self.Tx[0:-1]:
|
|
file.write('%s, ' % i)
|
|
file.write(str(self.Tx[-1])+'\n\n')
|
|
|
|
# Number of pings average
|
|
file.write("Nb pings averaged in 1 profile\n")
|
|
file.write(str(self.average) + '\n\n')
|
|
|
|
# Pulse length
|
|
file.write("Pulse length (s)\n")
|
|
file.write(str(self.pulseLength) + '\n\n')
|
|
|
|
file.close()
|
|
|
|
def load_txt_file(self, path):
|
|
""" This method allows to load the mean profile data from a
|
|
text file"""
|
|
|
|
# Opening the text file to write in
|
|
file = open(path, "r")
|
|
|
|
# Line 1 and 2: skipped
|
|
file.readline().rstrip('\n')
|
|
file.readline().rstrip('\n')
|
|
|
|
# Lines 3-4 : initial burst name
|
|
file.readline().rstrip('\n') # .rstrip('\n') for removing the "end
|
|
# line" character
|
|
temp = file.readline().rstrip('\n')
|
|
self.file_name = temp
|
|
|
|
# Lines 6-7 : frequencies
|
|
file.readline().rstrip('\n')
|
|
file.readline().rstrip('\n')
|
|
temp = np.fromstring(file.readline().rstrip('\n'), dtype=float, sep=',')
|
|
self.Freq = temp
|
|
|
|
# Lines 9-10 : frequencies in text format
|
|
file.readline().rstrip('\n')
|
|
file.readline().rstrip('\n')
|
|
temp = file.readline().rstrip('\n')
|
|
self.freqText = temp.split(sep=', ')
|
|
|
|
# Line 12 - 13 : Number of profiles averaged
|
|
file.readline().rstrip('\n')
|
|
file.readline().rstrip('\n')
|
|
temp = eval(file.readline().rstrip('\n'))
|
|
self.NumProfiles = temp
|
|
|
|
# Lines 15-16 : range data
|
|
file.readline().rstrip('\n')
|
|
file.readline().rstrip('\n')
|
|
temp = np.fromstring(file.readline().rstrip('\n'), dtype=float, sep=',')
|
|
# Reshaping the array in a column vector
|
|
temp = temp.reshape(len(temp), 1)
|
|
self.r = temp
|
|
|
|
# Lines 18-19 : minimum range
|
|
file.readline().rstrip('\n')
|
|
file.readline().rstrip('\n')
|
|
temp = eval(file.readline().rstrip('\n'))
|
|
self.rMin = temp
|
|
|
|
# Lines 21-22 : maximum range
|
|
file.readline().rstrip('\n')
|
|
file.readline().rstrip('\n')
|
|
temp = eval(file.readline().rstrip('\n'))
|
|
self.rMax = temp
|
|
|
|
# Lines 24 --- ABS data
|
|
file.readline().rstrip('\n')
|
|
file.readline().rstrip('\n')
|
|
# Reading the lines one by one
|
|
for f in range(len(self.Freq)):
|
|
temp = np.fromstring(file.readline().rstrip('\n'), dtype=float,
|
|
sep=', ')
|
|
|
|
if f == 0:
|
|
self.V = temp
|
|
else:
|
|
# Adding a column
|
|
self.V = np.c_[self.V, temp]
|
|
|
|
# Rx gain
|
|
file.readline().rstrip('\n')
|
|
file.readline().rstrip('\n')
|
|
temp = np.fromstring(file.readline().rstrip('\n'), dtype=float, sep=',')
|
|
self.Rx = temp
|
|
|
|
# Tx gain
|
|
file.readline().rstrip('\n')
|
|
file.readline().rstrip('\n')
|
|
temp = np.fromstring(file.readline().rstrip('\n'), dtype=float, sep=',')
|
|
self.Tx = temp
|
|
|
|
# Number of pings averaged
|
|
file.readline().rstrip('\n')
|
|
file.readline().rstrip('\n')
|
|
temp = np.fromstring(file.readline().rstrip('\n'), dtype=float, sep=',')
|
|
self.average = temp
|
|
|
|
# Pulse length
|
|
file.readline().rstrip('\n')
|
|
file.readline().rstrip('\n')
|
|
temp = np.fromstring(file.readline().rstrip('\n'), dtype=float, sep=',')
|
|
self.pulseLength = temp
|
|
|
|
file.close()
|
|
|