#!/opt/local/bin/perl -w # # cs2txt.pl will convert cross-spectra binary file to text file # This will quickly become outdated with the Perl module under development, however, there is good cause for constructing this script: educational (learning Perl). The Perl module will need to read data in from these files and store them in a matrix using PDL as the backbone for computations. So understanding how to extact (without matlab or octave) is crucial. # # ################################### # BINARY FILE TEMPLATE: # # * They have a variable size header section followed by the cross spectra products. # * The data uses Big-Endian byte ordering (Most Significant Byte first. This means that on Intel platforms, you will need to swap the byte order for the variable being read.) # * IEEE floating point values single (4bytes) and double (8byte precision). # * Twoʼs complement, integer values. # # Data Type Definitions: # * Uint8 : Unsigned 8bit integer # * Sint8 : Signed 8bit integer # * Uint16 : Unsigned 16bit integer # * Sint16 : Signed 16bit integer # * Uint32 : Unsigned 32bit integer # * Sint32 : Signed 32bit integer # * Uint64 : Unsigned 64bit integer # * Sint64 : Signed 64bit integer # * Float : IEEE single precision floating point number (4bytes) # * Double : IEEE double precision floating point number (8bytes) # * Size4 : Unsigned 32bit integer indicating the size of following data # * Char4 : Four character code (meaning that the next four bytes make a four character string) # * Char8 : 8byte string zero terminated (zero fill to get 8bytes max. must have at least one zero) # * Char32 : 32byte string zero terminated (zero fill to get max. must have at least one zero) # * Char64 : 64byte string zero terminated (zero fill to get max. must have at least one zero) # * Char256 : 256byte string zero terminated (zero fill to get max. must have at least one zero) # * Complex : 2 IEEE single precision floating point numbers of real and imag pairs (8bytes, 4bytes each float) # # HEADER: # Each File has two major sections. A Header section and a Data section. The Header section is as follows: # - The header is expandable. Each newer version also contains the information used the by older version. # - When reading a CrossSpectra file that is a newer version than you expect then use the Extent field to skip to the beginning of the cross spectra data. # - The following Header description is a set of data fields in order where each field description is a value type with implied size, followed by the field name, and followed by the fieldʼs description. # * Note. If version is 3 or less, then nRangeCells=31, nDopplerCells=512, nFirstRangeCell=1 # # Version 1: # * SInt16 -> nCsaFileVersion -> File Version 1 to latest. (If greater than 32, itʼs probably not a spectra file.) # * UInt32 -> nDateTime -> TimeStamp. Seconds from Jan 1,1904 local computer time at site. The timestamp for CSQ files represents the start time of the data (nCsaKind = 1). The timestamp for CSS and CSA files is the center time of the data (nCsaKind = 2). # * SInt32 -> nV1Extent -> Header Bytes extension (Version 4 is +62 Bytes Till Data) # # Version 2: # * SInt16 -> nCsKind -> Type of CrossSpectra Data. 1 is self spectra for all used channels, followed by cross spectra. Timestamp is start time of data. 2 is self spectra for all used channels, followed by cross spectra, followed by quality data. Timestamp is center time of data. # * SInt32 -> nV2Extent -> Header Bytes extension (Version 4 is +56 Bytes Till Data) # # Version 3: # * Char4 -> nSiteCodeName -> Four character site code 'site' # * SInt32 -> nV3Extent -> Header Bytes extension (Version 4 is +48 Bytes Till Data) # # Version 4: # * SInt32 -> nCoverageMinutes -> Coverage Time in minutes for the data. ʻCSQ' is normally 5minutes (4.5 rounded). 'CSS' is normally 15minutes average. 'CSA' is normally 60minutes average. # * SInt32 -> bDeletedSource -> Was the ʻCSQ' deleted by CSPro after reading. # * SInt32 -> bOverrideSourceInfo -> If not zero, CSPro used its own preferences to override the source ʻCSQʼ spectra sweep settings. # * Float -> fStartFreqMHz -> Transmit Start Freq in MHz # * Float -> fRepFreqHz -> Transmit Sweep Rate in Hz # * Float -> fBandwidthKHz -> Transmit Sweep bandwidth in kHz # * SInt32 -> bSweepUp -> Transmit Sweep Freq direction is up if non zero, else down. NOTE: CenterFreq is fStartFreqMHz + fBandwidthKHz/2 * -2^(bSweepUp==0) # * SInt32 -> nDopplerCells -> Number of Doppler Cells (nominally 512) # * SInt32 -> nRangeCells -> Number of RangeCells (nominally 32 for ʻCSQ', 31 for 'CSS' & 'CSA') # * SInt32 -> nFirstRangeCell -> Index of First Range Cell in data from zero at the receiver. ʻCSQ' files nominally use zero. 'CSS' or 'CSA' files nominally use one because CSPro cuts off the first range cell as meaningless. # * Float -> fRangeCellDistKm -> Distance between range cells in kilometers. # * SInt32 -> nV4Extent -> Header Bytes extension (Version 4 is +0 Bytes Till Data) # # Version 5: # * SInt32 -> nOutputInterval -> The Output Interval in Minutes. # * Char4 -> nCreatorTypeCode -> The creator application type code. # * Char4 -> nCreatorVersion -> The creator application version. # * SInt32 -> nActiveChannels -> Number of active antennas # * SInt32 -> nSpectraChannels -> Number antenna used in cross spectra # * UInt32 -> nActiveChannelBits -> Bit indicator of which antennas are in use msb is ant#1 to lsb #32 # * SInt32 -> nV5Extent -> Header Bytes extension (Version 5 is +0 Bytes Till Data) If zero then cross spectra data follows, but if this file were version 6 or greater then the nV5Extent would tell you how many more bytes the version 6 and greater uses until the data. # # DATA: # The data section is a multi-dimensional array of self and cross spectra data. # Repeat For 1 to nRangeCells: # * Float[nDopplerCells] Antenna1 voltage squared amplitude self spectra. # * Float[nDopplerCells] Antenna2 voltage squared amplitude self spectra. # * Float[nDopplerCells] Antenna3 voltage squared amplitude self spectra. # (Warning: Some Antenna3 amplitude values may be negative to indicate noise or interference at those doppler bins. These negative values should be absoluted before use.) # * Complex[nDopplerCells] Antenna 1 to Antenna 2 cross spectra. # * Complex[nDopplerCells] Antenna 1 to Antenna 3 cross spectra. # * Complex[nDopplerCells] Antenna 2 to Antenna 3 cross spectra. # if nCsaKind is 2 then also read or skip # * Float[nDopplerCells] Quality array from zero to one in value. # End Repeat # # Note: To convert self spectra to dBm use: # 10*log10(abs(voltagesquared)) - (-40. + 5.8) # The -40. is conversion loss in the receiver and +5.8 is processing computational gain. # ############################################## # # Author: # Revision: # Date: # use strict; use PDL; # for now hard-wire file, but evenutally this should be either be passed in with ARGV or Getopt::Long my $file = qw( /users/dpath2o/downloads/CSS_SBRD_10_05_01_0045.cs4 ); # various headers depending on which version of software my @hdrnames1 = qw( version timestamp v1_bytes ); my @hdrnames2 = qw( version timestamp v1_bytes cs_type v2_bytes ); my @hdrnames3 = qw( version timestamp v1_bytes cs_type v2_bytes sitename v3_bytes ); my @hdrnames4 = qw( version timestamp v1_bytes cs_type v2_bytes sitename v3_bytes t_coverage csq_delete csq_pref f_start f_sweep f_bandwidth f_up n_Doppler n_range range_i delta_range v4_bytes ); my @hdrnames5 = qw( version timestamp v1_bytes cs_typev 2_bytes sitename v3_bytes t_coverage csq_delete csq_pref f_start f_sweep f_bandwidth f_up n_Doppler n_range range_i delta_range v4_bytes t_output creator creator_version n_active_chs n_channels bit_indicator v5_bytes ); # open the file open(my $fh , "< :raw" , $file ) or die "could not open $file: $!\n"; # define buffer my $buffer; # read in the file version and then make a decision on which header array (above) to use read($fh,$buffer,8) or die "could not read $file: $!\n"; print "characters read to buffer from 8 bit read call: $buffer\n"; my $vers = unpack("s+",$buffer) or die "could not unpack $buffer: $!\n"; print "characters unpacked (this should define version -- i.e. be a signed 16-bit integer): $vers\n"; # condition-based header read/unpack to hash variable %header # read/unpack data to piddles (matrices) # print to text file close($fh) or die "could not close $fh: $!\n";