libSUFR
a LIBrary of Some Useful Fortran Routines
All Classes Namespaces Files Functions Variables Pages
angles.f90
Go to the documentation of this file.
1!> \file angles.f90 Procedures to handle angles
2
3
4! Copyright (c) 2002-2025 Marc van der Sluys - Nikhef/Utrecht University - marc.vandersluys.nl
5!
6! This file is part of the libSUFR package,
7! see: http://libsufr.sourceforge.net/
8!
9! This is free software: you can redistribute it and/or modify it under the terms of the European Union
10! Public Licence 1.2 (EUPL 1.2). This software is distributed in the hope that it will be useful, but
11! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12! PURPOSE. See the EU Public Licence for more details. You should have received a copy of the European
13! Union Public Licence along with this code. If not, see <https://www.eupl.eu/1.2/en/>.
14!
15!
16
17
18
19
20!***********************************************************************************************************************************
21!> \brief Procedures to handle angles
22
24 use sufr_kinds, only: double
25
26 implicit none
27 save
28 private :: double
29
30contains
31
32
33
34 !*********************************************************************************************************************************
35 !> \brief Returns angle in radians between 0 and 2pi
36 !!
37 !! \param ang Input angle (radians)
38 !! \retval rev Angle in radians between 0 and 2pi
39
40 pure function rev(ang)
41 use sufr_kinds, only: double
42 use sufr_constants, only: pi2
43
44 implicit none
45 real(double), intent(in) :: ang
46 real(double) :: rev
47
48 rev = ang - dble(floor(ang/pi2)) * pi2
49
50 end function rev
51 !*********************************************************************************************************************************
52
53 !*********************************************************************************************************************************
54 !> \brief Returns angle in radians between 0 and 2pi -- single precision
55 !!
56 !! \param ang Input angle (radians)
57 !! \retval rrev Angle in radians between 0 and 2pi
58
59 pure function rrev(ang)
60 use sufr_constants, only: rpi2
61
62 implicit none
63 real, intent(in) :: ang
64 real :: rrev
65
66 rrev = ang - real(floor(ang/rpi2)) * rpi2
67
68 end function rrev
69 !*********************************************************************************************************************************
70
71
72
73 !*********************************************************************************************************************************
74 !> \brief Returns angle in degrees between 0 and 360
75 !!
76 !! \param ang Input angle (degrees)
77 !! \retval rev360 Angle in degrees between 0 and 360
78
79 pure function rev360(ang)
80 use sufr_kinds, only: double
81
82 implicit none
83 real(double), intent(in) :: ang
84 real(double) :: rev360
85
86 rev360 = ang - dble(floor(ang/360.d0)) * 360.d0
87
88 end function rev360
89 !*********************************************************************************************************************************
90
91
92
93
94
95 !*********************************************************************************************************************************
96 !> \brief Returns angle in radians between -pi and pi
97 !!
98 !! \param ang Input angle (radians)
99 !! \retval rev2 Angle in radians between -pi and pi
100
101 pure function rev2(ang)
102 use sufr_kinds, only: double
103 use sufr_constants, only: pi,pi2
104
105 implicit none
106 real(double), intent(in) :: ang
107 real(double) :: rev2
108
109 rev2 = ang - dble(floor(ang/pi2)) * pi2
110 if(rev2.gt.pi) rev2 = rev2 - pi2
111
112 end function rev2
113 !*********************************************************************************************************************************
114
115
116 !*********************************************************************************************************************************
117 !> \brief Returns angle in radians between -pi and pi -- single precision
118 !!
119 !! \param ang Input angle (radians)
120 !! \retval rrev2 Angle in radians between -pi and pi
121
122 pure function rrev2(ang)
123 use sufr_constants, only: rpi,rpi2
124
125 implicit none
126 real, intent(in) :: ang
127 real :: rrev2
128
129 rrev2 = ang - real(floor(ang/rpi2)) * rpi2
130 if(rrev2.gt.rpi) rrev2 = rrev2 - rpi2
131
132 end function rrev2
133 !*********************************************************************************************************************************
134
135
136
137
138
139
140 !*********************************************************************************************************************************
141 !> \brief Returns angle in radians between cen-pi and cen+pi
142 !!
143 !! \param ang Input angle (radians)
144 !! \param cen 'Central value' (radians)
145 !! \retval revc Angle in radians between cen-pi and cen+pi
146
147 pure function revc(ang,cen)
148 use sufr_kinds, only: double
149 use sufr_constants, only: pi,pi2
150
151 implicit none
152 real(double), intent(in) :: ang,cen
153 real(double) :: revc
154
155 revc = ang - floor(ang/pi2) * pi2
156 if(cen.ge.pi) then
157 if(revc.lt.cen-pi) revc = revc + pi2
158 else
159 if(revc.gt.cen+pi) revc = revc - pi2
160 end if
161
162 end function revc
163 !*********************************************************************************************************************************
164
165
166
167
168 !*********************************************************************************************************************************
169 !> \brief Returns time in hours between 0 and 24
170 !!
171 !! \param tm Input time (hours)
172 !! \retval rv Time in hours between 0 and 24
173
174 pure function rv(tm)
175 use sufr_kinds, only: double
176 implicit none
177 real(double), intent(in) :: tm
178 real(double) :: rv
179
180 rv = tm - floor(tm/24.d0) * 24
181
182 end function rv
183 !*********************************************************************************************************************************
184
185
186 !*********************************************************************************************************************************
187 !> \brief Returns time in hours between -12 and 12
188 !!
189 !! \param tm Input time (hours)
190 !! \retval rv12 Time in hours between -12 and 12
191
192 pure function rv12(tm)
193 use sufr_kinds, only: double
194 implicit none
195 real(double), intent(in) :: tm
196 real(double) :: rv12
197
198 rv12 = tm - floor(tm/24.d0) * 24
199 if(rv12.gt.12.d0) rv12 = rv12 - 24.d0
200
201 end function rv12
202 !*********************************************************************************************************************************
203
204
205 !*********************************************************************************************************************************
206 !> \brief Returns angle in degrees between -180 and 180
207 !!
208 !! \param ang Input angle (degrees)
209 !! \retval rv180 Angle in degrees between -180 and 180
210
211 pure function rv180(ang)
212 use sufr_kinds, only: double
213 implicit none
214 real(double), intent(in) :: ang
215 real(double) :: rv180
216
217 rv180 = ang - floor(ang/360.d0) * 360
218 if(rv180.gt.180.d0) rv180 = rv180 - 360.d0
219
220 end function rv180
221 !*********************************************************************************************************************************
222
223
224
225
226 !*********************************************************************************************************************************
227 !> \brief Calculate the angular separation between two objects with given longitudes and latitudes.
228 !!
229 !! \param l1 Longitude of object 1 (rad).
230 !! \param l2 Longitude of object 2 (rad).
231 !! \param b1 Latitude of object 1 (rad).
232 !! \param b2 Latitude of object 2 (rad).
233 !!
234 !! \retval asep Angular separation between the two objects (rad).
235 !!
236 !! \see Celestial Mechanics in a Nutshell (CMiaNS), Sect.5.3: Angular distance and phase angle between two
237 !! celestial objects (CMiaNS.sf.net).
238
239 pure function asep(l1,l2, b1,b2)
240 use sufr_kinds, only: double
241
242 implicit none
243 real(double), intent(in) :: l1,l2, b1,b2
244 real(double) :: asep,dl,db,bb
245
246 dl = rev2(l2-l1)
247 asep = acos(min(max( sin(b1)*sin(b2) + cos(b1)*cos(b2)*cos(dl), -1.d0),1.d0)) ! Min/max: catch round-off errors leading to |arg|>1.
248
249 if(asep.lt.3.d-3) then ! for angles < 10'
250 bb = rev2((b1+b2)/2.d0)
251 db = rev2(b2-b1)
252 asep = sqrt((dl*cos(bb))**2 + db**2)
253 end if
254
255 end function asep
256 !*********************************************************************************************************************************
257
258
259 !*********************************************************************************************************************************
260 !> \brief Calculates the position angle of object 2 with respect to object 1, COUNTERCLOCKWISE from the north
261 !!
262 !! \param lon1 Longitude of object 1 - RA if measuring from the north (rad)
263 !! \param lon2 Longitude of object 2 - RA if measuring from the north (rad)
264 !! \param lat1 Latitude of object 1 - Dec if measuring from the north (rad)
265 !! \param lat2 Latitude of object 2 - Dec if measuring from the north (rad)
266 !!
267 !! \retval calpa Position angle of object 2 wrt object 1 (rad)
268
269 pure function calpa(lon1,lon2,lat1,lat2)
270 implicit none
271 real(double), intent(in) :: lon1,lon2,lat1,lat2
272 real(double) :: calpa,dlon
273
274 dlon = lon2-lon1
275 calpa = atan2( sin(dlon), cos(lat1)*tan(lat2) - sin(lat1)*cos(dlon) )
276
277 end function calpa
278 !*********************************************************************************************************************************
279
280
281
282
283 !*********************************************************************************************************************************
284 !> \brief Converts a position angle to one of eight English two-letter abbreviations (NE, SW)
285 !!
286 !! \param pa Position angle (radians, N=0)
287 !! \retval pastr_en English two-letter wind-direction abbreviation
288
289 pure function pastr_en(pa)
290 use sufr_kinds, only: double
291 use sufr_constants, only: pi2
292
293 implicit none
294 real(double), intent(in) :: pa
295 character :: pastr_en*(2),pas(9)*(2)
296
297 pas = (/'N ','NE','E ','SE','S ','SW','W ','NW','N '/) ! You can use trim()
298 pastr_en = pas(ceiling( rev(pa)/pi2 * 8 + 0.5d0 )) ! 1-9
299
300 end function pastr_en
301 !*********************************************************************************************************************************
302
303
304 !*********************************************************************************************************************************
305 !> \brief Converts a position angle to one of eight full Dutch strings (noordoosten, noorden)
306 !!
307 !! \param pa Position angle (radians, N=0)
308 !! \retval pastr_nl Full Dutch wind-direction string
309
310 pure function pastr_nl(pa)
311 use sufr_kinds, only: double
312 use sufr_constants, only: pi2
313
314 implicit none
315 real(double), intent(in) :: pa
316 character :: pastr_nl*(11),pas(9)*(11)
317
318 pas = (/'noorden ','noordoosten','oosten ','zuidoosten ','zuiden ','zuidwesten ','westen ','noordwesten', &
319 'noorden '/)
320 pastr_nl = pas(ceiling( rev(pa)/pi2 * 8 + 0.5d0 )) ! 1-9
321
322 end function pastr_nl
323 !*********************************************************************************************************************************
324
325
326 !*********************************************************************************************************************************
327 !> \brief Converts a position angle to one of eight Dutch abbreviations (NO,ZW)
328 !!
329 !! \param pa Position angle (radians, N=0)
330 !! \retval pastr_nls Dutch wind-direction abbreviation
331
332 pure function pastr_nls(pa)
333 use sufr_kinds, only: double
334 use sufr_constants, only: pi2
335
336 implicit none
337 real(double), intent(in) :: pa
338 character :: pastr_nls*(2),pas(9)*(2)
339
340 pas = (/'N ','NO','O ','ZO','Z ','ZW','W ','NW','N '/) ! You can use trim()
341 pastr_nls = pas(ceiling( rev(pa)/pi2 * 8 + 0.5d0 )) ! 1-9
342
343 end function pastr_nls
344 !*********************************************************************************************************************************
345
346
347
348
349
350
351 !*********************************************************************************************************************************
352 !> \brief Converts a wind direction/azimuth to one of 16 English wind directions (northnortheast, westsouthwest)
353 !!
354 !! \param wd Wind direction/azimuth (radians, N=0)
355 !! \retval wdstr_en Full English wind direction
356
357 pure function wdstr_en(wd)
358 use sufr_kinds, only: double
359 use sufr_constants, only: r2d
360
361 implicit none
362 real(double), intent(in) :: wd
363 character :: wdstr_en*(14),wds(0:15)*(14)
364
365 wds = [character(len=14) :: 'north','northnortheast','northeast','eastnortheast','east','eastsoutheast','southeast', &
366 'southsoutheast','south','southsouthwest','southwest','westsouthwest','west','westnorthwest','northwest','northnorthwest']
367
368 wdstr_en = wds(mod( nint(wd*r2d/22.5) + 32, 16 )) ! Mod, so that -1 -> 15
369
370 end function wdstr_en
371 !*********************************************************************************************************************************
372
373
374 !*********************************************************************************************************************************
375 !> \brief Converts a wind direction/azimuth to one of 16 three-letter English wind-direction abbreviations (NNE, WSW)
376 !!
377 !! \param wd Wind direction/azimuth (radians, N=0)
378 !! \retval wdstr_ens Three-letter English wind-direction abbreviation
379
380 pure function wdstr_ens(wd)
381 use sufr_kinds, only: double
382 use sufr_constants, only: r2d
383
384 implicit none
385 real(double), intent(in) :: wd
386 character :: wdstr_ens*(3),wds(0:15)*(3)
387
388 wds = (/'N ','NNE','NE ','ENE','E ','ESE','SE ','SSE','S ','SSW','SW ','WSW','W ','WNW','NW ','NNW'/)
389 wdstr_ens = wds(mod( nint(wd*r2d/22.5) + 32, 16 )) ! Mod, so that -1 -> 15
390
391 end function wdstr_ens
392 !*********************************************************************************************************************************
393
394
395 !*********************************************************************************************************************************
396 !> \brief Converts a wind direction/azimuth to one of 16 three-letter Dutch wind-direction abbreviations (NNO, WZW)
397 !!
398 !! \param wd Wind direction/azimuth (radians, N=0)
399 !! \retval wdstr_nls Three-letter Dutch wind-direction abbreviation
400
401 pure function wdstr_nls(wd)
402 use sufr_kinds, only: double
403 use sufr_constants, only: r2d
404
405 implicit none
406 real(double), intent(in) :: wd
407 character :: wdstr_nls*(3),wds(0:15)*(3)
408
409 wds = (/'N ','NNO','NO ','ONO','O ','OZO','ZO ','ZZO','Z ','ZZW','ZW ','WZW','W ','WNW','NW ','NNW'/)
410 wdstr_nls = wds(mod( nint( wd*r2d/22.5) + 32, 16 )) ! Mod, so that -1 -> 15
411
412 end function wdstr_nls
413 !*********************************************************************************************************************************
414
415
416 !*********************************************************************************************************************************
417 !> \brief Converts a wind direction/azimuth to one of 16 full Dutch wind-direction strings (noordnoordoost, zuidwest)
418 !!
419 !! \param wd Wind direction (radians, N=0)
420 !! \retval wdstr_nl Full Dutch wind-direction string
421
422 pure function wdstr_nl(wd)
423 use sufr_kinds, only: double
424 use sufr_constants, only: r2d
425
426 implicit none
427 real(double), intent(in) :: wd
428 character :: wdstr_nl*(14),wds(0:15)*(14)
429
430 wds = (/ &
431 'noord ', 'noordnoordoost', 'noordoost ', 'oostnoordoost ', &
432 'oost ', 'oostzuidoost ', 'zuidoost ', 'zuidzuidoost ', &
433 'zuid ', 'zuidzuidwest ', 'zuidwest ', 'westzuidwest ', &
434 'west ', 'westnoordwest ', 'noordwest ', 'noordnoordwest'/)
435
436 wdstr_nl = wds(mod( nint( wd*r2d/22.5) + 32, 16 )) ! Mod, so that -1 -> 15
437
438 end function wdstr_nl
439 !*********************************************************************************************************************************
440
441
442 !*********************************************************************************************************************************
443 !> \brief Converts a wind direction/azimuth to one of 8 two-letter Dutch secondary wind-direction abbreviations (NO, Z).
444 !! Wrapper for pastr_nls().
445 !!
446 !! \param wd Wind direction (radians, N=0)
447 !! \retval wdstr_nls2 Dutch two-letter wind-direction abbreviation
448
449 pure function wdstr_nls2(wd)
450 use sufr_kinds, only: double
451
452 implicit none
453 real(double), intent(in) :: wd
454 character :: wdstr_nls2*(2)
455
457
458 end function wdstr_nls2
459 !*********************************************************************************************************************************
460
461
462
463 !*********************************************************************************************************************************
464 !> \brief Converts a wind direction/azimuth to one of 8 full Dutch secondary wind-direction (noordoost, zuid).
465 !! Derived from pastr_nl().
466 !!
467 !! \param wd Wind direction (radians, N=0)
468 !! \retval wdstr_nl8 String with full Dutch wind-direction
469
470 pure function wdstr_nl8(wd)
471 use sufr_kinds, only: double
472 use sufr_constants, only: pi2
473
474 implicit none
475 real(double), intent(in) :: wd
476 character :: wdstr_nl8*(9),wds(9)*(9)
477
478 wds = (/'noord ','noordoost','oost ','zuidoost ','zuid ','zuidwest ','west ','noordwest', 'noord '/)
479 wdstr_nl8 = wds(ceiling( rev(wd)/pi2 * 8 + 0.5d0 )) ! 1-9
480
481 end function wdstr_nl8
482 !*********************************************************************************************************************************
483
484
485
486
487
488
489end module sufr_angles
490!***********************************************************************************************************************************
491
Procedures to handle angles.
Definition angles.f90:23
pure character function, dimension(3) wdstr_ens(wd)
Converts a wind direction/azimuth to one of 16 three-letter English wind-direction abbreviations (NNE...
Definition angles.f90:381
pure character function, dimension(14) wdstr_en(wd)
Converts a wind direction/azimuth to one of 16 English wind directions (northnortheast,...
Definition angles.f90:358
pure real function rrev(ang)
Returns angle in radians between 0 and 2pi – single precision.
Definition angles.f90:60
pure real(double) function revc(ang, cen)
Returns angle in radians between cen-pi and cen+pi.
Definition angles.f90:148
pure character function, dimension(2) wdstr_nls2(wd)
Converts a wind direction/azimuth to one of 8 two-letter Dutch secondary wind-direction abbreviations...
Definition angles.f90:450
pure real(double) function rv(tm)
Returns time in hours between 0 and 24.
Definition angles.f90:175
pure real(double) function calpa(lon1, lon2, lat1, lat2)
Calculates the position angle of object 2 with respect to object 1, COUNTERCLOCKWISE from the north.
Definition angles.f90:270
pure real(double) function rev2(ang)
Returns angle in radians between -pi and pi.
Definition angles.f90:102
pure real(double) function rv180(ang)
Returns angle in degrees between -180 and 180.
Definition angles.f90:212
pure real function rrev2(ang)
Returns angle in radians between -pi and pi – single precision.
Definition angles.f90:123
pure character function, dimension(2) pastr_en(pa)
Converts a position angle to one of eight English two-letter abbreviations (NE, SW)
Definition angles.f90:290
pure character function, dimension(3) wdstr_nls(wd)
Converts a wind direction/azimuth to one of 16 three-letter Dutch wind-direction abbreviations (NNO,...
Definition angles.f90:402
pure real(double) function rev360(ang)
Returns angle in degrees between 0 and 360.
Definition angles.f90:80
pure character function, dimension(9) wdstr_nl8(wd)
Converts a wind direction/azimuth to one of 8 full Dutch secondary wind-direction (noordoost,...
Definition angles.f90:471
pure real(double) function asep(l1, l2, b1, b2)
Calculate the angular separation between two objects with given longitudes and latitudes.
Definition angles.f90:240
pure real(double) function rev(ang)
Returns angle in radians between 0 and 2pi.
Definition angles.f90:41
pure character function, dimension(2) pastr_nls(pa)
Converts a position angle to one of eight Dutch abbreviations (NO,ZW)
Definition angles.f90:333
pure character function, dimension(11) pastr_nl(pa)
Converts a position angle to one of eight full Dutch strings (noordoosten, noorden)
Definition angles.f90:311
pure real(double) function rv12(tm)
Returns time in hours between -12 and 12.
Definition angles.f90:193
pure character function, dimension(14) wdstr_nl(wd)
Converts a wind direction/azimuth to one of 16 full Dutch wind-direction strings (noordnoordoost,...
Definition angles.f90:423
Provides all constants in the library, and routines to define them.
Definition constants.f90:23
real(double), parameter, public pi2
2*pi
Definition constants.f90:40
real(double), parameter, public pi
pi
Definition constants.f90:39
real, parameter, public rpi2
2*pi
Definition constants.f90:68
real(double), parameter, public r2d
Radians to degrees.
Definition constants.f90:42
real, parameter, public rpi
pi
Definition constants.f90:67
Provides kinds and related constants/routines.
Definition kinds.f90:26
integer, parameter double
Double-precision float. Precision = 15, range = 307.
Definition kinds.f90:35