Import the function to read Sintela  DAS files from the notebook “Sintela DAS Raw Data v3 File Reader v1.2.0.nb”.

sintelaReadRawV3[fileName_String,OptionsPattern[{firstChan->1,lastChan->-1,startTime->{1970},endTime->{2050},verbose->False}]]:= Module[ (* v1.2.0 of this function *)
{fileSizeBytes,inStream,syncWord,hdrSize,hdrVers,sampCnt,sampTime,numChans,numSamps,sampRate,chanSpacing,gaugeLen,trigger,startChan,chanStep,gpsPPSTime,gpsPPSSampOffs,gpsStat,trigFlags,statFlags,packetSizeBytes,totalPackets,totalSamps,packet,sampTimeOffset=20,startTimeSecs,endTimeSecs,rdBuf,data={},firstPacket=True,firstSampTime},

(* Decode header information *)
fileSizeBytes=UnitConvert[FileSize[filename],"Bytes"]//QuantityMagnitude;
inStream=OpenRead[fileName,BinaryFormat->True];
syncWord=BinaryRead[inStream, "UnsignedInteger32"] (* offset 0 *);
hdrSize=BinaryRead[inStream, "UnsignedInteger32"] (* offset 4 *);
hdrVers=BinaryRead[inStream, "UnsignedInteger32"] (* offset 8 *);
If[OptionValue[verbose],
If[
syncWord==16^^11223344&&hdrSize==96,
Print["Sintela Data Type 3 - Raw 32 Bit."], 
(Print["Not a Sintela Type 3 File."];Close[inStream];Abort[])
] ;(* end if *)
]; (* end if *)
sampCnt=BinaryRead[inStream, "UnsignedInteger64"] (* offset 12 *);
sampTime=BinaryRead[inStream,"UnsignedInteger64"] (* offset 20 *);
numChans=BinaryRead[inStream,"UnsignedInteger32"] (* offset 28 *);
numSamps=BinaryRead[inStream,"UnsignedInteger32"] (* offset 32 *);
sampRate=BinaryRead[inStream,"Real32"] (* offset 36 *);
chanSpacing=BinaryRead[inStream,"Real32"] (* offset 40 *);
gaugeLen=BinaryRead[inStream,"Real32"] (* offset 44 *);
trigger=BinaryRead[inStream,"UnsignedInteger32"] (* offset 48 *);
startChan=BinaryRead[inStream,"UnsignedInteger32"] (* offset 52 *);
chanStep=BinaryRead[inStream,"UnsignedInteger32"] (* offset 56 *);
gpsPPSTime=BinaryRead[inStream,"UnsignedInteger32"] (* offset 60 *);
gpsPPSSampOffs=BinaryRead[inStream,"Integer32"] (* offset 64 *);
gpsStat=BinaryRead[inStream,"UnsignedInteger32"] (* offset 68 *);
trigFlags=BinaryRead[inStream,"UnsignedInteger8"] (* offset 72 *);
statFlags=BinaryRead[inStream,"UnsignedInteger8"] (* offset 73 *);

(* Display first header *)
If[OptionValue[verbose],
Print["Packet Channels x Samples : ",numChans," x ",numSamps];Print["Start Channel: ",startChan];
Print["Fs: ",NumberForm[sampRate,{8,3}]," Hz"] ;Print["Channel Spacing: ",NumberForm[chanSpacing,{4,1}]," m"];
Print["Gauge Length: ",NumberForm[gaugeLen,{4,1}]," m"];

Print["File Start Time: ",DateString[FromUnixTime[sampTime/10^9,TimeZone->"GMT"]],".",Mod[sampTime,10^9]," GMT"];
]; (* end If *)
packetSizeBytes=(hdrSize+(numChans*numSamps*4));
totalPackets=Round[fileSizeBytes/packetSizeBytes];
totalSamps=totalPackets*numSamps;
If[OptionValue[verbose],
Print["Total Samples Per Channel: ",totalSamps];
Print["Total Packets: ",totalPackets];
Print["Bytes per Packet: ",packetSizeBytes];
]; (* end If *)

(* Convert start and end times to UnixTime seconds *)
startTimeSecs=Block[{$TimeZone=0},UnixTime[OptionValue[startTime]]];
endTimeSecs=Block[{$TimeZone=0},UnixTime[OptionValue[endTime]]];

(* Read and extract data samples (skipping over header blocks) *)
SetStreamPosition[inStream,0];(* Rewind file *)
For[packet=1,packet<=totalPackets,packet++,
(* skip to sample time of current packet *)
SetStreamPosition[inStream,StreamPosition[inStream]+sampTimeOffset];
sampTime=BinaryRead[inStream,"UnsignedInteger64"]; (* read sample time *)
(* skip to end of header *)
SetStreamPosition[inStream,StreamPosition[inStream]+hdrSize-sampTimeOffset-8];
(* read samples if they're within requested time window, and keep only requested channels *)
If[startTimeSecs<(sampTime/10^9)<endTimeSecs,
rdBuf=Partition[BinaryReadList[inStream,"Real32",numChans*numSamps],numChans][[All,OptionValue[firstChan];;OptionValue[lastChan]]];
AppendTo[data,rdBuf];
(* if this is first saved packet, keep the time *)
If[firstPacket,firstSampTime=sampTime;firstPacket=False],
(* if packet not within requested time window, skip to start of next packet *)
SetStreamPosition[inStream,StreamPosition[inStream]+(numChans*numSamps*4)] 
]; (* end If *)
If[(sampTime/10^9)>endTimeSecs,Break[]]; (* stop if all requested samples have been read *)
]; (* end For *)
Close[inStream];

(* Data are now in sublists for each packet and we must rearrange to only sublists of requested channels. If no data at all were extracted from file, then just print message and quit *)
If[!firstPacket, (* if data in time range were found *)
If[OptionValue[firstChan]!=OptionValue[lastChan], (* if more than one channel selected *)
data=Flatten[data,1],
data=Flatten[data,2] (* else only one channel selected *)
]; (* end If *)
If[OptionValue[verbose],
If[OptionValue[firstChan]!=OptionValue[lastChan], (* if more than one channel selected *)
Print["Output Data Channels x Samples : ",Dimensions[data][[2]]," x ",Dimensions[data][[1]]],
Print["Output Data Channels x Samples : 1 x ",Length[data]] (* else only one channel selected *)
]; (* end If *)
Print["First Output Sample Time: ",DateString[FromUnixTime[firstSampTime/10^9,TimeZone->"GMT"]],".",Mod[firstSampTime,10^9]," GMT"]
]; (* end If *),
(* else if no data in time range were found *)
Print["### No data in this file for selected time range ###"];
Abort[];
] ;(* end If *)

Return[{firstSampTime,data}]
] (* end module *)

filename="/Volumes/Geo-Sense/2025\ Jan\ Data/decimator_2025-01-29_00.23.20_UTC_000122.raw";

(*filename="/Volumes/1\ TB/Geo-Sense\ data/decimator_2025-01-27_23.46.20_UTC_000007.raw";*)

{strtTime,dasDat}=sintelaReadRawV3[filename,verbose->True];

Convert the data into a sound file, but play it back at 10x speed to save time and to make low-frequency signals audible. Note that the bad clock on the first DAS deployment caused the sampling rate to be about 1051 Hz instead of the nominal 1000 Hz.

Print["First Output Sample Time: ",DateString[FromUnixTime[strtTime/10^9,TimeZone->"GMT"]],".",Mod[strtTime,10^9]," GMT"]

s=ListPlay[HighpassFilter[dasDat[[All,16]],(Quantity[10, "Hz", hertz, "Hertz"]),1001,SampleRate->1051],SampleRate->10510]

ssl=SampledSoundList[HighpassFilter[10 dasDat[[All,16]],(Quantity[10, "Hz", hertz, "Hertz"]),1001,SampleRate->1051],10510];

Sound@ssl

Export["chan16.wav",ssl]

Dimensions@dasDat

770.8/60

Range[0,720,60]

spec=Spectrogram[HighpassFilter[5 dasDat[[All,16]],(Quantity[2, "Hz", hertz, "Hertz"]),1001,SampleRate->1051],1024,512,BlackmanWindow,SampleRate->1051,PlotRange->{All,{0,50}},FrameLabel->{{"Frequency (Hz)",""},{"Time (s)","Geo-Sense DAS Channel 16"}},FrameTicks->{{Automatic,None},{Range[0,720,60],None}},ImageSize->Full]

Export["chan16.jpg",spec]

We’ll want to correct the times printed on the file names and spectrogram titles. First figure out how to convert a time in the data file’s format into a suitable string format.

strtTime

Convert the value to a time object.

strtTimeTO=FromUnixTime[strtTime/10^9,TimeZone->"UTC"]

Create a string format that mimics the filename format.

FileNameTake[filename]

DateString[strtTimeTO,{"ISODate","_","Hour",".","Minute",".","Second"}]

Import the functions to correct the times in the filenames from the notebook “GeoSense Time Warp.nb”.

(* initTime must be run once before trueTime or warpTime are called *)
initTime[timeFile_String]:=Module[{timeDat},
timeDat=Import[timeFile];
timeDat=Drop[timeDat[[1,All,All]],1];
Return[{UnixTime[#[[3]]]+3600 #[[4]]+60 #[[5]]+#[[6]],UnixTime[#[[9]]]+3600 #[[10]]+60 #[[11]]+#[[12]]}&/@timeDat
]; (* end Return *)
] (* end Module *)

The results of initTime must be assigned to a list called dT so it can be used by trueTime and warpTime.

$TimeZone=0; (* dates/times to be converted are in UTC *)
dT=initTime["/Volumes/1\ TB/MBARI\ Geo-sense\ 1_0\ time\ correction\ Rev0.03.xlsx"];
$TimeZone=(RefLink[$SystemTimeZone, paclet:ref/$SystemTimeZone]); (* return to local time *)

(* trueTime converts a warped time in UnixTime seconds to a true time *)
trueTime[warpTime_Real]:=Module[{i},
i=Select[dT,#[[2]]>warpTime&->"Index",1]//First;
((warpTime-dT[[i-1,2]])/(dT[[i,2]]-dT[[i-1,2]]))*(dT[[i,1]]-dT[[i-1,1]])+dT[[i-1,1]]
] (* end Module *)

Here’s the warped file start time, 29Jan25 at 00:23:20 in UnixTime seconds.

DecimalForm[N[strtTime/10^9],13]

Convert the warped file start time to a true time.

strtTimeTrue=trueTime[N[strtTime/10^9]]

DecimalForm[strtTimeTrue,13]

strtTimeTrueTO=FromUnixTime[strtTimeTrue,TimeZone->"UTC"]

This is correct. According to the Sintela time drift spreadsheet, 29Jan25 at 00:23:20 should convert to 28Jan25 at 23:11:39.

FromUnixTime[trueTime[N[strtTime/10^9]],TimeZone->"UTC"]

Write code to create corrected filename.

filename

FileNameTake[filename]

chan=16

trueDateStr=DateString[strtTimeTrueTO,{"Year","-","Month","-","Day","-","Hour",".","Minute",".","Second"}]

"Geo_Sense_DAS_Ch"<>ToString[chan]<>"_"<>trueDateStr<>StringTake[FileNameTake[filename],{-15,-5}]

Write function to process every .raw file in a given directory to correct the warped file times to true time, create spectrograms and .wav files, and write them out to a given directory.

makeSndsSpecs[inDir_String,outDir_String,chan_Integer]:=Module[{fnames,fnum,strtTime,dasDat,strtTimeTrueTO,trueDateStr,fbase,snd,spec,sndGain=10,specGain=5},
fnames=FileNames["*.raw",inDir]; (* get all the data file names *)
Monitor[
For[fnum=1,fnum<=Length[fnames],fnum++, (* for each file... *)
{strtTime,dasDat}=sintelaReadRawV3[fnames[[fnum]],firstChan->chan,lastChan->chan,verbose->False]; (* read the file *)
strtTimeTrueTO=FromUnixTime[trueTime[N[strtTime/10^9]],TimeZone->"UTC"]; (* convert start time *)
trueDateStr=DateString[strtTimeTrueTO,{"Year","-","Month","-","Day","-","Hour",".","Minute",".","Second"}]; (* form string with correct date/time *)
fbase="Geo_Sense_DAS_Ch"<>ToString[chan]<>"_"<>trueDateStr<>StringTake[FileNameTake[fnames[[fnum]]],{-15,-5}]; (* form complete file name base *)
snd=SampledSoundList[HighpassFilter[sndGain dasDat,(Quantity[10, "Hz", hertz, "Hertz"]),1001,SampleRate->1051],10510];
spec=Spectrogram[HighpassFilter[specGain dasDat,(Quantity[2, "Hz", hertz, "Hertz"]),1001,SampleRate->1051],1024,512,BlackmanWindow,SampleRate->1051,PlotRange->{All,{0,50}},FrameLabel->{{"Frequency (Hz)",""},{"Time (s)",fbase}},FrameTicks->{{Automatic,None},{Range[0,720,60],None}},ImageSize->Full];
Export[outDir<>"/"<>fbase<>".wav",snd];
Export[outDir<>"/"<>fbase<>".jpg",spec];
] (* end For *)
,fnames[[fnum]]
] ;(* end Monitor *)
] (* end Module *)

Process files on SD card in test directory.

(*makeSndsSpecs["/Volumes/Geo-Sense/test_data","/Volumes/Geo-Sense/test_out",16]*)

Process files on 1 TB hard disk in test directory.

(*makeSndsSpecs["/Volumes/1\ TB/test","/Volumes/1\ TB/testSndsSpecs",16]*)

Process all deployment files on 1 TB hard disk.

makeSndsSpecs["/Volumes/1\ TB/Geo-Sense\ data","/Volumes/1\ TB/SndsSpecsTrueTime",16]