DCCL v4
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 <cstdio>
25#include <ctime> // P.Brodsky
26#include <iomanip>
27#include <iostream>
28
29#include "WhoiUtil.h"
30
31LATLON_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
45double Decode_latlon(LATLON_COMPRESSED latlon_in)
46{
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
57unsigned char Encode_heading(float heading)
58{
59 return ((unsigned char)(heading * 255.0 / 360.0 + 0.5));
60}
61
62double 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 */
68char 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
73float 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. */
77unsigned 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
91float Decode_salinity(unsigned char sal)
92{
93 if (sal == 0)
94 return (sal);
95 return (((float)sal / 10.0) + 20.0);
96}
97
98unsigned 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
117float 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
138unsigned 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
149float Decode_temperature(unsigned char temperature) { return (temperature * 40.0 / 256.0 - 4.0); }
150
151unsigned char Encode_sound_speed(float sound_speed)
152{
153 return ((unsigned char)((sound_speed - 1425.0) * 2));
154}
155
156float Decode_sound_speed(unsigned char sound_speed) { return ((float)sound_speed / 2.0 + 1425.0); }
157
158unsigned 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
169float Decode_hires_altitude(unsigned short alt) { return ((float)alt / 100.0); }
170
171unsigned 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
206void 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
219TIME_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
243long 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
261unsigned 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
273float Decode_watts(unsigned char watts_encoded) { return ((float)watts_encoded * 4.0); }
274
275char 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
302float 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
314double 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
348double 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
360double 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}