[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnuastro-commits] master 25038d1 1/2: Library (Units): deg. to hh:mm:ss
From: |
Mohammad Akhlaghi |
Subject: |
[gnuastro-commits] master 25038d1 1/2: Library (Units): deg. to hh:mm:ss and dd:mm:ss and vice-versa functions |
Date: |
Wed, 8 Apr 2020 22:31:54 -0400 (EDT) |
branch: master
commit 25038d195574d1a0bf8a3e9e7d8b4ae7a4213513
Author: Kartik Ohri <address@hidden>
Commit: Mohammad Akhlaghi <address@hidden>
Library (Units): deg. to hh:mm:ss and dd:mm:ss and vice-versa functions
New functions, `gal_units_ra_to_decimal`, `gal_units_dec_to_decimal`,
`gal_units_decimal_to_ra` and `gal_units_decimal_to_dec` have been added to
convert right ascension and declination units from string to decimal form
and vice versa.
This partially completes task #15567.
---
lib/Makefile.am | 4 +-
lib/arithmetic.c | 97 ++++++++++++-
lib/gnuastro/arithmetic.h | 5 +
lib/gnuastro/units.h | 81 +++++++++++
lib/units.c | 340 ++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 524 insertions(+), 3 deletions(-)
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 2b36836..f880d6a 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -62,7 +62,7 @@ libgnuastro_la_SOURCES = arithmetic.c arithmetic-and.c
arithmetic-bitand.c \
interpolate.c jpeg.c label.c list.c match.c options.c pdf.c \
permutation.c pointer.c polygon.c qsort.c dimension.c speclines.c \
statistics.c table.c tableintern.c threads.c tiff.c tile.c \
- tile-internal.c timing.c txt.c type.c wcs.c
+ tile-internal.c timing.c txt.c type.c units.c wcs.c
@@ -83,7 +83,7 @@ pkginclude_HEADERS = gnuastro/config.h
$(headersdir)/arithmetic.h \
$(headersdir)/speclines.h $(headersdir)/statistics.h \
$(headersdir)/table.h $(headersdir)/threads.h $(headersdir)/tiff.h \
$(headersdir)/tile.h $(headersdir)/txt.h $(headersdir)/type.h \
- $(headersdir)/wcs.h
+ $(headersdir)/units.h $(headersdir)/wcs.h
diff --git a/lib/arithmetic.c b/lib/arithmetic.c
index bab5707..5367c40 100644
--- a/lib/arithmetic.c
+++ b/lib/arithmetic.c
@@ -31,6 +31,7 @@ along with Gnuastro. If not, see
<http://www.gnu.org/licenses/>.
#include <gnuastro/list.h>
#include <gnuastro/blank.h>
+#include <gnuastro/units.h>
#include <gnuastro/qsort.h>
#include <gnuastro/pointer.h>
#include <gnuastro/threads.h>
@@ -396,6 +397,44 @@ arithmetic_abs(int flags, gal_data_t *in)
"UNIARY_FUNCTION_ON_ELEMENT", in->type); \
}
+#define UNIARY_FUNCTION_ON_ELEMENT_OUTPUT_STRING(OP) \
+ switch(in->type) \
+ { \
+ case GAL_TYPE_UINT8: \
+ UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, uint8_t, OP) \
+ break; \
+ case GAL_TYPE_INT8: \
+ UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, int8_t, OP) \
+ break; \
+ case GAL_TYPE_UINT16: \
+ UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, uint16_t, OP) \
+ break; \
+ case GAL_TYPE_INT16: \
+ UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, int16_t, OP) \
+ break; \
+ case GAL_TYPE_UINT32: \
+ UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, uint32_t, OP) \
+ break; \
+ case GAL_TYPE_INT32: \
+ UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, int32_t, OP) \
+ break; \
+ case GAL_TYPE_UINT64: \
+ UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, uint64_t, OP) \
+ break; \
+ case GAL_TYPE_INT64: \
+ UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, int64_t, OP) \
+ break; \
+ case GAL_TYPE_FLOAT32: \
+ UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, float, OP) \
+ break; \
+ case GAL_TYPE_FLOAT64: \
+ UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, double, OP) \
+ break; \
+ default: \
+ error(EXIT_FAILURE, 0, "%s: type code %d not recognized", \
+ "UNIARY_FUNCTION_ON_ELEMENT_OUTPUT_STRING", in->type); \
+ }
+
#define UNIARY_FUNCTION_ON_ELEMENT(OP) \
switch(in->type) \
{ \
@@ -434,6 +473,13 @@ arithmetic_abs(int flags, gal_data_t *in)
"UNIARY_FUNCTION_ON_ELEMENT", in->type); \
}
+
+#define UNIFUNC_RUN_FUNCTION_ON_ELEMENT_STRING(OT, OP){ \
+ OT *oa=o->array; \
+ char **ia=in->array, **iaf=ia + in->size; \
+ do *oa++ = OP(*ia++); while(ia<iaf); \
+}
+
static gal_data_t *
arithmetic_unary_function(int operator, int flags, gal_data_t *in)
{
@@ -446,7 +492,11 @@ arithmetic_unary_function(int operator, int flags,
gal_data_t *in)
point). So even if the user requested inplace opereation, if its not a
floating point type, its not useful.*/
if( (flags & GAL_ARITHMETIC_INPLACE)
- && (in->type==GAL_TYPE_FLOAT32 || in->type==GAL_TYPE_FLOAT64) )
+ && (in->type==GAL_TYPE_FLOAT32 || in->type==GAL_TYPE_FLOAT64)
+ && (operator != GAL_ARITHMETIC_CONVERT_DECIMAL_RA
+ && operator != GAL_ARITHMETIC_CONVERT_DECIMAL_DEC
+ && operator != GAL_ARITHMETIC_CONVERT_RA_DECIMAL
+ && operator != GAL_ARITHMETIC_CONVERT_DEC_DECIMAL ) )
inplace=1;
if(inplace)
@@ -459,6 +509,16 @@ arithmetic_unary_function(int operator, int flags,
gal_data_t *in)
otype = ( in->type==GAL_TYPE_FLOAT64
? GAL_TYPE_FLOAT64
: GAL_TYPE_FLOAT32 );
+
+ /* Check for operators which have fixed output types */
+ if ( operator == GAL_ARITHMETIC_CONVERT_RA_DECIMAL ||
+ operator == GAL_ARITHMETIC_CONVERT_DEC_DECIMAL )
+ otype = GAL_TYPE_FLOAT64;
+
+ if (operator == GAL_ARITHMETIC_CONVERT_DECIMAL_RA ||
+ operator == GAL_ARITHMETIC_CONVERT_DECIMAL_DEC)
+ otype = GAL_TYPE_STRING;
+
o = gal_data_alloc(NULL, otype, in->ndim, in->dsize, in->wcs,
0, in->minmapsize, in->quietmmap,
NULL, NULL, NULL);
@@ -479,6 +539,22 @@ arithmetic_unary_function(int operator, int flags,
gal_data_t *in)
UNIARY_FUNCTION_ON_ELEMENT( log10 );
break;
+ case GAL_ARITHMETIC_CONVERT_RA_DECIMAL:
+ UNIFUNC_RUN_FUNCTION_ON_ELEMENT_STRING( double, gal_units_ra_to_decimal
);
+ break;
+
+ case GAL_ARITHMETIC_CONVERT_DEC_DECIMAL:
+ UNIFUNC_RUN_FUNCTION_ON_ELEMENT_STRING( double, gal_units_dec_to_decimal
);
+ break;
+
+ case GAL_ARITHMETIC_CONVERT_DECIMAL_RA:
+ UNIARY_FUNCTION_ON_ELEMENT_OUTPUT_STRING( gal_units_decimal_to_ra );
+ break;
+
+ case GAL_ARITHMETIC_CONVERT_DECIMAL_DEC:
+ UNIARY_FUNCTION_ON_ELEMENT_OUTPUT_STRING( gal_units_decimal_to_dec );
+ break;
+
default:
error(EXIT_FAILURE, 0, "%s: operator code %d not recognized",
__func__, operator);
@@ -1738,6 +1814,16 @@ gal_arithmetic_set_operator(char *string, size_t
*num_operands)
else if (!strcmp(string, "log10"))
{ op=GAL_ARITHMETIC_OP_LOG10; *num_operands=1; }
+ /* Units conversion functions */
+ else if (!strcmp(string, "ra-to-decimal"))
+ { op=GAL_ARITHMETIC_CONVERT_RA_DECIMAL; *num_operands=1; }
+ else if (!strcmp(string, "dec-to-decimal"))
+ { op=GAL_ARITHMETIC_CONVERT_DEC_DECIMAL; *num_operands=1; }
+ else if (!strcmp(string, "decimal-to-ra"))
+ { op=GAL_ARITHMETIC_CONVERT_DECIMAL_RA; *num_operands=1; }
+ else if (!strcmp(string, "decimal-to-dec"))
+ { op = GAL_ARITHMETIC_CONVERT_DECIMAL_DEC;*num_operands=1; }
+
/* Statistical/higher-level operators. */
else if (!strcmp(string, "minvalue"))
{ op=GAL_ARITHMETIC_OP_MINVAL; *num_operands=1; }
@@ -1888,6 +1974,11 @@ gal_arithmetic_operator_string(int operator)
case GAL_ARITHMETIC_OP_LOG: return "log";
case GAL_ARITHMETIC_OP_LOG10: return "log10";
+ case GAL_ARITHMETIC_CONVERT_RA_DECIMAL: return "ra-to-decimal";
+ case GAL_ARITHMETIC_CONVERT_DEC_DECIMAL:return "dec-to-decimal";
+ case GAL_ARITHMETIC_CONVERT_DECIMAL_RA: return "decimal-to-ra";
+ case GAL_ARITHMETIC_CONVERT_DECIMAL_DEC:return "decimal-to-dec";
+
case GAL_ARITHMETIC_OP_MINVAL: return "minvalue";
case GAL_ARITHMETIC_OP_MAXVAL: return "maxvalue";
case GAL_ARITHMETIC_OP_NUMBERVAL: return "numbervalue";
@@ -1986,6 +2077,10 @@ gal_arithmetic(int operator, size_t numthreads, int
flags, ...)
case GAL_ARITHMETIC_OP_SQRT:
case GAL_ARITHMETIC_OP_LOG:
case GAL_ARITHMETIC_OP_LOG10:
+ case GAL_ARITHMETIC_CONVERT_RA_DECIMAL:
+ case GAL_ARITHMETIC_CONVERT_DEC_DECIMAL:
+ case GAL_ARITHMETIC_CONVERT_DECIMAL_RA:
+ case GAL_ARITHMETIC_CONVERT_DECIMAL_DEC:
d1 = va_arg(va, gal_data_t *);
out=arithmetic_unary_function(operator, flags, d1);
break;
diff --git a/lib/gnuastro/arithmetic.h b/lib/gnuastro/arithmetic.h
index 55fb624..f39fc1e 100644
--- a/lib/gnuastro/arithmetic.h
+++ b/lib/gnuastro/arithmetic.h
@@ -106,6 +106,11 @@ enum gal_arithmetic_operators
GAL_ARITHMETIC_OP_LOG, /* log() */
GAL_ARITHMETIC_OP_LOG10, /* log10() */
+ GAL_ARITHMETIC_CONVERT_RA_DECIMAL, /* right ascension to decimal */
+ GAL_ARITHMETIC_CONVERT_DEC_DECIMAL, /* declination to decimal */
+ GAL_ARITHMETIC_CONVERT_DECIMAL_RA, /* right ascension to decimal */
+ GAL_ARITHMETIC_CONVERT_DECIMAL_DEC, /* declination to decimal */
+
GAL_ARITHMETIC_OP_MINVAL, /* Minimum value of array. */
GAL_ARITHMETIC_OP_MAXVAL, /* Maximum value of array. */
GAL_ARITHMETIC_OP_NUMBERVAL, /* Number of (non-blank) elements. */
diff --git a/lib/gnuastro/units.h b/lib/gnuastro/units.h
new file mode 100644
index 0000000..952e141
--- /dev/null
+++ b/lib/gnuastro/units.h
@@ -0,0 +1,81 @@
+/*********************************************************************
+Units -- Convert data from one unit to other.
+This is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+ Kartik Ohri <address@hidden>
+Contributing author(s):
+ Mohammad Akhlaghi <address@hidden>
+Copyright (C) 2020, Free Software Foundation, Inc.
+
+Gnuastro is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+#ifndef __GAL_UNITS_H__
+#define __GAL_UNITS_H__
+
+/* When we are within Gnuastro's building process, `IN_GNUASTRO_BUILD' is
+ defined. In the build process, installation information (in particular
+ `GAL_CONFIG_SIZEOF_SIZE_T' that we need below) is kept in
+ `config.h'. When building a user's programs, this information is kept in
+ `gnuastro/config.h'. Note that all `.c' files in Gnuastro's source must
+ start with the inclusion of `config.h' and that `gnuastro/config.h' is
+ only created at installation time (not present during the building of
+ Gnuastro). */
+#ifndef IN_GNUASTRO_BUILD
+#include <gnuastro/config.h>
+#endif
+
+
+/* C++ Preparations */
+#undef __BEGIN_C_DECLS
+#undef __END_C_DECLS
+#ifdef __cplusplus
+# define __BEGIN_C_DECLS extern "C" {
+# define __END_C_DECLS }
+#else
+# define __BEGIN_C_DECLS /* empty */
+# define __END_C_DECLS /* empty */
+#endif
+/* End of C++ preparations */
+
+
+
+/* Actual header contants (the above were for the Pre-processor). */
+__BEGIN_C_DECLS /* From C++ preparations */
+
+
+
+
+
+/*************************************************************
+ ************** Convert units to decimal ***************
+ *************************************************************/
+double
+gal_units_ra_to_decimal (char *convert);
+
+double
+gal_units_dec_to_decimal (char *convert);
+
+/*************************************************************
+ ************** Convert decimal to units ***************
+ *************************************************************/
+char *
+gal_units_decimal_to_ra (double decimal);
+
+char *
+gal_units_decimal_to_dec (double decimal);
+
+__END_C_DECLS /* From C++ preparations */
+
+#endif /* __GAL_UNITS_H__ */
\ No newline at end of file
diff --git a/lib/units.c b/lib/units.c
new file mode 100644
index 0000000..e23fc35
--- /dev/null
+++ b/lib/units.c
@@ -0,0 +1,340 @@
+/*********************************************************************
+Units -- Convert data from one unit to other.
+This is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+ Kartik Ohri <address@hidden>
+Contributing author(s):
+ Mohammad Akhlaghi <address@hidden>
+Copyright (C) 2020, Free Software Foundation, Inc.
+
+Gnuastro is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+#include <config.h>
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <error.h>
+
+
+
+
+
+/**********************************************************************/
+/**************** Functions to parse strings *****************/
+/**********************************************************************/
+
+
+/* Parse the input string consisting of numbers separated by given
+ * delimiter into an array. */
+int
+gal_units_extract_decimal (char *convert, const char *delimiter,
+ double *args, size_t n)
+{
+ char *copy, *token, *end;
+ size_t i = 0;
+ /* Create a copy of the string to be parsed */
+ copy = strdup (convert);
+
+ do
+ {
+ /* Check if the required number of arguments are passed */
+ if (i == n + 1)
+ {
+ free (copy);
+ error (0, 0, "'%s' exceeds maximum number of "
+ "arguments\n", convert);
+ return 0;
+ }
+
+
+ /* Extract the substring till the next delimiter */
+ token = strtok (i == 0 ? copy : NULL, delimiter);
+
+ if (token)
+ {
+ /* Parse extracted string as a number */
+ args[i++] = strtod (token, &end);
+ /* Check if strtod fails to parse any character in substring */
+ if (*end && *end != delimiter)
+ {
+ free (copy);
+ error (0, 0, "Unable to parse element %lu in "
+ "'%s'\n", i, convert);
+ return 0;
+ }
+ }
+
+ }
+ while (token && *token);
+
+ free (copy);
+ /* Check if the number of elements parsed is unequal to numbers of
+ * elements requested */
+ if (i != n)
+ {
+ error (0, 0, "`%s' must contain %lu numbers, but has "
+ "%lu numbers\n", convert, n, i);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**********************************************************************/
+/**************** Convert string to decimal *****************/
+/**********************************************************************/
+
+
+/* Parse the right ascension input as a string in form of hh:mm:ss to a
+ * single decimal value calculated by (hh + mm / 60 + ss / 3600 ) * 15. */
+double
+gal_units_ra_to_decimal (char *convert)
+{
+ double val[3];
+ double decimal = 0.0;
+
+ /* Check whether the string is successfully parsed */
+ if (gal_units_extract_decimal (convert, ":", val, 3))
+ {
+ /* Check whether value of hours is in within limits */
+ if (val[0] < 0.0 || val[0] > 24.0)
+ {
+ error (0, 0, "Error: Value of hours should be "
+ "between be 0 and 24. %s\n", convert);
+ return NAN;
+ }
+ /* Add hours to the decimal value */
+ decimal += val[0];
+
+ /* Check whether value of minutes is in within limits */
+ if (val[1] < 0.0 || val[1] > 60.0)
+ {
+ error (0, 0, "Error: Value of minutes should be "
+ "between be 0 and 60. %s\n", convert);
+ return NAN;
+ }
+ /* Convert minutes to hours and add to the decimal value */
+ decimal += val[1] / 60;
+
+ /* Check whether value of seconds is in within limits */
+ if (val[2] < 0.0 || val[2] > 60.0)
+ {
+ error (0, 0, "Error: Value of seconds should be "
+ "between be 0 and 60. %s\n", convert);
+ return NAN;
+ }
+ /* Convert seconds to hours and add to the decimal value */
+ decimal += val[2] / 3600;
+
+ /* Convert value to decimal */
+ decimal *= 15.0;
+
+ return decimal;
+ }
+ else
+ return NAN;
+}
+
+/* Parse the declination input as a string in form of dd:mm:ss to a decimal
+ * calculated by (dd + mm / 60 + ss / 3600 ). */
+double
+gal_units_dec_to_decimal (char *convert)
+{
+ double val[3];
+ double decimal = 0.0;
+ int sign = 1;
+
+ if (gal_units_extract_decimal (convert, ":", val, 3))
+ {
+ /* Check whether value of decimal is in within limits */
+ if (val[0] < -90.0 || val[0] > 90.0)
+ {
+ error (0, 0, "Error: Value of decimal should be "
+ "between be -90 and 90. %s\n", convert);
+ return NAN;
+ }
+
+ /* If declination is negative, the first value in the array will be
+ * negative and all other values will be positive. In that case, we
+ * set sign equal to -1. Therefore, we multiply the first value by
+ * sign to make it positive. The final answer is again multiplied by
+ * sign to make its sign same as original. */
+ if (val[0] < 0.0)
+ sign = -1;
+
+ /* Add decimal to the decimal value after making it positive */
+ decimal += val[0] * sign;
+
+ /* Check whether value of arc-minutes is in within limits */
+ if (val[1] < 0.0 || val[1] > 60.0)
+ {
+ error (0, 0, "Error: Value of arc-minutes should be "
+ "between be 0 and 60. %s\n", convert);
+ return NAN;
+ }
+ /* Convert arc-minutes to decimal and add to the decimal value */
+ decimal += val[1] / 60;
+
+ /* Check whether value of arc-seconds is in within limits */
+ if (val[2] < 0.0 || val[2] > 60.0)
+ {
+ error (0, 0, "Error: Value of arc-seconds should be "
+ "between be 0 and 60. %s\n", convert);
+ return NAN;
+ }
+ /* Convert arc-seconds to decimal and add to the decimal value */
+ decimal += val[2] / 3600;
+
+ /* Make the sign of the decimal value same as input */
+ decimal *= sign;
+
+ return decimal;
+ }
+ else
+ return NAN;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**********************************************************************/
+/**************** Convert decimal to string *****************/
+/**********************************************************************/
+
+
+/* Parse the right ascension input as a decimal to a string in form of
+ * hh:mm:ss.ss . */
+char *
+gal_units_decimal_to_ra (double decimal)
+{
+ int hours = 0, minutes = 0;
+ float seconds = 0.0; /* For sub-second accuracy */
+ /* Allocate string of length 15 which is large enough for string of
+ * format hh:mm:ss.ss and sign */
+ char *ra = ( char * ) malloc (sizeof (char) * 15);
+
+ /* Check if decimal value is within bounds otherwise return error */
+ if (decimal < 0 || decimal > 360)
+ {
+ error (0, 0, "Error: Value of decimal should be "
+ "between be 0 and 360. %.10f\n", decimal);
+ return 0;
+ }
+
+ /* Divide decimal value by 15 */
+ decimal /= 15.0;
+
+ /* Extract integer part of decimal value to obtain hours */
+ hours = ( int ) (decimal);
+
+ /* Subtract hours from decimal and multiply remaining value by 60 to
+ * obtain minutes. */
+ minutes = ( int ) ((decimal - hours) * 60);
+
+ /* Subtract hours and minutes from decimal and multiply remaining value
+ * by 3600 to obtain seconds. */
+ seconds = (decimal - hours - minutes / 60.0) * 3600;
+
+ /* Format the extracted hours, minutes and seconds as a string with
+ * leading zeros if required, in hh:mm:ss format */
+ snprintf (ra,
+ sizeof (char) * 15, "%02d:%02d:%05.2f", hours, minutes, seconds);
+
+ return ra;
+}
+
+/* Parse the declination input as a decimal to a string in form of dd:mm:ss*/
+char *
+gal_units_decimal_to_dec (double decimal)
+{
+ int degrees = 0, arc_minutes = 0;
+ float arc_seconds = 0.0;
+ /* Allocate string of length 15 which is large enough for string of
+ * format hh:mm:ss.ss and sign */
+ char *dec = ( char * ) malloc (sizeof (char) * 15);
+ int sign = 1;
+
+ /* Check if decimal value is within bounds otherwise return error */
+ if (decimal < -90 || decimal > 90)
+ {
+ error (0, 0, "Error: Value of decimal should be "
+ "between be -90 and 90. %.10f\n", decimal);
+ return 0;
+ }
+
+ /* If declination is negative, we set sign equal to -1. We multiply the
+ * decimal by to make sure it is positive. We then extract degrees,
+ * arc-minutes and arc-seconds from the decimal. Finally, we add a minus
+ * sign in beginning of string if input was negative. */
+ if (decimal < 0)
+ sign = -1;
+
+ /* Multiply decimal by sign to make its positive. */
+ decimal *= sign;
+
+ /* Extract integer part of decimal value to obtain degrees. */
+ degrees = ( int ) (decimal);
+
+ /* Subtract degrees from decimal and multiply remaining value by 60 to
+ * obtain arc-minutes. */
+ arc_minutes = ( int ) ((decimal - degrees) * 60);
+
+ /* Subtract degrees and arc-minutes from decimal and multiply remaining
+ * value by 3600 to obtain arc-seconds. */
+ arc_seconds = (decimal - degrees - arc_minutes / 60.0) * 3600;
+
+ /* Format the extracted degrees, arc-minutes and arc-seconds as a string
+ * with leading zeros if required, in hh:mm:ss format with correct sign */
+ if (sign < 0)
+ snprintf (dec, sizeof (char)
+ * 15, "-%02d:%02d:%05.2f", degrees, arc_minutes,
arc_seconds);
+ else
+ snprintf (dec, sizeof (char)
+ * 15, "%02d:%02d:%05.2f", degrees, arc_minutes,
arc_seconds);
+
+ return dec;
+}
\ No newline at end of file