DCCL v4
WhoiUtil.cpp
1 // Copyright 2012-2023:
2 // GobySoft, LLC (2013-)
3 // Massachusetts Institute of Technology (2007-2014)
4 // Community contributors (see AUTHORS file)
5 // File authors:
6 // Toby Schneider <toby@gobysoft.org>
7 //
8 //
9 // This file is part of the Dynamic Compact Control Language Library
10 // ("DCCL").
11 //
12 // DCCL is free software: you can redistribute it and/or modify
13 // it under the terms of the GNU Lesser General Public License as published by
14 // the Free Software Foundation, either version 2.1 of the License, or
15 // (at your option) any later version.
16 //
17 // DCCL is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU Lesser General Public License for more details.
21 //
22 // You should have received a copy of the GNU Lesser General Public License
23 // along with DCCL. If not, see <http://www.gnu.org/licenses/>.
24 #include <cstdio>
25 #include <ctime> // P.Brodsky
26 #include <iomanip>
27 #include <iostream>
28 
29 #include "WhoiUtil.h"
30 
31 LATLON_COMPRESSED Encode_latlon(double latlon_in)
32 {
33  /* Latitude and longitude are compressed into 3 byte values each. */
34  LONG_AND_COMP out;
35  double encoded;
36  encoded = latlon_in * ((double)(0x007FFFFFL) / 180.0);
37  encoded += (encoded > 0.0) ? 0.5 : -0.5;
38  // deal with truncation
39  // std::cout << std::setprecision(16) << encoded << std::endl;
40  out.as_long = (long int)encoded;
41  // std::cout << std::hex << out.as_long << std::endl;
42  return (out.as_compressed);
43 }
44 
45 double Decode_latlon(LATLON_COMPRESSED latlon_in)
46 {
47  LONG_AND_COMP in;
48  in.as_long = 0; //Clear the MSB
49  in.as_compressed = latlon_in;
50 
51  if (in.as_long & 0x00800000L) // if is negative,
52  in.as_long |= ~0xFFFFFFL; // sign extend
53 
54  return (double)in.as_long * (180.0 / (double)(0x007FFFFFL));
55 }
56 
57 unsigned char Encode_heading(float heading)
58 {
59  return ((unsigned char)(heading * 255.0 / 360.0 + 0.5));
60 }
61 
62 double Decode_heading(unsigned char heading) { return ((double)heading * 360.0 / 255.0); }
63 
64 /* Encodes velocity in meters per second into a single byte with a
65  * resolution of 2.5 cm/sec. Input range is 0- ~6 meters/second
66  * (12 knots).
67  */
68 char Encode_est_velocity(float est_velocity)
69 {
70  return ((char)(est_velocity * 25.0 + 0.5)); // added 0.5 to perform basic positive rounding
71 }
72 
73 float Decode_est_velocity(char est_velocity) { return (est_velocity / 25.0); }
74 
75 /* Code-decode salinity in range of 20-45 parts per thousand at a
76  * resolution of 0.1 part per thousand. */
77 unsigned char Encode_salinity(float salinity)
78 {
79  float output;
80  if (salinity < 20.0)
81  return (0);
82  else
83  {
84  output = (salinity - 20.0) * 10;
85  if (output > 255)
86  output = 255;
87  return ((unsigned char)output);
88  }
89 }
90 
91 float Decode_salinity(unsigned char sal)
92 {
93  if (sal == 0)
94  return (sal);
95  return (((float)sal / 10.0) + 20.0);
96 }
97 
98 unsigned short Encode_depth(float depth)
99 /* 0 -100 meters: 0-1000 (10 cm resolution)
100  * 100 -200 meters: 1001-1500 (20 cm resolution)
101  * 200 -1000 meters: 1501-3100 (50 cm resolution)
102  * 1000-6000 meters: 3101-8100 (1 meter resolution) */
103 {
104  if (depth < 0)
105  return (0);
106  if (depth < 100)
107  return ((short unsigned int)((depth + .05) * 1.0 / 0.1));
108  if (depth < 200)
109  return ((short unsigned int)(((depth - 100) + 0.1) * 1.0 / 0.2 + 1000));
110  if (depth < 1000)
111  return ((short unsigned int)(((depth - 200) + 0.25) * 1.0 / 0.5 + 1500));
112  if (depth < 6000)
113  return ((short unsigned int)(((depth - 1000) + 0.5) * 1.0 / 1.0 + 3100));
114  return (8100);
115 }
116 
117 float Decode_depth(unsigned short depth)
118 {
119  /* 0 -100 meters: 0-1000 (10 cm resolution)
120  * 100 -200 meters: 1001-1500 (20 cm resolution)
121  * 200 -1000 meters: 1501-3100 (50 cm resolution)
122  * 1000-6000 meters: 3101-8100 (1 meter resolution)
123  */
124  unsigned short DEPTH_MODE_MASK = 0x1FFF; // P.Brodsky
125  depth &= DEPTH_MODE_MASK; // Only using bottom 13 bits.
126  if (depth <= 1000)
127  return (depth * 0.1 / 1.0);
128  else if (depth <= 1500)
129  return (100 + (depth - 1000) * 0.2 / 1.0);
130  else if (depth <= 3100)
131  return (200 + (depth - 1500) * 0.5 / 1.0);
132  else if (depth <= 8100)
133  return (1000 + (depth - 3100) * 1.0 / 1.0);
134  else
135  return (6000);
136 }
137 
138 unsigned char Encode_temperature(float temperature)
139 {
140  if (temperature < -4)
141  temperature = -4;
142  temperature += 4;
143  temperature = temperature * 256.0 / 40.0 + 0.5;
144  if (temperature > 255)
145  temperature = 255;
146  return ((unsigned char)temperature);
147 }
148 
149 float Decode_temperature(unsigned char temperature) { return (temperature * 40.0 / 256.0 - 4.0); }
150 
151 unsigned char Encode_sound_speed(float sound_speed)
152 {
153  return ((unsigned char)((sound_speed - 1425.0) * 2));
154 }
155 
156 float Decode_sound_speed(unsigned char sound_speed) { return ((float)sound_speed / 2.0 + 1425.0); }
157 
158 unsigned short Encode_hires_altitude(float alt) /* 10 cm resolution to 655 meters. */
159 {
160  alt *= 100;
161  if (alt > 65535.0)
162  return (65535U);
163  else if (alt < 0)
164  return (0);
165  else
166  return ((unsigned short)alt);
167 }
168 
169 float Decode_hires_altitude(unsigned short alt) { return ((float)alt / 100.0); }
170 
171 unsigned short Encode_gfi_pitch_oil(float gfi, float pitch, float oil)
172 {
173  /* We are encoding 3 parameters that are somewhat specific to
174  * the REMUS 6000 into 2 bytes. * GFI= 5 bits: 0-100 (3.3 resolution)
175  * OIL= 5 bits: this gives us resolution of 3.3 percent
176  * Pitch=6 bits;
177  180 into 64, resolution of 3 degrees. */
178  unsigned short result;
179  unsigned short temp;
180  if (gfi < 0) // 5 bits of gfi
181  gfi = 0;
182  else if (gfi > 100)
183  gfi = 100;
184  result = (unsigned short)(gfi * 31.0 / 100.0);
185  if (oil < 0) // 5 bits of oil
186  oil = 0;
187  else if (oil > 100)
188  oil = 100;
189  oil *= 31.0 / 100.0;
190  result |= ((unsigned short)oil << 5);
191  if (pitch > 90) // 6 bits of pitch
192  pitch = 90;
193  else if (pitch < -90)
194  pitch = -90;
195  pitch *= 63.0 / 180.0;
196  // Scale to 6 bits;
197  if (pitch > 0.0)
198  pitch += 0.5;
199  else if (pitch < 0)
200  pitch -= 0.5;
201  temp = ((short)pitch << 10);
202  result |= temp & 0xFC00;
203  return (result);
204 }
205 
206 void Decode_gfi_pitch_oil(unsigned short gfi_pitch_oil, float* gfi, float* pitch, float* oil)
207 {
208  unsigned short temp;
209  short temp_pitch;
210 
211  temp = (unsigned int)((gfi_pitch_oil & 0x001F) * 100.0 / 31.0);
212  *gfi = temp;
213  temp = (gfi_pitch_oil & 0x03E0) >> 5;
214  *oil = temp * 100.0 / 31.0;
215  temp_pitch = ((short)(gfi_pitch_oil & 0xFC00)) >> 10;
216  *pitch = temp_pitch * 180.0 / 63.0;
217 }
218 
219 TIME_DATE Encode_time_date(long secs_since_1970)
220 {
221  /* The time is encoded as follows:
222  * Month 4 bits * Day 5 bits
223  * hour 5 bits
224  * Min 6 bits
225  * Secs 4 bits
226  // 4 secs per bit
227  * The year is not encoded.
228 */
229  struct tm tm;
230  TIME_DATE_LONG comp;
231  /* Note: substitute localtime() for local timezone
232  * instead of GMT. */
233  time_t secs_since_1970_time_t(secs_since_1970);
234  tm = *gmtime(&secs_since_1970_time_t);
235  comp.as_long = (unsigned long)tm.tm_sec >> 2;
236  comp.as_long += (unsigned long)tm.tm_min << 4;
237  comp.as_long += (unsigned long)tm.tm_hour << (4 + 6);
238  comp.as_long += (unsigned long)tm.tm_mday << (4 + 6 + 5);
239  comp.as_long += (unsigned long)(tm.tm_mon + 1) << (4 + 6 + 5 + 5);
240  return (comp.as_time_date);
241 }
242 
243 long Decode_time_date(TIME_DATE input, short* mon, short* day, short* hour, short* min, short* sec)
244 {
245  TIME_DATE_LONG comp;
246  comp.as_long = 0;
247  comp.as_time_date = input;
248 
249  *mon = (short)((comp.as_long >> (4 + 6 + 5 + 5)) & 0x000F);
250  *day = (short)((comp.as_long >> (4 + 6 + 5)) & 0x001F);
251  *hour = (short)((comp.as_long >> (4 + 6)) & 0x001F);
252  *min = (short)((comp.as_long >> (4)) & 0x003F);
253  *sec = (short)(((comp.as_long) & 0x000F) * 4);
254 
255  /* It is possible to force this routine to return the
256  * seconds since 1970. Left as an exercise for the reader…
257  */
258  return (0);
259 }
260 
261 unsigned char Encode_watts(float volts, float amps)
262 {
263  /* Resolution is 4 watts, max is unsaturated is 1018 watts. */
264  float watts = (volts * amps) / 4;
265  if (watts > 255)
266  watts = 255;
267  else if (watts < 0)
268  watts = 0;
269  // ok, not possible...
270  return ((unsigned char)watts);
271 }
272 
273 float Decode_watts(unsigned char watts_encoded) { return ((float)watts_encoded * 4.0); }
274 
275 char Encode_speed(SPEED_MODE mode, float speed)
276 {
277  /* Max RPM: 2540 * Max Speed: 4.2 meters/sec (8.1 knots) */
278  switch (mode)
279  {
280  case SPEED_MODE_RPM: // Clamp to avoid errors. speed /= 20.0;
281  if (speed > 127)
282  speed = 127;
283  else if (speed < -127)
284  speed = -127;
285  break;
286  case SPEED_MODE_KNOTS: // Convert to m/sec speed *= 0.5144444;
287  // Fall thru...
288  case SPEED_MODE_MSEC:
289  speed *= 30;
290  if (speed > 127)
291  speed = 127;
292  else if (speed < -127)
293  speed = -127;
294  break;
295  }
296 
297  speed += (speed > 0.0) ? 0.5 : -0.5;
298 
299  return ((char)speed);
300 }
301 
302 float Decode_speed(SPEED_MODE mode, char speed)
303 {
304  switch (mode)
305  {
306  case SPEED_MODE_RPM: return (speed * 20.0);
307  case SPEED_MODE_MSEC: return ((float)speed / 30.0);
308  case SPEED_MODE_KNOTS: // "Knots mode not supported by modem codec"
309  return (0);
310  }
311  return (0);
312 }
313 
314 double DecodeRangerLL(unsigned char c1, unsigned char c2, unsigned char c3, unsigned char c4,
315  unsigned char c5)
316 {
317  int deg100, deg10, deg1, min10, min1, minp1000, minp100, minp10, minp1, sign;
318  double fMin, fDeg, fSign, fRet;
319 
320  // deg
321  deg100 = ((c1 & 0xf0) >> 4);
322  deg10 = c1 & 0x0f;
323  deg1 = ((c2 & 0xf0) >> 4);
324  fDeg = deg100 * 100.0 + deg10 * 10.0 + deg1 * 1.0;
325 
326  // sign
327  sign = c2 & 0x0f;
328  if ((sign == 0x0c) || (sign == 0x0d))
329  fSign = -1.0;
330  else
331  fSign = 1.0;
332 
333  // min
334  min10 = ((c3 & 0xf0) >> 4);
335  min1 = c3 & 0x0f;
336  minp1000 = ((c4 & 0xf0) >> 4);
337  minp100 = c4 & 0x0f;
338  minp10 = ((c5 & 0xf0) >> 4);
339  minp1 = c5 & 0x0f;
340  fMin = min10 * 10.0 + min1 * 1.0 + minp1000 * 0.1 + minp100 * 0.01 + minp10 * 0.001 +
341  minp1 * 0.0001;
342 
343  fRet = (fDeg + (fMin / 60.0)) * fSign;
344 
345  return fRet;
346 }
347 
348 double DecodeRangerBCD2(unsigned char c1, unsigned char c2)
349 {
350  int i1000, i100, i10, i1;
351 
352  i1000 = ((c1 & 0xf0) >> 4);
353  i100 = c1 & 0x0f;
354  i10 = ((c2 & 0xf0) >> 4);
355  i1 = c2 & 0x0f;
356 
357  return i1000 * 1000.0 + i100 * 100.0 + i10 * 10.0 + i1 * 1.0;
358 }
359 
360 double DecodeRangerBCD(unsigned char c1)
361 {
362  int i10, i1;
363 
364  i10 = ((c1 & 0xf0) >> 4);
365  i1 = c1 & 0x0f;
366 
367  return i10 * 10.0 + i1 * 1.0;
368 }
LONG_AND_COMP
Definition: WhoiUtil.h:34
TIME_DATE_LONG
Definition: WhoiUtil.h:46
LATLON_COMPRESSED
Definition: WhoiUtil.h:29
TIME_DATE
Definition: WhoiUtil.h:41