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