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