RDI_PD0_IO.pl
changeset 39 3bddaa514ef5
parent 37 40d85448debf
child 40 6a46e9d31106
equal deleted inserted replaced
38:b1b1ef3f4a9a 39:3bddaa514ef5
     1 #======================================================================
     1 #======================================================================
     2 #                    R D I _ P D 0 _ I O . P L 
     2 #                    R D I _ B B _ R E A D . P L 
     3 #                    doc: Sat Jan 18 14:54:43 2003
     3 #                    doc: Sat Jan 18 14:54:43 2003
     4 #                    dlm: Tue Mar  7 12:07:29 2017
     4 #                    dlm: Tue Aug  8 16:19:45 2017
     5 #                    (c) 2003 A.M. Thurnherr
     5 #                    (c) 2003 A.M. Thurnherr
     6 #                    uE-Info: 1250 39 NIL 0 0 72 0 2 4 NIL ofnI
     6 #					 uE-Info: 562 21 NIL 0 0 72 0 2 4 NIL ofnI
     7 #======================================================================
     7 #======================================================================
     8 
     8 	
     9 # Read RDI PD0 binary data files (*.[0-9][0-9][0-9])
     9 # Read RDI PD0 binary data files (*.[0-9][0-9][0-9])
    10 
    10 	
    11 # HISTORY:
    11 # HISTORY:
    12 #	Jan 18, 2003: - incepted aboard the Aurora Australis (KAOS)
    12 #	Jan 18, 2003: - incepted aboard the Aurora Australis (KAOS)
    13 #	Jan 19, 2003: - continued
    13 #	Jan 19, 2003: - continued
    14 #	Jan 20, 2003: - replaced INTENSITY by AMPLITUDE
    14 #	Jan 20, 2003: - replaced INTENSITY by AMPLITUDE
    15 #	Jan 21, 2003: - changed heading-correction field names
    15 #	Jan 21, 2003: - changed heading-correction field names
    32 #				  - renamed to RDI_BB_Read.pl
    32 #				  - renamed to RDI_BB_Read.pl
    33 #				  - BUG: had used POSIX::mktime with wrong month def!
    33 #				  - BUG: had used POSIX::mktime with wrong month def!
    34 #	Oct 30, 2005: - added WH300 FW16.27 file format
    34 #	Oct 30, 2005: - added WH300 FW16.27 file format
    35 #				  - added DATA_FORMAT_VARIANT
    35 #				  - added DATA_FORMAT_VARIANT
    36 #				  - changed semantics so that first valid ensemble is
    36 #				  - changed semantics so that first valid ensemble is
    37 #				    in E[0] (instead of E[$ensNo-1])
    37 #					in E[0] (instead of E[$ensNo-1])
    38 #	Nov  8, 2005: - replaced UNIXTIME by UNIX_TIME
    38 #	Nov  8, 2005: - replaced UNIXTIME by UNIX_TIME
    39 #				  - added SECNO
    39 #				  - added SECNO
    40 #	Aug 31: 2006: - added DAYNO
    40 #	Aug 31: 2006: - added DAYNO
    41 #	Aug  1, 2007: - BUG: typo in monthLength()
    41 #	Aug  1, 2007: - BUG: typo in monthLength()
    42 #	Sep 18, 2007: - modified readHeader() readDta() WBRhdr() WBRens() to
    42 #	Sep 18, 2007: - modified readHeader() readDta() WBRhdr() WBRens() to
    60 #	May  6, 2014: - loosened input format checks
    60 #	May  6, 2014: - loosened input format checks
    61 #	May  7, 2014: - removed BT_present flag
    61 #	May  7, 2014: - removed BT_present flag
    62 #	Sep  6, 2014: - adapted WBRhdr to >7 data types
    62 #	Sep  6, 2014: - adapted WBRhdr to >7 data types
    63 #	Oct 15, 2014: - implemented work-around for readData() not recognizing
    63 #	Oct 15, 2014: - implemented work-around for readData() not recognizing
    64 #					incomplete ensemble at the end, which seems to imply that there is
    64 #					incomplete ensemble at the end, which seems to imply that there is
    65 #				    a garbage final ensemble that passes the checksum test???
    65 #					a garbage final ensemble that passes the checksum test???
    66 #	Oct  2, 2015: - added &skip_initial_trash()
    66 #	Oct  2, 2015: - added &skip_initial_trash()
    67 #	Dec 18, 2015: - added most data types to WBPofs()
    67 #	Dec 18, 2015: - added most data types to WBPofs()
    68 #				  - BUG: WBPens() requires round() for scaled values
    68 #				  - BUG: WBPens() requires round() for scaled values
    69 #	Jan  9, 2016: - removed system() from writeData()
    69 #	Jan  9, 2016: - removed system() from writeData()
    70 #				  - BUG: WBRhdr() did not set DATA_SOURCE_ID
    70 #				  - BUG: WBRhdr() did not set DATA_SOURCE_ID
    82 #	Aug 23, 2016: - added &clearEns()
    82 #	Aug 23, 2016: - added &clearEns()
    83 #	Nov  9, 2016: - made WBRhdr() return undef on "empty" files
    83 #	Nov  9, 2016: - made WBRhdr() return undef on "empty" files
    84 #	Nov 18, 2016: - BUG: ensNo was not reported correctly in format errors
    84 #	Nov 18, 2016: - BUG: ensNo was not reported correctly in format errors
    85 #	Nov 23, 2016: - no longer set pitch/roll/heading to undef in clearEns()
    85 #	Nov 23, 2016: - no longer set pitch/roll/heading to undef in clearEns()
    86 #	Mar  7, 2016: - renamed round() to stop clashing with ANTSLIB
    86 #	Mar  7, 2016: - renamed round() to stop clashing with ANTSLIB
    87 
    87 #	May 18, 2017: - maded readHeader() more permissive (checkFmt flag)
       
    88 #				  - added partial support for Ocean Surveyor data
       
    89 #	Aug  1, 2017: BUG: minor typo in ocean surveyor code (err check not done?)
       
    90 #	Aug  7, 2017: - added LAG_LENGTH
       
    91 #				  - added SPEED_OF_SOUND to header
       
    92 #	Aug  8, 2017: - replaced croak() by die()
       
    93 #				  - added actual transducer frequencies
       
    94 	
    88 # FIRMWARE VERSIONS:
    95 # FIRMWARE VERSIONS:
    89 #	It appears that different firmware versions generate different file
    96 #	It appears that different firmware versions generate different file
    90 #	structures. Currently (Sep 2005) these routines have been tested
    97 #	structures. Currently (Sep 2005) these routines have been tested
    91 #	with the following firmware versions (as reported by [listHdr]):
    98 #	with the following firmware versions (as reported by [listHdr]):
    92 #
    99 #
    93 #	Firmw.	DATA_FORMAT(_VARIANT)	Owner 	Cruise	FIXED_LEADER_LENGTH
   100 #	Firmw.	DATA_FORMAT(_VARIANT)	Owner	Cruise	FIXED_LEADER_LENGTH
    94 #------------------------------------------------------------
   101 #------------------------------------------------------------
    95 #	05.52	BB150 (1)				UH 		CLIVAR/P16S 42
   102 #	05.52	BB150 (1)				UH		CLIVAR/P16S 42
    96 #	16.12	WH300 (1)				FSU 	A0304		53
   103 #	16.12	WH300 (1)				FSU 	A0304		53
    97 #	16.21	WH300 (1)				LDEO 	NBP0402		53
   104 #	16.21	WH300 (1)				LDEO	NBP0402 	53
    98 #	16.27	WH300 (2)				Nash 	?			59
   105 #	16.27	WH300 (2)				Nash	?			59
    99 
   106 	
   100 # PD0 FILE FORMAT EXTENSIONS:
   107 # PD0 FILE FORMAT EXTENSIONS:
   101 #
   108 #
   102 #	- file creator encoded in DATA_SOURCE_ID
   109 #	- file creator encoded in DATA_SOURCE_ID
   103 #
   110 #
   104 #	- first ensemble uses default RDI DATA_SOURCE_ID because the LDEO_IX
   111 #	- first ensemble uses default RDI DATA_SOURCE_ID because the LDEO_IX
   105 #	  software assumes this
   112 #	  software assumes this
   106 #
   113 #
   107 #	- DATA_SOURCE_ID = 0x7F						original TRDI PD0 file
   114 #	- DATA_SOURCE_ID = 0x7F 					original TRDI PD0 file
   108 #
   115 #
   109 #	- DATA_SOURCE_ID = 0xA0 | PATCHED_MASK		produced by IMP+LADP 
   116 #	- DATA_SOURCE_ID = 0xA0 | PATCHED_MASK		produced by IMP+LADP 
   110 #		PATCHED_MASK & 0x04:						pitch value has been patched
   117 #		PATCHED_MASK & 0x04:						pitch value has been patched
   111 #		PATCHED_MASK & 0x02:						roll value has been patched
   118 #		PATCHED_MASK & 0x02:						roll value has been patched
   112 #		PATCHED_MASK & 0x01:						heading value has been patched
   119 #		PATCHED_MASK & 0x01:						heading value has been patched
   113 #			- PITCH & ROLL can be missing (0x8000 badval as in velocities)
   120 #			- PITCH & ROLL can be missing (0x8000 badval as in velocities)
   114 #			- HEADING can be missing (0xF000 badval, as 0x8000 is valid 327.68 heading)
   121 #			- HEADING can be missing (0xF000 badval, as 0x8000 is valid 327.68 heading)
   115 #
   122 #
   116 #	- DATA_SOURCE_ID = 0xE0 					produced by editPD0
   123 #	- DATA_SOURCE_ID = 0xE0 					produced by editPD0
   117 
   124 	
   118 # NOTES:
   125 # NOTES:
   119 #	- RDI stores data in VAX/Intel byte order (little endian)
   126 #	- RDI stores data in VAX/Intel byte order (little endian)
   120 #	- the output data structure does not exactly mirror the file data
   127 #	- the output data structure does not exactly mirror the file data
   121 #	  structure; the header is not stored at all and the fixed leader
   128 #	  structure; the header is not stored at all and the fixed leader
   122 #	  data are not duplicated in every ensemble
   129 #	  data are not duplicated in every ensemble
   136 #	  setting (e.g. xducer orientation); I'v made an educated guess
   143 #	  setting (e.g. xducer orientation); I'v made an educated guess
   137 #	  as to which fields to move to the ENS array
   144 #	  as to which fields to move to the ENS array
   138 #	- all units except pressure are SI, i.e. in m and m/s
   145 #	- all units except pressure are SI, i.e. in m and m/s
   139 #	- I don't understand the ERROR_STATUS_WORD; here's what 3 different
   146 #	- I don't understand the ERROR_STATUS_WORD; here's what 3 different
   140 #	  instruments returned:
   147 #	  instruments returned:
   141 #	  	0x88000100	FSU instrument during A0304 (Firmware 16.12)
   148 #		0x88000100	FSU instrument during A0304 (Firmware 16.12)
   142 #		0x88008180	LDEO uplooker (slave) during NBP0402 (Firmware 16.21)
   149 #		0x88008180	LDEO uplooker (slave) during NBP0402 (Firmware 16.21)
   143 #		0x00008180	LDEO downlooker (master) during NBP0402 (Firmware 16.21)
   150 #		0x00008180	LDEO downlooker (master) during NBP0402 (Firmware 16.21)
   144 #	  According to the manual (January 2001 version) this would, for example,
   151 #	  According to the manual (January 2001 version) this would, for example,
   145 #	  indicate power failures on both FSU and LDEO slave instruments...
   152 #	  indicate power failures on both FSU and LDEO slave instruments...
   146 
   153 	
   147 # &readData() returns perl obj (ref to anonymous hash) with the following
   154 # &readData() returns perl obj (ref to anonymous hash) with the following
   148 # structure:
   155 # structure:
   149 #
   156 #
   150 #	DATA_SOURCE_ID					scalar		0x7f (Workhorse, also DVL)
   157 #	DATA_SOURCE_ID					scalar		0x7f (Workhorse, also DVL)
   151 #	NUMBER_OF_DATA_TYPES			scalar		6 (no BT) or 7
   158 #	NUMBER_OF_DATA_TYPES			scalar		6 (no BT) or 7
   152 #	ENSEMBLE_BYTES					scalar		?, number of bytes w/o checksum
   159 #	ENSEMBLE_BYTES					scalar		?, number of bytes w/o checksum
   153 #	HEADER_BYTES					scalar		?
   160 #	HEADER_BYTES					scalar		?
   154 #	FIXED_LEADER_BYTES				scalar		42 for BB150; 53 for WH300, 58 for WH600, 59 for WH300(Nash)
   161 #	FIXED_LEADER_BYTES				scalar		42 for BB150; 53 for WH300, 58 for WH600, 59 for WH300(Nash)
   155 #	VARIABLE_LEADER_BYTES			scalar		?
   162 #	VARIABLE_LEADER_BYTES			scalar		?
   156 #	VELOCITY_DATA_BYTES				scalar		?
   163 #	VELOCITY_DATA_BYTES 			scalar		?
   157 #	CORRELATION_DATA_BYTES			scalar		?
   164 #	CORRELATION_DATA_BYTES			scalar		?
   158 #	ECHO_INTENSITY_DATA_BYTES		scalar		?
   165 #	ECHO_INTENSITY_DATA_BYTES		scalar		?
   159 #	PERCENT_GOOD_DATA_BYTES			scalar		?
   166 #	PERCENT_GOOD_DATA_BYTES 		scalar		?
   160 #	BT_PRESENT						bool		NUMBER_OF_DATA_TYPES == 7
   167 #	BT_PRESENT						bool		NUMBER_OF_DATA_TYPES == 7
   161 #	BT_DATA_BYTES					scalar		undefined, ? if BT_PRESENT
   168 #	BT_DATA_BYTES					scalar		undefined, ? if BT_PRESENT
   162 #	CPU_FW_VER						scalar		0--255
   169 #	CPU_FW_VER						scalar		0--255
   163 #	CPU_FW_REV						scalar		0--255
   170 #	CPU_FW_REV						scalar		0--255
   164 #	BEAM_FREQUENCY					scalar		75, 150, 300, 600, 1200, 2400 [kHz]
   171 #	BEAM_FREQUENCY					scalar		75, 150, 300, 600, 1200, 2400 [kHz]
   165 #	CONVEX_BEAM_PATTERN				bool		undefined, 1
   172 #	LAG_LENGTH						scalar		???
       
   173 #	CONVEX_BEAM_PATTERN 			bool		undefined, 1
   166 #	CONCAVE_BEAM_PATTERN			bool		undefined, 1
   174 #	CONCAVE_BEAM_PATTERN			bool		undefined, 1
   167 #	SENSOR_CONFIG					scalar		1--3
   175 #	SENSOR_CONFIG					scalar		1--3
   168 #	XDUCER_HEAD_ATTACHED			bool		undefined, 1
   176 #	XDUCER_HEAD_ATTACHED			bool		undefined, 1
   169 #	BEAM_ANGLE						scalar		15,20,30,undefined=other [deg]
   177 #	BEAM_ANGLE						scalar		15,20,30,undefined=other [deg]
   170 #	N_BEAMS							scalar		4--5
   178 #	N_BEAMS 						scalar		4--5
   171 #	N_DEMODS						scalar		2--3(???),undefined=n/a
   179 #	N_DEMODS						scalar		2--3(???),undefined=n/a
   172 #	N_BINS							scalar		1--128
   180 #	N_BINS							scalar		1--128
   173 #	PINGS_PER_ENSEMBLE				scalar		0--16384
   181 #	PINGS_PER_ENSEMBLE				scalar		0--16384
   174 #	BIN_LENGTH						scalar		0.01--64 [m]
   182 #	BIN_LENGTH						scalar		0.01--64 [m]
   175 #	BLANKING_DISTANCE				scalar		0-99.99 [m]
   183 #	BLANKING_DISTANCE				scalar		0-99.99 [m]
   176 #	MIN_CORRELATION					scalar		0--255
   184 #	MIN_CORRELATION 				scalar		0--255
   177 #	N_CODE_REPETITIONS				scalar		0--255
   185 #	N_CODE_REPETITIONS				scalar		0--255
   178 #	MIN_PERCENT_GOOD				scalar		1--100 [%]
   186 #	MIN_PERCENT_GOOD				scalar		1--100 [%]
   179 #	MAX_ERROR_VELOCITY				scalar		0--5 [m/s]
   187 #	MAX_ERROR_VELOCITY				scalar		0--5 [m/s]
   180 #	TIME_BETWEEN_PINGS				scalar		0--? [s]
   188 #	TIME_BETWEEN_PINGS				scalar		0--? [s]
   181 #	BEAM_COORDINATES				bool		undefined,1
   189 #	BEAM_COORDINATES				bool		undefined,1
   182 #	INSTRUMENT_COORDINATES			bool		undefined,1
   190 #	INSTRUMENT_COORDINATES			bool		undefined,1
   183 #	SHIP_COORDINATES				bool		undefined,1
   191 #	SHIP_COORDINATES				bool		undefined,1
   184 #	EARTH_COORDINATES				bool		undefined,1
   192 #	EARTH_COORDINATES				bool		undefined,1
   185 #	PITCH_AND_ROLL_USED				bool		undefined,1
   193 #	PITCH_AND_ROLL_USED 			bool		undefined,1
   186 #	USE_3_BEAM_ON_LOW_CORR			bool		undefined,1
   194 #	USE_3_BEAM_ON_LOW_CORR			bool		undefined,1
   187 #	BIN_MAPPING_ALLOWED				bool		undefined,1
   195 #	BIN_MAPPING_ALLOWED 			bool		undefined,1
   188 #	HEADING_ALIGNMENT 				scalar		-179.99..180 [deg]
   196 #	HEADING_ALIGNMENT				scalar		-179.99..180 [deg]
   189 #	HEADING_BIAS			 		scalar		-179.99..180 [deg]
   197 #	HEADING_BIAS					scalar		-179.99..180 [deg]
   190 #	CALCULATE_SPEED_OF_SOUND		bool		undefined,1
   198 #	CALCULATE_SPEED_OF_SOUND		bool		undefined,1
   191 #	USE_PRESSURE_SENSOR				bool		undefined,1
   199 #	USE_PRESSURE_SENSOR 			bool		undefined,1
   192 #	USE_COMPASS						bool		undefined,1
   200 #	USE_COMPASS 					bool		undefined,1
   193 #	USE_PITCH_SENSOR				bool		undefined,1
   201 #	USE_PITCH_SENSOR				bool		undefined,1
   194 #	USE_ROLL_SENSOR					bool		undefined,1
   202 #	USE_ROLL_SENSOR 				bool		undefined,1
   195 #	USE_CONDUCTIVITY_SENSOR			bool		undefined,1
   203 #	USE_CONDUCTIVITY_SENSOR 		bool		undefined,1
   196 #	USE_TEMPERATURE_SENSOR			bool		undefined,1
   204 #	USE_TEMPERATURE_SENSOR			bool		undefined,1
   197 #	SPEED_OF_SOUND_CALCULATED		bool		undefined,1
   205 #	SPEED_OF_SOUND_CALCULATED		bool		undefined,1
   198 #	PRESSURE_SENSOR_AVAILABLE		bool		undefined,1
   206 #	PRESSURE_SENSOR_AVAILABLE		bool		undefined,1
   199 #	COMPASS_AVAILABLE				bool		undefined,1
   207 #	COMPASS_AVAILABLE				bool		undefined,1
   200 #	PITCH_SENSOR_AVAILABLE			bool		undefined,1
   208 #	PITCH_SENSOR_AVAILABLE			bool		undefined,1
   201 #	ROLL_SENSOR_AVAILABLE			bool		undefined,1
   209 #	ROLL_SENSOR_AVAILABLE			bool		undefined,1
   202 #	CONDUCTIVITY_SENSOR_AVAILABLE	bool		undefined,1
   210 #	CONDUCTIVITY_SENSOR_AVAILABLE	bool		undefined,1
   203 #	TEMPERATURE_SENSOR_AVAILABLE	bool		undefined,1
   211 #	TEMPERATURE_SENSOR_AVAILABLE	bool		undefined,1
   204 #	DISTANCE_TO_BIN1_CENTER			scalar		0--655.35 [m]
   212 #	DISTANCE_TO_BIN1_CENTER 		scalar		0--655.35 [m]
   205 #	TRANSMITTED_PULSE_LENGTH		scalar		0--655.35 [m]
   213 #	TRANSMITTED_PULSE_LENGTH		scalar		0--655.35 [m]
   206 #	RL_FIRST_BIN					scalar		1--128
   214 #	RL_FIRST_BIN					scalar		1--128
   207 #	RL_LAST_BIN						scalar		1--128
   215 #	RL_LAST_BIN 					scalar		1--128
   208 #	FALSE_TARGET_THRESHOLD			scalar		0--254, undefined=disabled
   216 #	FALSE_TARGET_THRESHOLD			scalar		0--254, undefined=disabled
   209 #	LOW_LATENCY_SETTING				scalar		0--5(???)
   217 #	LOW_LATENCY_SETTING 			scalar		0--5(???)
   210 #	TRANSMIT_LAG_DISTANCE			scalar		0--655.35 [m]
   218 #	TRANSMIT_LAG_DISTANCE			scalar		0--655.35 [m]
   211 #	CPU_SERIAL_NUMBER				scalar		undefined, 0--65535 if WH300
   219 #	CPU_SERIAL_NUMBER				scalar		undefined, 0--65535 if WH300
   212 #	NARROW_BANDWIDTH				bool		undefined,1 (only set if WH300)
   220 #	NARROW_BANDWIDTH				bool		undefined,1 (only set if WH300)
   213 #	WIDE_BANDWIDTH					bool		undefined,1 (only set if WH300)
   221 #	WIDE_BANDWIDTH					bool		undefined,1 (only set if WH300)
   214 #	TRANSMIT_POWER					scalar		undefined, 0--255(high) if WH300
   222 #	TRANSMIT_POWER					scalar		undefined, 0--255(high) if WH300
   215 #	TRANSMIT_POWER_HIGH				bool		undefined,1 (only set if WH300)
   223 #	TRANSMIT_POWER_HIGH 			bool		undefined,1 (only set if WH300)
   216 #	BT_PINGS_PER_ENSEMBLE			scalar		0--999
   224 #	BT_PINGS_PER_ENSEMBLE			scalar		0--999
   217 #	BT_DELAY_BEFORE_REACQUIRE		scalar		0--999
   225 #	BT_DELAY_BEFORE_REACQUIRE		scalar		0--999
   218 #	BT_MIN_CORRELATION				scalar		0--255
   226 #	BT_MIN_CORRELATION				scalar		0--255
   219 #	BT_MIN_EVAL_AMPLITUDE			scalar		0--255
   227 #	BT_MIN_EVAL_AMPLITUDE			scalar		0--255
   220 #	BT_MIN_PERCENT_GOOD				scalar		0--100 [%]
   228 #	BT_MIN_PERCENT_GOOD 			scalar		0--100 [%]
   221 #	BT_MODE							scalar		4,5,6(?)
   229 #	BT_MODE 						scalar		4,5,6(?)
   222 #	BT_MAX_ERROR_VELOCITY			scalar		0--5 [m/s], undef=not screened
   230 #	BT_MAX_ERROR_VELOCITY			scalar		0--5 [m/s], undef=not screened
   223 #	BT_RL_MIN_SIZE					scalar		0--99.9 [m]
   231 #	BT_RL_MIN_SIZE					scalar		0--99.9 [m]
   224 #	BT_RL_NEAR						scalar		0--999.9 [m]
   232 #	BT_RL_NEAR						scalar		0--999.9 [m]
   225 #	BT_RL_FAR						scalar		0--999.9 [m]
   233 #	BT_RL_FAR						scalar		0--999.9 [m]
   226 #	BT_MAX_TRACKING_DEPTH			scalar		8--999.9 [m]
   234 #	BT_MAX_TRACKING_DEPTH			scalar		8--999.9 [m]
   227 #	ENSEMBLE[ensemble_no-1]			array		ensemble info
   235 #	ENSEMBLE[ensemble_no-1] 		array		ensemble info
   228 #		XDUCER_FACING_UP			bool		undefined, 1
   236 #		XDUCER_FACING_UP			bool		undefined, 1
   229 #		XDUCER_FACING_DOWN			bool		undefined, 1
   237 #		XDUCER_FACING_DOWN			bool		undefined, 1
   230 #		N_BEAMS_USED				scalar		3,4,5(?)
   238 #		N_BEAMS_USED				scalar		3,4,5(?)
   231 #		NUMBER						scalar		1--16777215
   239 #		NUMBER						scalar		1--16777215
   232 #		BUILT_IN_TEST_ERROR			scalar		?,undefined=none
   240 #		BUILT_IN_TEST_ERROR 		scalar		?,undefined=none
   233 #		SPEED_OF_SOUND				scalar		1400--1600 [m/s]
   241 #		SPEED_OF_SOUND				scalar		1400--1600 [m/s]
   234 #		XDUCER_DEPTH				scalar		0.1--999.9 [m]
   242 #		XDUCER_DEPTH				scalar		0.1--999.9 [m]
   235 #		HEADING						scalar		0--359.99 [deg]    --- IMP EXTENSION: undef
   243 #		HEADING 					scalar		0--359.99 [deg]    --- IMP EXTENSION: undef
   236 #		PITCH						scalar		-20.00-20.00 [deg] --- IMP EXTENSION: undef
   244 #		PITCH						scalar		-20.00-20.00 [deg] --- IMP EXTENSION: undef
   237 #		ROLL						scalar		-20.00-20.00 [deg] --- IMP EXTENSION: undef
   245 #		ROLL						scalar		-20.00-20.00 [deg] --- IMP EXTENSION: undef
   238 #		SALINITY					scalar		0-40 [psu]
   246 #		SALINITY					scalar		0-40 [psu]
   239 #		TEMPERATURE					scalar		-5.00--40.00 [deg]
   247 #		TEMPERATURE 				scalar		-5.00--40.00 [deg]
   240 #		MIN_PRE_PING_WAIT_TIME		scalar		? [s]
   248 #		MIN_PRE_PING_WAIT_TIME		scalar		? [s]
   241 #		HEADING_STDDEV				scalar		0-180 [deg]
   249 #		HEADING_STDDEV				scalar		0-180 [deg]
   242 #		PITCH_STDDEV				scalar		0.0-20.0 [deg]
   250 #		PITCH_STDDEV				scalar		0.0-20.0 [deg]
   243 #		ROLL_STDDEV					scalar		0.0-20.0 [deg]
   251 #		ROLL_STDDEV 				scalar		0.0-20.0 [deg]
   244 #		ADC_XMIT_CURRENT			scalar		0--255
   252 #		ADC_XMIT_CURRENT			scalar		0--255
   245 #		ADC_XMIT_VOLTAGE			scalar		0--255
   253 #		ADC_XMIT_VOLTAGE			scalar		0--255
   246 #		ADC_AMBIENT_TEMPERATURE		scalar		0--255
   254 #		ADC_AMBIENT_TEMPERATURE 	scalar		0--255
   247 #		ADC_PRESSURE_PLUS			scalar		0--255
   255 #		ADC_PRESSURE_PLUS			scalar		0--255
   248 #		ADC_PRESSURE_MINUS			scalar		0--255
   256 #		ADC_PRESSURE_MINUS			scalar		0--255
   249 #		ADC_ATTITUDE_TEMPERATURE	scalar		0--255
   257 #		ADC_ATTITUDE_TEMPERATURE	scalar		0--255
   250 #		ADC_ATTITUDE				scalar		0--255
   258 #		ADC_ATTITUDE				scalar		0--255
   251 #		ADC_CONTAMINATION			scalar		0--255
   259 #		ADC_CONTAMINATION			scalar		0--255
   252 #		ERROR_STATUS_WORD			scalar		undefined, ? (only set if WH300)
   260 #		ERROR_STATUS_WORD			scalar		undefined, ? (only set if WH300)
   253 #		PRESSURE					scalar		undefined, ?-? [dbar] (only set if WH300)
   261 #		PRESSURE					scalar		undefined, ?-? [dbar] (only set if WH300)
   254 #		PRESSURE_STDDEV				scalar		undefined, ?-? [dbar] (only set if WH300)
   262 #		PRESSURE_STDDEV 			scalar		undefined, ?-? [dbar] (only set if WH300)
   255 #		DATE						string		MM/DD/YYYY
   263 #		DATE						string		MM/DD/YYYY
   256 #		YEAR						scalar		?
   264 #		YEAR						scalar		?
   257 #		MONTH						scalar		1--12
   265 #		MONTH						scalar		1--12
   258 #		DAY							scalar		1--31
   266 #		DAY 						scalar		1--31
   259 #		TIME						string		HH:MM:SS.hh
   267 #		TIME						string		HH:MM:SS.hh
   260 #		HOUR						scalar		0--23
   268 #		HOUR						scalar		0--23
   261 #		MINUTE						scalar		0--59
   269 #		MINUTE						scalar		0--59
   262 #		SECONDS						scalar		0--59.99
   270 #		SECONDS 					scalar		0--59.99
   263 #		UNIX_TIME					scalar		0--?
   271 #		UNIX_TIME					scalar		0--?
   264 #		SECNO						scalar		0--? (number of seconds since daystart)
   272 #		SECNO						scalar		0--? (number of seconds since daystart)
   265 #		DAYNO						double		fractional day number since start of current year (1.0 is midnight Jan 1st)
   273 #		DAYNO						double		fractional day number since start of current year (1.0 is midnight Jan 1st)
   266 #		VELOCITY[bin][beam]			scalars		-32.767--32.768 [m/s], undef=bad
   274 #		VELOCITY[bin][beam] 		scalars 	-32.767--32.768 [m/s], undef=bad
   267 #		CORRELATION[bin][beam]		scalars		1--255, undefined=bad
   275 #		CORRELATION[bin][beam]		scalars 	1--255, undefined=bad
   268 #		ECHO_AMPLITUDE[bin][beam]	scalars		0--255
   276 #		ECHO_AMPLITUDE[bin][beam]	scalars 	0--255
   269 #		PERCENT_GOOD[bin][beam]		scalars		0--255
   277 #		PERCENT_GOOD[bin][beam] 	scalars 	0--255
   270 #		BT_RANGE[beam]				scalars		tons [m]
   278 #		BT_RANGE[beam]				scalars 	tons [m]
   271 #		BT_VELOCITY[beam]			scalars		see VELOCITY
   279 #		BT_VELOCITY[beam]			scalars 	see VELOCITY
   272 #		BT_CORRELATION[beam]		scalars		see CORRELATION
   280 #		BT_CORRELATION[beam]		scalars 	see CORRELATION
   273 #		BT_EVAL_AMPLITUDE[beam]		scalars		0--255
   281 #		BT_EVAL_AMPLITUDE[beam] 	scalars 	0--255
   274 #		BT_PERCENT_GOOD[beam]		scalars		see PERCENT_GOOD
   282 #		BT_PERCENT_GOOD[beam]		scalars 	see PERCENT_GOOD
   275 #		BT_RL_VELOCITY[beam]		scalars		see VELOCITY
   283 #		BT_RL_VELOCITY[beam]		scalars 	see VELOCITY
   276 #		BT_RL_CORRELATION[beam]		scalars		see CORRELATION
   284 #		BT_RL_CORRELATION[beam] 	scalars 	see CORRELATION
   277 #		BT_RL_ECHO_AMPLITUDE[beam]	scalars		see ECHO_AMPLITUDE
   285 #		BT_RL_ECHO_AMPLITUDE[beam]	scalars 	see ECHO_AMPLITUDE
   278 #		BT_RL_PERCENT_GOOD[beam]	scalars		see PERCENT_GOOD
   286 #		BT_RL_PERCENT_GOOD[beam]	scalars 	see PERCENT_GOOD
   279 #		BT_SIGNAL_STRENGTH[beam]	scalars		0--255
   287 #		BT_SIGNAL_STRENGTH[beam]	scalars 	0--255
   280 #		HIGH_GAIN					bool		1, undefined
   288 #		HIGH_GAIN					bool		1, undefined
   281 #		LOW_GAIN					bool		1, undefined
   289 #		LOW_GAIN					bool		1, undefined
   282 
   290 	
   283 use strict;
   291 	use strict;
   284 use Time::Local;						# timegm()
   292 	use Time::Local;						# timegm()
   285 
   293 	
   286 #----------------------------------------------------------------------
   294 #----------------------------------------------------------------------
   287 # Time Conversion Subroutines
   295 # Time Conversion Subroutines
   288 #----------------------------------------------------------------------
   296 #----------------------------------------------------------------------
   289 
   297 	
   290 sub monthLength($$)										# of days in month
   298 	sub monthLength($$) 									# of days in month
   291 {
   299 	{
   292     my($Y,$M) = @_;
   300 		my($Y,$M) = @_;
   293 
   301 	
   294     return 31 if ($M==1 || $M==3 || $M==5 || $M==7 ||
   302 		return 31 if ($M==1 || $M==3 || $M==5 || $M==7 ||
   295                   $M==8 || $M==10 || $M==12);
   303 					  $M==8 || $M==10 || $M==12);
   296     return 30 if ($M==4 || $M==6 || $M==9 || $M==11);
   304 		return 30 if ($M==4 || $M==6 || $M==9 || $M==11);
   297     return 28 if ($Y%4 != 0);
   305 		return 28 if ($Y%4 != 0);
   298     return 29 if ($Y%100 != 0);
   306 		return 29 if ($Y%100 != 0);
   299     return 28 if ($Y%400 > 0);
   307 		return 28 if ($Y%400 > 0);
   300     return 29;
   308 		return 29;
   301 }
   309 	}
   302 
   310 	
   303 { my($epoch,$lM,$lD,$lY,$ldn);							# static scope
   311 	{ my($epoch,$lM,$lD,$lY,$ldn);							# static scope
   304 
   312 	
   305   sub dayNo($$$$$$)
   313 	  sub dayNo($$$$$$)
   306   {
   314 	  {
   307 	  my($Y,$M,$D,$h,$m,$s) = @_;
   315 		  my($Y,$M,$D,$h,$m,$s) = @_;
   308 	  my($dn);
   316 		  my($dn);
   309   
   317 	  
   310 	  if ($Y==$lY && $M==$lM && $D==$lD) {				# same day as last samp
   318 		  if ($Y==$lY && $M==$lM && $D==$lD) {				# same day as last samp
   311 		  $dn = $ldn;
   319 			  $dn = $ldn;
   312 	  } else {											# new day
   320 		  } else {											# new day
   313 		  $epoch = $Y unless defined($epoch);			# 1st call
   321 			  $epoch = $Y unless defined($epoch);			# 1st call
   314 		  $lY = $Y; $lM = $M; $lD = $D;					# store
   322 			  $lY = $Y; $lM = $M; $lD = $D; 				# store
   315   
   323 	  
   316 		  for ($dn=0,my($cY)=$epoch; $cY<$Y; $cY++) {	# multiple years
   324 			  for ($dn=0,my($cY)=$epoch; $cY<$Y; $cY++) {	# multiple years
   317 			  $dn += 337 + &monthLength($Y,$M);
   325 				  $dn += 337 + &monthLength($Y,$M);
       
   326 			  }
       
   327 	  
       
   328 			  $dn += $D;									# day in month
       
   329 			  while (--$M > 0) {							# preceding months
       
   330 				  $dn += &monthLength($Y,$M);
       
   331 			  }
       
   332 	
       
   333 			  $ldn = $dn;									# store
   318 		  }
   334 		  }
   319   
   335 		  return $dn + $h/24 + $m/24/60 + $s/24/3600;
   320 		  $dn += $D;									# day in month
       
   321 		  while (--$M > 0) {							# preceding months
       
   322 			  $dn += &monthLength($Y,$M);
       
   323 		  }
       
   324 
       
   325 		  $ldn = $dn;									# store
       
   326 	  }
   336 	  }
   327 	  return $dn + $h/24 + $m/24/60 + $s/24/3600;
   337 	
   328   }
   338 	} # static scope
   329 
   339 	
   330 } # static scope
       
   331 
       
   332 #----------------------------------------------------------------------
   340 #----------------------------------------------------------------------
   333 # Read Data
   341 # Read Data
   334 #----------------------------------------------------------------------
   342 #----------------------------------------------------------------------
   335 
   343 	
   336 my($WBRcfn,$WBPcfn);									# current file names for reading/patching
   344 	my($WBRcfn,$WBPcfn);									# current file names for reading/patching
   337 my($BIT_errors) = 0;									# built-in-test errors
   345 	my($BIT_errors) = 0;									# built-in-test errors
   338 
   346 	
   339 my($FmtErr) = "%s: illegal %s Id 0x%04x at ensemble %d";
   347 	my($FmtErr) = "%s: illegal %s Id 0x%04x at ensemble %d";
   340 
   348 	
   341 #----------------------------------------------------------------------
   349 #----------------------------------------------------------------------
   342 # skip to first valid ensemble (skip over initial garbage)
   350 # skip to first valid ensemble (skip over initial garbage)
   343 #----------------------------------------------------------------------
   351 #----------------------------------------------------------------------
   344 
   352 	
   345 sub skip_initial_trash(@)
   353 	sub skip_initial_trash(@)
   346 {
   354 	{
   347 	my($quiet) = @_;
   355 		my($quiet) = @_;
   348 	my($buf,$dta);
   356 		my($buf,$dta);
   349 
   357 	
   350 	my($found) = 0;										# zero consecutive 0x7f found
   358 		my($found) = 0; 									# zero consecutive 0x7f found
   351 	my($skipped) = 0;
   359 		my($skipped) = 0;
   352 	while ($found < 2) {
   360 		while ($found < 2) {
   353 		sysread(WBRF,$buf,1) == 1 || last;
   361 			sysread(WBRF,$buf,1) == 1 || last;
   354 		($dta) = unpack('C',$buf);
   362 			($dta) = unpack('C',$buf);
   355 		if ($dta == 0x7f) {
   363 			if ($dta == 0x7f) {
   356 			$found++;
   364 				$found++;
   357 		} elsif ($found==1 && ($dta==0xE0 || ($dta&0xF0==0xA0 && $dta&0x0F<8))) {
   365 			} elsif ($found==1 && ($dta==0xE0 || ($dta&0xF0==0xA0 && $dta&0x0F<8))) {
   358 			$found++;
   366 				$found++;
   359 		} elsif ($found == 0) {
   367 			} elsif ($found == 0) {
   360 			$skipped++;
   368 				$skipped++;
   361 		} else {
   369 			} else {
   362 			$skipped += $found;
   370 				$skipped += $found;
   363 			$found = 0;
   371 				$found = 0;
   364 		}
   372 			}
       
   373 		}
       
   374 		die("$WBRcfn: no valid ensemble header found [$!]\n")
       
   375 			if ($found < 2);
       
   376 		printf(STDERR "WARNING: %d bytes of initial garbage\n",$skipped)
       
   377 			if ($skipped > 0 && !$quiet);
       
   378 		return sysseek(WBRF,-2,1);
   365 	}
   379 	}
   366 	die("$WBRcfn: no valid ensemble header found [$!]\n")
   380 	
   367 		if ($found < 2);
       
   368 	printf(STDERR "WARNING: %d bytes of initial garbage\n",$skipped)
       
   369 		if ($skipped > 0 && !$quiet);
       
   370 	return sysseek(WBRF,-2,1);
       
   371 }
       
   372 
       
   373 #----------------------------------------------------------------------
   381 #----------------------------------------------------------------------
   374 # readHeader(file_name,^dta) WBRhdr(^data)
   382 # readHeader(file_name,^dta) WBRhdr(^data)
   375 #	- read header data
   383 #	- read header data
   376 #	- also includes some data from 1st ens
   384 #	- also includes some data from 1st ens
   377 #----------------------------------------------------------------------
   385 #----------------------------------------------------------------------
   378 
   386 	
   379 sub readHeader(@)
   387 sub readHeader(@)
   380 {
   388 {
   381 	my($fn,$dta) = @_;
   389 	my($fn,$dta) = @_;
   382 	$WBRcfn = $fn;
   390 	$WBRcfn = $fn;
   383     open(WBRF,$WBRcfn) || die("$WBRcfn: $!");
   391 	open(WBRF,$WBRcfn) || die("$WBRcfn: $!");
   384     WBRhdr($dta) || die("$WBRcfn: Insufficient data\n");
   392 	WBRhdr($dta) || die("$WBRcfn: Insufficient data\n");
   385 }
   393 }
   386 
   394 
   387 sub WBRhdr($)
   395 sub WBRhdr(@)
   388 {
   396 {
   389 	my($dta) = @_;
   397 	my($dta,$checkFmt) = @_;
   390 	my($start_ens,$buf,$hid,$did,$Ndt,$B,$W,$i,$dummy,$id,@WBRofs);
   398 	my($start_ens,$buf,$hid,$did,$Ndt,$B,$W,$i,$dummy,$id,@WBRofs);
   391 	my($B1,$B2,$B3,$B4,$B5,$B6,$B7,$W1,$W2,$W3,$W4,$W5);
   399 	my($B1,$B2,$B3,$B4,$B5,$B6,$B7,$W1,$W2,$W3,$W4,$W5);
   392 	
   400 	my($BT_dt);
       
   401     
   393 	#--------------------
   402 	#--------------------
   394 	# HEADER
   403 	# HEADER
   395 	#--------------------
   404 	#--------------------
   396 
   405 
   397 	skip_initial_trash();
   406 	skip_initial_trash();
   415 		$dta->{PRODUCER} = 'editPD0 (Thurnherr software)';
   424 		$dta->{PRODUCER} = 'editPD0 (Thurnherr software)';
   416 	} else {
   425 	} else {
   417 		$dta->{PRODUCER} = sprintf('unknown (0x%02X)');
   426 		$dta->{PRODUCER} = sprintf('unknown (0x%02X)');
   418 	}
   427 	}
   419 
   428 
   420 	printf(STDERR "WARNING: unexpected number of data types (%d)\n",
   429 	if ($checkFmt) {
   421 		$dta->{NUMBER_OF_DATA_TYPES})
   430 		printf(STDERR "WARNING: unexpected number of data types (%d)\n",
   422 			unless ($dta->{NUMBER_OF_DATA_TYPES} == 6 ||
   431 			$dta->{NUMBER_OF_DATA_TYPES})
   423 					$dta->{NUMBER_OF_DATA_TYPES} == 7);
   432 				unless ($dta->{NUMBER_OF_DATA_TYPES} == 6 ||
   424 ##	$dta->{BT_PRESENT} = ($dta->{NUMBER_OF_DATA_TYPES} == 7);
   433 						$dta->{NUMBER_OF_DATA_TYPES} == 7);
   425 	$dta->{BT_PRESENT} = ($dta->{NUMBER_OF_DATA_TYPES} >= 7);
   434 		$dta->{BT_PRESENT} = ($dta->{NUMBER_OF_DATA_TYPES} >= 7);
       
   435 	}
   426 					  
   436 					  
   427 	sysread(WBRF,$buf,2*$dta->{NUMBER_OF_DATA_TYPES})
   437 	sysread(WBRF,$buf,2*$dta->{NUMBER_OF_DATA_TYPES})
   428 		== 2*$dta->{NUMBER_OF_DATA_TYPES}
   438 		== 2*$dta->{NUMBER_OF_DATA_TYPES}
   429 			|| die("$WBRcfn: $!");
   439 			|| die("$WBRcfn: $!");
   430 	@WBRofs = unpack("v$dta->{NUMBER_OF_DATA_TYPES}",$buf);
   440 	@WBRofs = unpack("v$dta->{NUMBER_OF_DATA_TYPES}",$buf);
   431 
       
   432 	$dta->{HEADER_BYTES} 					= $WBRofs[0];
       
   433 	$dta->{FIXED_LEADER_BYTES} 				= $WBRofs[1] - $WBRofs[0];
       
   434 	$dta->{VARIABLE_LEADER_BYTES}			= $WBRofs[2] - $WBRofs[1];
       
   435 	$dta->{VELOCITY_DATA_BYTES}				= $WBRofs[3] - $WBRofs[2];
       
   436 	$dta->{CORRELATION_DATA_BYTES}			= $WBRofs[4] - $WBRofs[3];
       
   437 	$dta->{ECHO_INTENSITY_DATA_BYTES}		= $WBRofs[5] - $WBRofs[4];
       
   438 	if ($dta->{BT_PRESENT}) {
       
   439 		$dta->{PERCENT_GOOD_DATA_BYTES}		= $WBRofs[6] - $WBRofs[5];
       
   440 		$dta->{BT_DATA_BYTES}				= $dta->{ENSEMBLE_BYTES} - 4 - $WBRofs[6];
       
   441 	} else {
       
   442 		$dta->{PERCENT_GOOD_DATA_BYTES}		= $dta->{ENSEMBLE_BYTES} - 4 - $WBRofs[5];
       
   443 	}
       
   444 
       
   445 	if ($dta->{FIXED_LEADER_BYTES} == 42) {				# Eric Firing's old instrument I used in 2004
       
   446 		$dta->{INSTRUMENT_TYPE} = 'BB150';
       
   447 	} elsif ($dta->{FIXED_LEADER_BYTES} == 53) {		# old firmware: no serial numbers
       
   448 		$dta->{INSTRUMENT_TYPE} = 'Workhorse';	
       
   449 	} elsif ($dta->{FIXED_LEADER_BYTES} == 59) {		# new firmware: with serial numbers
       
   450 		$dta->{INSTRUMENT_TYPE} = 'Workhorse';
       
   451     } elsif ($dta->{FIXED_LEADER_BYTES} == 58) {		# DVL
       
   452 		$dta->{INSTRUMENT_TYPE} = 'Explorer';
       
   453     } 
       
   454 
       
   455 #	for ($i=0; $i<$dta->{NUMBER_OF_DATA_TYPES}; $i++) {
   441 #	for ($i=0; $i<$dta->{NUMBER_OF_DATA_TYPES}; $i++) {
   456 #		printf(STDERR "\nWBRofs[$i] = %d",$WBRofs[$i]);
   442 #		printf(STDERR "\nWBRofs[$i] = %d",$WBRofs[$i]);
   457 #	}
   443 #	}
   458 
   444 
       
   445 
       
   446 	$dta->{HEADER_BYTES}					= $WBRofs[0];
       
   447 	$dta->{FIXED_LEADER_BYTES}				= $WBRofs[1] - $WBRofs[0];
       
   448 	$dta->{VARIABLE_LEADER_BYTES}			= $WBRofs[2] - $WBRofs[1];
       
   449     if ($checkFmt) {
       
   450 		$dta->{VELOCITY_DATA_BYTES} 			= $WBRofs[3] - $WBRofs[2];
       
   451 		$dta->{CORRELATION_DATA_BYTES}			= $WBRofs[4] - $WBRofs[3];
       
   452 		$dta->{ECHO_INTENSITY_DATA_BYTES}		= $WBRofs[5] - $WBRofs[4];
       
   453 		if ($dta->{BT_PRESENT}) {
       
   454 			$dta->{PERCENT_GOOD_DATA_BYTES} 	= $WBRofs[6] - $WBRofs[5];
       
   455 			$dta->{BT_DATA_BYTES}				= $dta->{ENSEMBLE_BYTES} - 4 - $WBRofs[6];
       
   456 		} else {
       
   457 			$dta->{PERCENT_GOOD_DATA_BYTES} 	= $dta->{ENSEMBLE_BYTES} - 4 - $WBRofs[5];
       
   458 	    }
       
   459     }
       
   460 
       
   461 	if ($dta->{FIXED_LEADER_BYTES} == 42) { 			# Eric Firing's old instrument I used in 2004
       
   462 		$dta->{INSTRUMENT_TYPE} = 'BB150';
       
   463 	} elsif ($dta->{FIXED_LEADER_BYTES} == 53) {		# old firmware: no serial numbers
       
   464 		$dta->{INSTRUMENT_TYPE} = 'Workhorse';  
       
   465 	} elsif ($dta->{FIXED_LEADER_BYTES} == 59) {		# new firmware: with serial numbers
       
   466 		$dta->{INSTRUMENT_TYPE} = 'Workhorse';
       
   467 	} elsif ($dta->{FIXED_LEADER_BYTES} == 58) {		# DVL
       
   468 		$dta->{INSTRUMENT_TYPE} = 'Explorer';
       
   469 	} elsif ($dta->{FIXED_LEADER_BYTES} == 60) {		# OS75
       
   470 		$dta->{INSTRUMENT_TYPE} = 'Ocean Surveyor';
       
   471     } else {
       
   472 		$dta->{INSTRUMENT_TYPE} = 'unknown';
       
   473     }
       
   474 
       
   475 	#--------------------------------
       
   476 	# Variable Leader: SPEED_OF_SOUND
       
   477 	#--------------------------------
       
   478 
       
   479 	sysseek(WBRF,$start_ens+$WBRofs[1],0) || die("$WBRcfn: $!");
       
   480 	sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
       
   481 	$id = unpack('v',$buf);
       
   482 	if ($dta->{INSTRUMENT_TYPE} eq 'Ocean Surveyor') {
       
   483 		$id == 0x0081 || printf(STDERR $FmtErr."\n",$WBRcfn,"Variable Leader",$id,1);
       
   484     } else {
       
   485 		$id == 0x0080 || printf(STDERR $FmtErr."\n",$WBRcfn,"Variable Leader",$id,1);
       
   486     }
       
   487 	sysseek(WBRF,12,1) || die("$WBRcfn: $!");							# skip up to speed of sound
       
   488 	sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
       
   489 	$dta->{SPEED_OF_SOUND} = unpack('v',$buf);
       
   490 	
   459 	#----------------------------------
   491 	#----------------------------------
   460 	# Check Data Format of 1st Ensemble
   492 	# Check Data Format of 1st Ensemble
   461 	#----------------------------------
   493 	#----------------------------------
   462 
   494 
   463 	sysseek(WBRF,$start_ens+$WBRofs[1],0) || die("$WBRcfn: $!");
   495 	if ($checkFmt) {
   464 	sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   496 		sysseek(WBRF,$start_ens+$WBRofs[2],0) || die("$WBRcfn: $!");
   465 	$id = unpack('v',$buf);
   497 		sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   466 	$id == 0x0080 || printf(STDERR $FmtErr."\n",$WBRcfn,"Variable Leader",$id,1);
   498 		$id = unpack('v',$buf);
   467 
   499 		$id == 0x0100 || printf(STDERR $FmtErr."\n",$WBRcfn,"Velocity Data",$id,1);
   468 	sysseek(WBRF,$start_ens+$WBRofs[2],0) || die("$WBRcfn: $!");
   500 	
   469 	sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   501 		sysseek(WBRF,$start_ens+$WBRofs[3],0) || die("$WBRcfn: $!");
   470 	$id = unpack('v',$buf);
   502 		sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   471 	$id == 0x0100 || printf(STDERR $FmtErr."\n",$WBRcfn,"Velocity Data",$id,1);
   503 		$id = unpack('v',$buf);
   472 
   504 		$id == 0x0200 || printf(STDERR $FmtErr."\n",$WBRcfn,"Correlation Data",$id,1);
   473 	sysseek(WBRF,$start_ens+$WBRofs[3],0) || die("$WBRcfn: $!");
   505 	    
   474 	sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   506 		sysseek(WBRF,$start_ens+$WBRofs[4],0) || die("$WBRcfn: $!");
   475 	$id = unpack('v',$buf);
   507 		sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   476 	$id == 0x0200 || printf(STDERR $FmtErr."\n",$WBRcfn,"Correlation Data",$id,1);
   508 		$id = unpack('v',$buf);
   477     
   509 		$id == 0x0300 || printf(STDERR $FmtErr."\n",$WBRcfn,"Echo Intensity",$id,1);
   478 	sysseek(WBRF,$start_ens+$WBRofs[4],0) || die("$WBRcfn: $!");
   510 	
   479 	sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   511 		sysseek(WBRF,$start_ens+$WBRofs[5],0) || die("$WBRcfn: $!");
   480 	$id = unpack('v',$buf);
   512 		sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   481 	$id == 0x0300 || printf(STDERR $FmtErr."\n",$WBRcfn,"Echo Intensity",$id,1);
   513 		$id = unpack('v',$buf);
   482 
   514 		$id == 0x0400 || printf(STDERR $FmtErr."\n",$WBRcfn,"Percent-Good Data",$id,1);
   483 	sysseek(WBRF,$start_ens+$WBRofs[5],0) || die("$WBRcfn: $!");
   515 	
   484 	sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   516 		if ($dta->{BT_PRESENT}) {
   485 	$id = unpack('v',$buf);
   517 			for ($BT_dt=6; $BT_dt<$dta->{NUMBER_OF_DATA_TYPES}; $BT_dt++) { 									# scan until BT found
   486 	$id == 0x0400 || printf(STDERR $FmtErr."\n",$WBRcfn,"Percent-Good Data",$id,1);
   518 				sysseek(WBRF,$start_ens+$WBRofs[$BT_dt],0) || die("$WBRcfn: $!");
   487 
   519 				sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   488 	my($BT_dt);
   520 				$id = unpack('v',$buf);
   489 	if ($dta->{BT_PRESENT}) {
   521 				last if ($id == 0x0600);
   490 		for ($BT_dt=6; $BT_dt<$dta->{NUMBER_OF_DATA_TYPES}; $BT_dt++) {										# scan until BT found
   522 			}
   491 			sysseek(WBRF,$start_ens+$WBRofs[$BT_dt],0) || die("$WBRcfn: $!");
   523 	
   492 			sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   524 			if ($BT_dt == $dta->{NUMBER_OF_DATA_TYPES}) {
   493 			$id = unpack('v',$buf);
   525 				printf(STDERR "WARNING: no BT data found\n");die;
   494 			last if ($id == 0x0600);
   526 				undef($dta->{BT_PRESENT});
   495 		}
   527 			}
   496 
   528 	    }
   497 		if ($BT_dt == $dta->{NUMBER_OF_DATA_TYPES}) {
       
   498 			printf(STDERR "WARNING: no BT data found\n");die;
       
   499 			undef($dta->{BT_PRESENT});
       
   500 		}
       
   501     }
   529     }
   502 
   530 
   503 	#--------------------
   531 	#--------------------
   504 	# FIXED LEADER
   532 	# FIXED LEADER
   505 	#--------------------
   533 	#--------------------
   506 
   534 
   507 	sysseek(WBRF,$start_ens+$WBRofs[0],0) || die("$WBRcfn: $!");
   535 	sysseek(WBRF,$start_ens+$WBRofs[0],0) || die("$WBRcfn: $!");
   508 	sysread(WBRF,$buf,42) == 42 || die("$WBRcfn: $!");
   536 	sysread(WBRF,$buf,42) == 42 || die("$WBRcfn: $!");
   509 	($id,$dta->{CPU_FW_VER},$dta->{CPU_FW_REV},$B1,$B2,$dummy,$dummy,$dummy,
   537 	($id,$dta->{CPU_FW_VER},$dta->{CPU_FW_REV},$B1,$B2,$dummy,
       
   538 	 $dta->{LAG_LENGTH},$dummy,
   510 	 $dta->{N_BINS},$dta->{PINGS_PER_ENSEMBLE},$dta->{BIN_LENGTH},
   539 	 $dta->{N_BINS},$dta->{PINGS_PER_ENSEMBLE},$dta->{BIN_LENGTH},
   511 	 $dta->{BLANKING_DISTANCE},$dummy,$dta->{MIN_CORRELATION},
   540 	 $dta->{BLANKING_DISTANCE},$dummy,$dta->{MIN_CORRELATION},
   512 	 $dta->{N_CODE_REPETITIONS},$dta->{MIN_PERCENT_GOOD},
   541 	 $dta->{N_CODE_REPETITIONS},$dta->{MIN_PERCENT_GOOD},
   513 	 $dta->{MAX_ERROR_VELOCITY},$dta->{TIME_BETWEEN_PINGS},$B3,$B4,$B5,
   542 	 $dta->{MAX_ERROR_VELOCITY},$dta->{TIME_BETWEEN_PINGS},$B3,$B4,$B5,
   514 	 $dta->{HEADING_ALIGNMENT},$dta->{HEADING_BIAS},$B6,$B7,
   543 	 $dta->{HEADING_ALIGNMENT},$dta->{HEADING_BIAS},$B6,$B7,
   516 	 $dta->{REF_LAYER_FIRST_BIN},$dta->{REF_LAYER_LAST_BIN},
   545 	 $dta->{REF_LAYER_FIRST_BIN},$dta->{REF_LAYER_LAST_BIN},
   517 	 $dta->{FALSE_TARGET_THRESHOLD},$dta->{LOW_LATENCY_SETTING},
   546 	 $dta->{FALSE_TARGET_THRESHOLD},$dta->{LOW_LATENCY_SETTING},
   518 	 $dta->{TRANSMIT_LAG_DISTANCE}) =
   547 	 $dta->{TRANSMIT_LAG_DISTANCE}) =
   519 		unpack('vCCCCC3CvvvCCCCvCCCCvvCCvvCCCCv',$buf);
   548 		unpack('vCCCCC3CvvvCCCCvCCCCvvCCvvCCCCv',$buf);
   520 
   549 
   521 	$id == 0x0000 || printf(STDERR $FmtErr."\n",$WBRcfn,"Fixed Leader",$id,0);
   550 	if ($dta->{INSTRUMENT_TYPE} eq 'Ocean Surveyor') {
   522 
   551 		$id == 0x0001 || printf(STDERR $FmtErr."\n",$WBRcfn,"Fixed Leader",$id,0);
   523     $dta->{BEAM_FREQUENCY} = 2**($B1 & 0x07) * 75;
   552     } else {
       
   553 		$id == 0x0000 || printf(STDERR $FmtErr."\n",$WBRcfn,"Fixed Leader",$id,0);
       
   554     }
       
   555 
       
   556 #   $dta->{BEAM_FREQUENCY} = 2**($B1 & 0x07) * 75;						# nominal
       
   557 	if    (($B1&0x07) == 0b000) { $dta->{BEAM_FREQUENCY} =   76.8; }		# actual
       
   558 	elsif (($B1&0x07) == 0b001) { $dta->{BEAM_FREQUENCY} =  153.6; }
       
   559 	elsif (($B1&0x07) == 0b010) { $dta->{BEAM_FREQUENCY} =  307.2; }
       
   560 	elsif (($B1&0x07) == 0b011) { $dta->{BEAM_FREQUENCY} =  614.4; }
       
   561 	elsif (($B1&0x07) == 0b100) { $dta->{BEAM_FREQUENCY} = 1228.8; }
       
   562 	elsif (($B1&0x07) == 0b101) { $dta->{BEAM_FREQUENCY} = 2457.6; }
       
   563 	else { die(sprintf("$WBRcfn: cannot decode BEAM_FREQUENCY (%03b)\n",$B1&0x07)); }
       
   564 
   524     $dta->{CONVEX_BEAM_PATTERN} = 1 if ($B1 & 0x08);
   565     $dta->{CONVEX_BEAM_PATTERN} = 1 if ($B1 & 0x08);
   525     $dta->{CONCAVE_BEAM_PATTERN} = 1 if (!($B1 & 0x08));
   566     $dta->{CONCAVE_BEAM_PATTERN} = 1 if (!($B1 & 0x08));
   526     $dta->{SENSOR_CONFIG} = ($B1 & 0x30) >> 4;
   567     $dta->{SENSOR_CONFIG} = ($B1 & 0x30) >> 4;
   527     $dta->{XDUCER_HEAD_ATTACHED} = 1 if ($B1 & 0x40);
   568     $dta->{XDUCER_HEAD_ATTACHED} = 1 if ($B1 & 0x40);
   528 
   569 
   648 sub readData(@)
   689 sub readData(@)
   649 {
   690 {
   650 	my($fn,$dta) = @_;
   691 	my($fn,$dta) = @_;
   651 	$WBRcfn = $fn;
   692 	$WBRcfn = $fn;
   652     open(WBRF,$WBRcfn) || die("$WBRcfn: $!\n");
   693     open(WBRF,$WBRcfn) || die("$WBRcfn: $!\n");
   653     WBRhdr($dta) || die("$WBRcfn: Insufficient Data\n");
   694     WBRhdr($dta,1) || die("$WBRcfn: Insufficient Data\n");
   654 	WBRens($dta->{N_BINS},$dta->{FIXED_LEADER_BYTES},
   695 	WBRens($dta->{N_BINS},$dta->{FIXED_LEADER_BYTES},
   655 		   \@{$dta->{ENSEMBLE}});
   696 		   \@{$dta->{ENSEMBLE}});
   656 	print(STDERR "$WBRcfn: $BIT_errors built-in-test errors\n")
   697 	print(STDERR "$WBRcfn: $BIT_errors built-in-test errors\n")
   657 		if ($BIT_errors);
   698 		if ($BIT_errors);
   658 }
   699 }
   723 		my($lastEns) = $ensNo;
   764 		my($lastEns) = $ensNo;
   724 		sysseek(WBRF,$start_ens+$WBRofs[1],0) || die("$WBRcfn: $!");
   765 		sysseek(WBRF,$start_ens+$WBRofs[1],0) || die("$WBRcfn: $!");
   725 		sysread(WBRF,$buf,4) == 4 || die("$WBRcfn: $!");
   766 		sysread(WBRF,$buf,4) == 4 || die("$WBRcfn: $!");
   726 		($id,$ensNo) = unpack("vv",$buf);										# only lower two bytes!!!
   767 		($id,$ensNo) = unpack("vv",$buf);										# only lower two bytes!!!
   727 
   768 
   728 		$id == 0x0080 ||
   769 		if (${$E}[$ens]->{INSTRUMENT_TYPE} eq 'Ocean Surveyor') {
   729 			die(sprintf($FmtErr,$WBRcfn,"Variable Leader",$id,$ensNo + ($lastEns - ($lastEns & 0xFFFF))));
   770 			$id == 0x0081 ||
       
   771 				die(sprintf($FmtErr,$WBRcfn,"Variable Leader",$id,$ensNo + ($lastEns - ($lastEns & 0xFFFF))));
       
   772         } else {
       
   773 			$id == 0x0080 ||
       
   774 				die(sprintf($FmtErr,$WBRcfn,"Variable Leader",$id,$ensNo + ($lastEns - ($lastEns & 0xFFFF))));
       
   775         }
   730 
   776 
   731 		if ($fixed_leader_bytes==42 || $fixed_leader_bytes==58) {				# BB150 & Explorer DVL
   777 		if ($fixed_leader_bytes==42 || $fixed_leader_bytes==58) {				# BB150 & Explorer DVL
   732 			sysread(WBRF,$buf,7) == 7 || die("$WBRcfn: $!");
   778 			sysread(WBRF,$buf,7) == 7 || die("$WBRcfn: $!");
   733 			(${$E}[$ens]->{YEAR},${$E}[$ens]->{MONTH},
   779 			(${$E}[$ens]->{YEAR},${$E}[$ens]->{MONTH},
   734 			 ${$E}[$ens]->{DAY},${$E}[$ens]->{HOUR},${$E}[$ens]->{MINUTE},
   780 			 ${$E}[$ens]->{DAY},${$E}[$ens]->{HOUR},${$E}[$ens]->{MINUTE},
  1289 #----------------------------------------------------------------------
  1335 #----------------------------------------------------------------------
  1290 
  1336 
  1291 sub clearEns($$)
  1337 sub clearEns($$)
  1292 {
  1338 {
  1293 	my($dta,$ens) = @_;
  1339 	my($dta,$ens) = @_;
  1294 	croak("clearEns: ens-index $ens out of range\n")
  1340 	die("clearEns: ens-index $ens out of range\n")
  1295 		unless ($ens>=0 && $ens<=$#{$dta->{ENSEMBLE}});
  1341 		unless ($ens>=0 && $ens<=$#{$dta->{ENSEMBLE}});
  1296 	for (my($bin)=0; $bin<$dta->{N_BINS}; $bin++) {
  1342 	for (my($bin)=0; $bin<$dta->{N_BINS}; $bin++) {
  1297 		undef(@{$dta->{ENSEMBLE}[$ens]->{VELOCITY}[$bin]});
  1343 		undef(@{$dta->{ENSEMBLE}[$ens]->{VELOCITY}[$bin]});
  1298 		@{$dta->{ENSEMBLE}[$ens]->{PERCENT_GOOD}[$bin]} = (0,0,0,0);
  1344 		@{$dta->{ENSEMBLE}[$ens]->{PERCENT_GOOD}[$bin]} = (0,0,0,0);
  1299 	}
  1345 	}