1 /***************************************************************************
2 locator.cpp - description
3 -------------------
4 begin : vie feb 7 2003
5 copyright : (C) 2003 by Jaime Robles
6 email : jaime@robles.es
7 ***************************************************************************/
8
9 /*****************************************************************************
10 * This file is part of KLog. *
11 * *
12 * KLog is free software: you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation, either version 3 of the License, or *
15 * (at your option) any later version. *
16 * *
17 * KLog 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 General Public License for more details. *
21 * *
22 * You should have received a copy of the GNU General Public License *
23 * along with KLog. If not, see <https://www.gnu.org/licenses/>. *
24 * *
25 *****************************************************************************/
26
27 #include "locator.h"
28 #include <QtDebug>
29
Locator()30 Locator::Locator(){
31
32 }
33
~Locator()34 Locator::~Locator(){
35 }
36
isValidLocator(const QString & tlocator)37 bool Locator::isValidLocator(const QString& tlocator){
38 /* -------------- Subroutine -----------------------
39 Check valid locator (VALID: AA00AA -> RR99XX)
40 Input : char *locator = 4 - 8 characters word wide locator.
41 returned value == -1 No error. (Valid locator).
42 returned value == 0 Error. (Invalid locator).
43 Note: also string "END" is considered a valid locator, but returned value is -2.
44 -------------------------------------------------
45 Wikipedia:
46 Character pairs encode longitude first, and then latitude.
47 The first pair (a field) encodes with base 18 and the letters "A" to "R".
48 The second pair (square) encodes with base 10 and the digits "0" to "9".
49 The third pair (subsquare) encodes with base 24 and the letters "a" to "x".
50 The fourth pair (extended square) encodes with base 10 and the digits "0" to "9".
51 The fifth and subsequent pairs are not formally defined, but recycling the third and fourth
52 pair algorithms is one possible definition: BL11bh16oo66
53
54 */
55
56 //qDebug() << "Locator::isValidLocator: " << tlocator << QT_ENDL;
57
58 //int lenght_of_locator;
59
60 QString testLocator ="A";
61 testLocator = tlocator.toUpper();
62 //lenght_of_locator = testLocator.length(); Locators up to 8 digits!
63 //IN, IN70, IN70DD, IN70DD20, IN70DD20
64 QRegularExpression rx;
65 rx.setPattern("^[A-R]{2}$");
66 if (rx.match(testLocator).hasMatch())
67 {
68 //qDebug() << "Locator::isValidLocator: Match 2: " << testLocator;
69 return true;
70 }
71 else
72 {
73 rx.setPattern("^[A-R]{2}[0-9]{2}$");
74 if (rx.match(testLocator).hasMatch())
75 {
76 //qDebug() << "Locator::isValidLocator: Match 4: " << testLocator;
77 return true;
78 }
79 else
80 {
81 rx.setPattern("^[A-R]{2}[0-9]{2}[A-X]{2}$");
82 if (rx.match(testLocator).hasMatch())
83 {
84 //qDebug() << "Locator::isValidLocator: Match 6: " << testLocator;
85 return true;
86 }
87 else
88 {
89 rx.setPattern("^[A-R]{2}[0-9]{2}[A-X]{2}[0-9]{2}$");
90 if (rx.match(testLocator).hasMatch())
91 {
92 //qDebug() << "Locator::isValidLocator: Match 8: " << testLocator;
93 return true;
94 }
95 else
96 {
97 //qDebug() << "Locator::isValidLocator: NOT VALID: " << testLocator;
98 return false;
99 }
100 }
101 }
102 }
103 }
104
105
getLat(const QString & tlocator)106 double Locator::getLat(const QString& tlocator){
107 //qDebug() << "Locator::getLat: " << tlocator;
108 // Read formula from: https://unclassified.software/files/source/MaidenheadLocator.cs
109 //Revisar las formulas porque salen distancias erroneas
110 if (!isValidLocator(tlocator))
111 {
112 return 0.0;
113 }
114
115 if (tlocator.length() == 2)
116 {
117 //qDebug() << "Locator::getLat - num: " << QString::number((tlocator.at(1)).toLatin1() );
118 //qDebug() << "Locator::getLat: " << QString::number((((tlocator.at(1)).toLatin1() - 65) * 10) - 90) << QT_ENDL;
119 return (((tlocator.at(1)).toLatin1() - 65) * 10) - 90;
120 }
121 if (tlocator.length() == 4)
122 {
123 return ((tlocator.at(1)).toLatin1() - 'A') * 10 + ((tlocator.at(3)).toLatin1() - '0' + 0.5) - 90;
124 //return (((tlocator.at(1)).toLatin1() - 65) * 10) + ((tlocator.at(3)).toLatin1() - 48) - 90;
125 }
126 else if (tlocator.length()== 6)
127 {
128 //qDebug() << "Locator::getLat: " << QString::number(((tlocator.at(1)).toLatin1() - 'A') * 10 + ((tlocator.at(3)).toLatin1() - '0') + ((tlocator.at(5)).toLatin1() - 'A' + 0.5) / 24 - 90) << QT_ENDL;
129 return ((tlocator.at(1)).toLatin1() - 'A') * 10 + ((tlocator.at(3)).toLatin1() - '0') + ((tlocator.at(5)).toLatin1() - 'A' + 0.5) / 24 - 90;
130 //return (((tlocator.at(1)).toLatin1() - 65) * 10) + ((tlocator.at(3)).toLatin1() - 48) + (((tlocator.at(5)).toLatin1() - 65 + 0.5) / 24) - 90;
131 }
132 else if (tlocator.length()== 8)
133 {
134 //qDebug() << "Locator::getLat: " << QString::number(((tlocator.at(1)).toLatin1() - 'A') * 10 + ((tlocator.at(3)).toLatin1() - '0') + ((tlocator.at(5)).toLatin1() - 'A' + 0.0) / 24 + ((tlocator.at(7)).toLatin1() - '0' + 0.5) / 240 - 90) << QT_ENDL;
135 return ((tlocator.at(1)).toLatin1() - 'A') * 10 + ((tlocator.at(3)).toLatin1() - '0') + ((tlocator.at(5)).toLatin1() - 'A' + 0.0) / 24 + ((tlocator.at(7)).toLatin1() - '0' + 0.5) / 240 - 90;
136 //return (((tlocator.at(1)).toLatin1() - 65) * 10) + ((tlocator.at(3)).toLatin1() - 48) + (((tlocator.at(5)).toLatin1() - 65 + 0.5) / 24) - 90;
137 }
138 else if (tlocator.length()== 10)
139 {
140 //qDebug() << "Locator::getLat: " << QString::number(((tlocator.at(1)).toLatin1() - 'A') * 10 + ((tlocator.at(3)).toLatin1() - '0') + ((tlocator.at(5)).toLatin1() - 'A' + 0.0) / 24 + ((tlocator.at(7)).toLatin1() - '0' + 0.0) / 240 + ((tlocator.at(9)).toLatin1() - 'A' + 0.5) / 240 / 24 - 90) << QT_ENDL;
141 return ((tlocator.at(1)).toLatin1() - 'A') * 10 + ((tlocator.at(3)).toLatin1() - '0') + ((tlocator.at(5)).toLatin1() - 'A' + 0.0) / 24 + ((tlocator.at(7)).toLatin1() - '0' + 0.0) / 240 + ((tlocator.at(9)).toLatin1() - 'A' + 0.5) / 240 / 24 - 90;
142 //return (((tlocator.at(1)).toLatin1() - 65) * 10) + ((tlocator.at(3)).toLatin1() - 48) + (((tlocator.at(5)).toLatin1() - 65 + 0.5) / 24) - 90;
143 }
144 else
145 {
146 return 0.0;
147 }
148
149 }
150
getLon(const QString & tlocator)151 double Locator::getLon(const QString& tlocator)
152 {
153 //qDebug() << "Locator::getLon: " << tlocator;
154
155 if (!isValidLocator(tlocator))
156 {
157 return 0.0;
158 }
159
160
161 if (tlocator.length() == 2)
162 {
163 return (((tlocator.at(0)).toLatin1() - 65) * 20) - 180;
164 }
165
166 if (tlocator.length() == 4)
167 {
168 return ((tlocator.at(0)).toLatin1() - 'A') * 20 + ((tlocator.at(2)).toLatin1() - '0' + 0.5) * 2 - 180;
169 //return (((tlocator.at(0)).toLatin1() - 65) * 20) + (((tlocator.at(2)).toLatin1() - 48) * 2) - 180;
170 }
171 else if (tlocator.length()== 6)
172 {
173 //qDebug() << "Locator::getLon: " << QString::number(((tlocator.at(0)).toLatin1() - 'A') * 20 + ((tlocator.at(2)).toLatin1() - '0') * 2 + ((tlocator.at(4)).toLatin1() - 'A' + 0.5) / 12 - 180) << QT_ENDL;
174 return ((tlocator.at(0)).toLatin1() - 'A') * 20 + ((tlocator.at(2)).toLatin1() - '0') * 2 + ((tlocator.at(4)).toLatin1() - 'A' + 0.5) / 12 - 180;
175 //return (((tlocator.at(0)).toLatin1() - 65) * 20) + (((tlocator.at(2)).toLatin1() - 48) * 2) + (((tlocator.at(4)).toLatin1() - 65 + 0.5) / 12) - 180;
176 }
177 else if (tlocator.length()== 8)
178 {
179 //qDebug() << "Locator::getLon: " << QString::number(((tlocator.at(0)).toLatin1() - 'A') * 20 + ((tlocator.at(2)).toLatin1() - '0') * 2 + ((tlocator.at(4)).toLatin1() - 'A' + 0.0) / 12 + ((tlocator.at(6)).toLatin1() - '0' + 0.5) / 120 - 180) << QT_ENDL;
180 return ((tlocator.at(0)).toLatin1() - 'A') * 20 + ((tlocator.at(2)).toLatin1() - '0') * 2 + ((tlocator.at(4)).toLatin1() - 'A' + 0.0) / 12 + ((tlocator.at(6)).toLatin1() - '0' + 0.5) / 120 - 180;
181 }
182 //else if (tlocator.length()== 10)
183 //{
184 // return ((tlocator.at(0)).toLatin1() - 'A') * 20 + ((tlocator.at(2)).toLatin1() - '0') * 2 + ((tlocator.at(4)).toLatin1() - 'A' + 0.0) / 12 + ((tlocator.at(6)).toLatin1() - '0' + 0.0) / 120 + ((tlocator.at(8)).toLatin1() - 'A' + 0.5) / 120 / 24 - 180;
185 // }
186 else
187 {
188 return 0.0;
189 }
190 }
191
getBeam(const double lon1,const double lat1,const double lon2,const double lat2)192 int Locator::getBeam(const double lon1, const double lat1, const double lon2, const double lat2){
193 double lon_a,lat_a,lon_b,lat_b, bearing;
194 //qDebug() << "Locator::getBeam1: " << QString::number(lon1) << "/" << QString::number(lat1) << QT_ENDL;
195 //qDebug() << "Locator::getBeam2: " << QString::number(lon2) << "/" << QString::number(lat2) << QT_ENDL;
196
197
198 lon_a=lon1*PI/180; // Convert degrees to radians
199 lat_a=lat1*PI/180;
200 lon_b=lon2*PI/180;
201 lat_b=lat2*PI/180;
202
203 //earing_Distance( double lon_a, double lat_a, /* Lon/Lat of point A */
204 // double lon_b, double lat_b, /* Lon/Lat of point B */
205 // double *bearing, double *distance )/* From A to B */
206 //{
207 double
208 cos_gc_arc, /* Great circle arc A to B */
209 cos_bearing, sin_bearing, /* cos/sin of bearing A to B */
210 lon_diff; /* Difference in longitude of B from A */
211 //double gc_arc;
212 /* Longitude differnce of B from A */
213 lon_diff = lon_b - lon_a;
214
215 /* Calculate great circle distance A to B */
216 cos_gc_arc = cos(lon_diff)*cos(lat_a)*cos(lat_b) + sin(lat_a)*sin(lat_b);
217 //gc_arc = acos( cos_gc_arc );
218
219 /* Distance in km */
220 // *distance = eradius * gc_arc;
221
222 /* Calculate bearing A to B */
223 cos_bearing = sin(lat_b) - sin(lat_a) * cos_gc_arc;
224 sin_bearing = sin(lon_diff) * cos(lat_a) * cos(lat_b);
225 bearing = atan2(sin_bearing, cos_bearing);
226
227 /* Correct negative (anticlockwise) bearings */
228
229 if( bearing < 0.0 )
230 {
231 bearing = (2*PI) + bearing;
232 }
233 bearing = 360 - (180/PI*bearing);
234 bearing = 360 - bearing;
235
236 //qDebug() << "Locator::getBeam: " << QString::number(bearing) << QT_ENDL;
237 /* Convert to degrees */
238
239 return int(bearing);
240
241 }
242
getDistance(const double lon1,const double lat1,const double lon2,const double lat2,const bool _imperialSystem)243 int Locator::getDistance(const double lon1, const double lat1, const double lon2, const double lat2, const bool _imperialSystem){
244 //http://en.wikipedia.org/wiki/Haversine_formula
245 //qDebug() << "Locator::getDistanceKilometres: MyPos("<< QString::number(lon1) << ")" << QT_ENDL;
246 // << QString::number(lat1) << ") - DxPos(" << QString::number(lon2) << "/" << QString::number(lat2) << ")" << QT_ENDL;
247 double lo1,la1,lo2,la2;
248
249 // TODO: Is it needed to check if the longitude and latitude are correct and/or between the magins?
250 // if (!( (checkCoords(lon1, lat1) ) && (checkCoords(lon2, lat2)) ))
251 // return 0;
252
253 lo1=lon1* DEG_TO_RAD; // Convert degrees to radians
254 la1=lat1* DEG_TO_RAD;
255 lo2=lon2* DEG_TO_RAD;
256 la2=lat2* DEG_TO_RAD;
257
258 if (!_imperialSystem){
259 //qDebug() << "Locator::getDistance (Km): " << QString::number((int)(acos(cos(la1)*cos(lo1)*cos(la2)*cos(lo2)+cos(la1)*sin(lo1)*cos(la2)*sin(lo2)+sin(la1)*sin(la2)) * EARTH_RADIUS)) << QT_ENDL;
260 return int(acos(cos(la1)*cos(lo1)*cos(la2)*cos(lo2)+cos(la1)*sin(lo1)*cos(la2)*sin(lo2)+sin(la1)*sin(la2)) * EARTH_RADIUS);
261 }else{ // In milles
262 //qDebug() << "Locator::getDistance (Milles): " << QString::number(((int)(acos(cos(la1)*cos(lo1)*cos(la2)*cos(lo2)+cos(la1)*sin(lo1)*cos(la2)*sin(lo2)+sin(la1)*sin(la2)) * EARTH_RADIUS))* 0.62137) << QT_ENDL;
263 return int(((acos(cos(la1)*cos(lo1)*cos(la2)*cos(lo2)+cos(la1)*sin(lo1)*cos(la2)*sin(lo2)+sin(la1)*sin(la2)) * EARTH_RADIUS)) * 0.62137);
264 }
265 }
266
checkCoords(const double lon1,const double lat1)267 bool Locator::checkCoords(const double lon1, const double lat1){
268 //qDebug() << "Locator::checkCoords" ;
269 // Checks if a coordinates is correct.
270 if ((lat1 > 90.0 || lat1 < -90.0) && (lon1 > 180.0 || lon1 < -180.0)){
271 return true;
272 }else{
273 return false;
274 }
275 }
276
getLocator(const double lon1,const double lat1) const277 QString Locator::getLocator(const double lon1, const double lat1) const{
278 /* -------------- Subroutine -----------------------
279 Calculate locator from longitude and latitude
280 Input : lon = Longitude in decimal degrees (+ = West; - = East).
281 lat = Latitude in decimal degrees (+ = North; - = South).
282 Output: locator = 6 characters world wide locator.
283 ------------------------------------------------- */
284 //qDebug() << "Locator::getLocator: (" << QString::number(lon1) << "/" << QString::number(lat1) << ")" << QT_ENDL;
285 QString locat = ""; //NO locator
286
287 double lo, la;
288 int alo,bla,clo,dla,elo,fla;
289
290 lo=(-lon1+180)/20;
291 la = (lat1+90)/10;
292
293 alo=int(floor(lo));
294 bla=int(floor(la));
295 lo=(lo-(double(alo)))*10;
296 la=(la-(double(bla)))*10;
297
298 clo = int(floor(lo));
299 dla = int(floor(la));
300
301 elo = int(floor((lo-double(clo) ) * 24 )) ;
302 fla = int(floor((la-double(dla) ) * 24 ));
303
304 //TODO: Test if locators are calculated correctly.
305 // generation function has been changed because of the QT4 migration
306 locat = locat + QChar(alo+'A');
307 locat = locat + QChar(bla+'A');
308 locat = locat + QChar(clo+'0');
309 locat = locat + QChar(dla+'0');
310 locat = locat + QChar(elo+'A');
311 locat = locat + QChar(fla+'A');
312
313 // locat.at(0)=QChar(alo+'A');
314
315 // locat.at(1)=QChar(bla+'A');
316 // locat.at(2)=QChar(clo+'0');
317 // locat.at(3)=QChar(dla+'0');
318 // locat.at(4)=QChar(elo+'A');
319 // locat.at(5)=QChar(fla+'A');
320
321
322
323 return locat;
324 }
325
326 /*
327 void Locator::degTodms(const double deg){
328 double temp;
329 double ddeg;
330 ddeg = 0;
331 ddeg += 1.0/7200.0; // Round-up to 0.5 sec
332 int ideg = (int)ddeg;
333 temp = ( deg - (double)ideg ) * 60.0;
334 int imin = (int)temp;
335 temp = ( temp - (double)imin ) * 60.0;
336 int isec = (int)(temp);
337 }
338
339
340 double Locator::dmsTodeg (int deg, int min, int sec)
341 {
342 return (double)deg + (double)min/60.0 + (double)sec/3600.0;
343 }
344 */
345
getBeamBetweenLocators(const QString & tlocator1,const QString & tlocator2)346 int Locator::getBeamBetweenLocators (const QString& tlocator1, const QString& tlocator2)
347 {
348 //qDebug() << "Locator::getBeamBetweenLocators: " << tlocator1 << "/" << tlocator2 << QT_ENDL;
349 if ( !(isValidLocator(tlocator1) && isValidLocator(tlocator2) ) )
350 {
351 return -1;
352 }
353 else
354 {
355 double lon1 = getLon(tlocator1);
356 double lon2 = getLon(tlocator2);
357 double lat1 = getLat(tlocator1);
358 double lat2 = getLat(tlocator2);
359
360 return getBeam(lon1, lat1, lon2, lat2);
361
362 }
363
364 }
365
getDistanceBetweenLocators(const QString & tlocator1,const QString & tlocator2,const bool _imperialSystem)366 int Locator::getDistanceBetweenLocators (const QString& tlocator1, const QString& tlocator2, const bool _imperialSystem)
367 {
368 if ( !(isValidLocator(tlocator1) && isValidLocator(tlocator2) ) )
369 {
370 return -1;
371 }
372 else
373 {
374 double lon1 = getLon(tlocator1);
375 double lon2 = getLon(tlocator2);
376 double lat1 = getLat(tlocator1);
377 double lat2 = getLat(tlocator2);
378
379 return getDistance(lon1, lat1, lon2, lat2, _imperialSystem);
380 //return getBeam(lon1, lat1, lon2, lat2);
381
382 }
383 }
384