Exemplo n.º 1
0
/**
 * Read QTH file and add data to list store.
 * @param liststore The GtkListStore where the data should be stored.
 * @param filename The full name of the qth file.
 * @return 1 if read is successful, 0 if an error occurs.
 *
 * The function uses the gtk-sat-data infrastructure to read the qth
 * data from the specified file.
 *
 * There is a little challenge here. First, we want to read the data from
 * the .qth files and store them in the list store. To do this we use a
 * qth_t structure, which can be populated using gtk_sat_data_read_qth.
 * Then, when the configuration is finished and the user presses "OK", we
 * want to write all the data back to the .qth files. To do that, we create
 * an up-to-date qth_t data structure and pass it to the gtk_sat_data_write_qth
 * function, which will take care of updating the GKeyFile data structure and
 * writing the contents to the .qth file.
 */
static guint read_qth_file(GtkListStore * liststore, gchar * filename)
{
    GtkTreeIter     item;       /* new item added to the list store */
    qth_t          *qth;        /* qth data structure */
    gchar          *defqth;
    gboolean        is_default = FALSE;
    gchar          *fname;
    gint            dispalt;    /* displayed altitude */

    if ((qth = g_try_new0(qth_t, 1)) == NULL)
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s:%d: Failed to allocate memory!\n"),
                    __FILE__, __LINE__);

        return FALSE;
    }

    /* read data from file */
    if (!qth_data_read(filename, qth))
    {
        g_free(qth);
        return FALSE;
    }

    /* calculate QRA locator */
    gint            retcode;

    qth->qra = g_malloc(7);
    retcode = longlat2locator(qth->lon, qth->lat, qth->qra, 3);

    if (retcode != RIG_OK)
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s:%d: Could not convert (%.2f,%.2f) to QRA."),
                    __FILE__, __LINE__, qth->lat, qth->lon);
        qth->qra[0] = '\0';
    }
    else
    {
        qth->qra[6] = '\0';
        sat_log_log(SAT_LOG_LEVEL_DEBUG,
                    _("%s:%d: QRA locator is %s"),
                    __FILE__, __LINE__, qth->qra);
    }

    /* is this the default qth? */
    defqth = sat_cfg_get_str(SAT_CFG_STR_DEF_QTH);

    if (g_str_has_suffix(filename, defqth))
    {
        is_default = TRUE;

        sat_log_log(SAT_LOG_LEVEL_INFO,
                    _("%s:%d: This appears to be the default QTH."),
                    __FILE__, __LINE__);
    }

    g_free(defqth);

    /* check wehter we are using imperial or metric system;
       in case of imperial we have to convert altitude from
       meters to feet.
       note: the internat data are always kept in metric and
       only the displayed numbers are converted. Therefore,
       we use a dedicated var 'dispalt'
     */
    /* NO, ONLY DISPLAY WIDGETS WILL BE AFFECTED BY THIS SETTING */
    if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_IMPERIAL))
    {
        dispalt = (gint) M_TO_FT(qth->alt);
    }
    else
    {
        dispalt = (gint) qth->alt;
    }

    /* strip file name; we don't need the whole path */
    fname = g_path_get_basename(filename);

    /* we now have all necessary data in the qth_t structure;
       add the data to the list store */
    gtk_list_store_append(liststore, &item);
    gtk_list_store_set(liststore, &item,
                       QTH_LIST_COL_NAME, qth->name,
                       QTH_LIST_COL_LOC, qth->loc,
                       QTH_LIST_COL_DESC, qth->desc,
                       QTH_LIST_COL_LAT, qth->lat,
                       QTH_LIST_COL_LON, qth->lon,
                       QTH_LIST_COL_ALT, dispalt,
                       QTH_LIST_COL_QRA, qth->qra,
                       QTH_LIST_COL_WX, qth->wx,
                       QTH_LIST_COL_DEF, is_default,
                       QTH_LIST_COL_TYPE, qth->type,
                       QTH_LIST_COL_GPSD_SERVER, qth->gpsd_server,
                       QTH_LIST_COL_GPSD_PORT, qth->gpsd_port, -1);

    g_free(fname);

    /* we are finished with this qth, free it */
    qth_data_free(qth);

    return TRUE;
}
Exemplo n.º 2
0
/** \brief Read QTH data from file.
 *  \param filename The file to read from.
 *  \param qth Pointer to a qth_t data structure where the data will be stored.
 *  \return FALSE if an error occurred, TRUE otherwise.
 *
 *  \note The function uses the new key=value file parser from glib.
 */
gint qth_data_read(const gchar *filename, qth_t *qth)
{
    GError *error = NULL;
    gchar *buff;
    gchar **buffv;

    qth->data = g_key_file_new();
    g_key_file_set_list_separator(qth->data, ';');

    /* bail out with error message if data can not be read */
    if (!g_key_file_load_from_file(qth->data, filename, G_KEY_FILE_KEEP_COMMENTS, &error))
    {

        g_key_file_free(qth->data);
        qth->data = NULL;

        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s: Could not load data from %s (%s)"),
                    __FUNCTION__, filename, error->message);

        return FALSE;
    }

    /* send a debug message, then read data */
    sat_log_log(SAT_LOG_LEVEL_DEBUG, _("%s: QTH data: %s"), __FUNCTION__, filename);

    /*** FIXME: should check that strings are UTF-8? */
    /* QTH Name */
    buff = g_path_get_basename(filename);
    buffv = g_strsplit(buff, ".qth", 0);
    qth->name = g_strdup(buffv[0]);

    g_free(buff);
    g_strfreev(buffv);
    /* g_key_file_get_string (qth->data, */
    /*                        QTH_CFG_MAIN_SECTION, */
    /*                        QTH_CFG_NAME_KEY, */
    /*                        &error); */
    if (error != NULL)
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s: Error reading QTH name (%s)."), __FUNCTION__, error->message);

        qth->name = g_strdup(_("ERROR"));
        g_clear_error(&error);
    }

    /* QTH location */
    qth->loc = g_key_file_get_string(qth->data, QTH_CFG_MAIN_SECTION, QTH_CFG_LOC_KEY, &error);
    if (error != NULL)
    {
        sat_log_log(SAT_LOG_LEVEL_INFO,
                    _("%s: QTH has no location (%s)."), __FUNCTION__, error->message);

        qth->loc = g_strdup("");
        g_clear_error(&error);
    }

    /* QTH description */
    qth->desc = g_key_file_get_string(qth->data, QTH_CFG_MAIN_SECTION, QTH_CFG_DESC_KEY, &error);
    if ((qth->desc == NULL) || (error != NULL))
    {
        sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: QTH has no description."), __FUNCTION__);

        qth->desc = g_strdup("");
        g_clear_error(&error);
    }

    /* Weather station */
    qth->wx = g_key_file_get_string(qth->data, QTH_CFG_MAIN_SECTION, QTH_CFG_WX_KEY, &error);
    if ((qth->wx == NULL) || (error != NULL))
    {
        sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: QTH has no weather station."), __FUNCTION__);

        qth->wx = g_strdup("");
        g_clear_error(&error);
    }

    /* QTH Latitude */
    buff = g_key_file_get_string(qth->data, QTH_CFG_MAIN_SECTION, QTH_CFG_LAT_KEY, &error);
    if (error != NULL)
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s: Error reading QTH latitude (%s)."), __FUNCTION__, error->message);

        g_clear_error(&error);

        if (buff != NULL)
            g_free(buff);

        qth->lat = 0.0;
    }
    else
    {
        qth->lat = g_ascii_strtod(buff, NULL);
        g_free(buff);
    }

    /* QTH Longitude */
    buff = g_key_file_get_string(qth->data, QTH_CFG_MAIN_SECTION, QTH_CFG_LON_KEY, &error);
    if (error != NULL)
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s: Error reading QTH longitude (%s)."), __FUNCTION__, error->message);

        g_clear_error(&error);

        if (buff != NULL)
            g_free(buff);

        qth->lon = 0.0;
    }
    else
    {
        qth->lon = g_ascii_strtod(buff, NULL);
        g_free(buff);
    }

    /* QTH Altitude */
    qth->alt = g_key_file_get_integer(qth->data, QTH_CFG_MAIN_SECTION, QTH_CFG_ALT_KEY, &error);
    if (error != NULL)
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s: Error reading QTH altitude (%s)."), __FUNCTION__, error->message);

        g_clear_error(&error);

        if (buff != NULL)
            g_free(buff);

        qth->alt = 0;
    }
    else
    {
    }


    /* QTH Type */
    qth->type = g_key_file_get_integer(qth->data, QTH_CFG_MAIN_SECTION, QTH_CFG_TYPE_KEY, &error);
    if (error != NULL)
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s: Error reading QTH type (%s)."), __FUNCTION__, error->message);

        g_clear_error(&error);


        qth->type = QTH_STATIC_TYPE;
    }

    /* GPSD Port */
    qth->gpsd_port = g_key_file_get_integer(qth->data,
                                            QTH_CFG_MAIN_SECTION, QTH_CFG_GPSD_PORT_KEY, &error);
    if (error != NULL)
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s: Error reading QTH type (%s)."), __FUNCTION__, error->message);

        g_clear_error(&error);

        /*if not set put it on the default port */
        qth->gpsd_port = 2947;
    }

    /* GPSD Server */
    qth->gpsd_server = g_key_file_get_string(qth->data,
                                             QTH_CFG_MAIN_SECTION, QTH_CFG_GPSD_SERVER_KEY, &error);
    if ((qth->gpsd_server == NULL) || (error != NULL))
    {
        sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: QTH has no GPSD Server."), __FUNCTION__);

        qth->gpsd_server = g_strdup("");
        g_clear_error(&error);
    }

    /* set QRA based on data */
    if (longlat2locator(qth->lon, qth->lat, qth->qra, 2) != RIG_OK)
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s: Could not set QRA for %s at %f, %f."),
                    __FUNCTION__, qth->name, qth->lon, qth->lat);
    }

    qth_validate(qth);

    /* Now, send debug message and return */
    sat_log_log(SAT_LOG_LEVEL_INFO,
                _("%s: QTH data: %s, %.4f, %.4f, %d"),
                __FUNCTION__, qth->name, qth->lat, qth->lon, qth->alt);

    return TRUE;
}
Exemplo n.º 3
0
int main (int argc, char *argv[]) {
	char recodedloc[13], *loc1, *loc2, sign;
	double lon1, lat1, lon2, lat2;
	double distance, az, mmm, sec;
	int  deg, min, retcode, loc_len, nesw = 0;

	if (argc < 2) {
		fprintf(stderr, "Usage: %s <locator1> <precision> [<locator2>]\n", argv[0]);
		exit(1);
	}

	loc1 = argv[1];
       	loc_len = argc > 2 ? atoi(argv[2]) : strlen(loc1)/2;
	loc2 = argc > 3 ? argv[3] : NULL;

	printf("Locator1:\t%s\n", loc1);

        /* hamlib function to convert maidenhead to decimal degrees */
	retcode = locator2longlat(&lon1, &lat1, loc1);
	if (retcode != RIG_OK) {
		fprintf(stderr, "locator2longlat() failed with malformed input.\n");
		exit(2);
	}

        /* hamlib function to convert decimal degrees to deg, min, sec */
	retcode = dec2dms(lon1, &deg, &min, &sec, &nesw);
	if (retcode != RIG_OK) {
		fprintf(stderr, "dec2dms() failed, invalid paramter address.\n");
		exit(2);
	}
	if (nesw == 1)
		sign = '-';
	else
                sign = '\0';
	printf("  Longitude:\t%f\t%c%d %d' %.2f\"\n", lon1, sign, deg, min, sec);

        /* hamlib function to convert deg, min, sec to decimal degrees */
	lon1 = dms2dec(deg, min, sec, nesw);
	printf("  Recoded lon:\t%f\n", lon1);

        /* hamlib function to convert decimal degrees to deg decimal minutes */
	retcode = dec2dmmm(lon1, &deg, &mmm, &nesw);
	if (retcode != RIG_OK) {
		fprintf(stderr, "dec2dmmm() failed, invalid paramter address.\n");
		exit(2);
	}
	if (nesw == 1)
		sign = '-';
	else
                sign = '\0';
	printf("  GPS lon:\t%f\t%c%d %.3f'\n", lon1, sign, deg, mmm);

        /* hamlib function to convert deg, decimal min to decimal degrees */
	lon1 = dmmm2dec(deg, mmm, nesw);
	printf("  Recoded GPS:\t%f\n", lon1);

        /* hamlib function to convert decimal degrees to deg, min, sec */
	retcode = dec2dms(lat1, &deg, &min, &sec, &nesw);
	if (retcode != RIG_OK) {
		fprintf(stderr, "dec2dms() failed, invalid paramter address.\n");
		exit(2);
	}
	if (nesw == 1)
		sign = '-';
	else
                sign = '\0';
	printf("  Latitude:\t%f\t%c%d %d' %.2f\"\n", lat1, sign, deg, min, sec);

        /* hamlib function to convert deg, min, sec to decimal degrees */
	lat1 = dms2dec(deg, min, sec, nesw);
	printf("  Recoded lat:\t%f\n", lat1);

        /* hamlib function to convert decimal degrees to deg decimal minutes */
	retcode = dec2dmmm(lat1, &deg, &mmm, &nesw);
	if (retcode != RIG_OK) {
		fprintf(stderr, "dec2dmmm() failed, invalid paramter address.\n");
		exit(2);
	}
	if (nesw == 1)
		sign = '-';
	else
                sign = '\0';
	printf("  GPS lat:\t%f\t%c%d %.3f'\n", lat1, sign, deg, mmm);

        /* hamlib function to convert deg, decimal min to decimal degrees */
	lat1 = dmmm2dec(deg, mmm, nesw);
	printf("  Recoded GPS:\t%f\n", lat1);

        /* hamlib function to convert decimal degrees to maidenhead */
	retcode = longlat2locator(lon1, lat1, recodedloc, loc_len);
	if (retcode != RIG_OK) {
		fprintf(stderr, "longlat2locator() failed, precision out of range.\n");
		exit(2);
	}
	printf("  Recoded:\t%s\n", recodedloc);

	if (loc2 == NULL)
		exit(0);

	/* Now work on the second locator */
	printf("\nLocator2:\t%s\n", loc2);

	retcode = locator2longlat(&lon2, &lat2, loc2);
	if (retcode != RIG_OK) {
		fprintf(stderr, "locator2longlat() failed with malformed input.\n");
		exit(2);
	}

	/* hamlib function to convert decimal degrees to deg, min, sec */
	retcode = dec2dms(lon2, &deg, &min, &sec, &nesw);
	if (retcode != RIG_OK) {
		fprintf(stderr, "dec2dms() failed, invalid paramter address.\n");
		exit(2);
	}
	if (nesw == 1)
		sign = '-';
	else
                sign = '\0';
	printf("  Longitude:\t%f\t%c%d %d' %.2f\"\n", lon2, sign, deg, min, sec);

	/* hamlib function to convert deg, min, sec to decimal degrees */
	lon2 = dms2dec(deg, min, sec, nesw);
	printf("  Recoded lon:\t%f\n", lon2);

	/* hamlib function to convert decimal degrees to deg decimal minutes */
	retcode = dec2dmmm(lon2, &deg, &mmm, &nesw);
	if (retcode != RIG_OK) {
		fprintf(stderr, "dec2dmmm() failed, invalid paramter address.\n");
		exit(2);
	}
	if (nesw == 1)
		sign = '-';
	else
                sign = '\0';
	printf("  GPS lon:\t%f\t%c%d %.3f'\n", lon2, sign, deg, mmm);

	/* hamlib function to convert deg, decimal min to decimal degrees */
	lon2 = dmmm2dec(deg, mmm, nesw);
	printf("  Recoded GPS:\t%f\n", lon2);

	/* hamlib function to convert decimal degrees to deg, min, sec */
	retcode = dec2dms(lat2, &deg, &min, &sec, &nesw);
	if (retcode != RIG_OK) {
		fprintf(stderr, "dec2dms() failed, invalid paramter address.\n");
		exit(2);
	}
	if (nesw == 1)
		sign = '-';
	else
                sign = '\0';
	printf("  Latitude:\t%f\t%c%d %d' %.2f\"\n", lat2, sign, deg, min, sec);

	/* hamlib function to convert deg, min, sec to decimal degrees */
	lat2 = dms2dec(deg, min, sec, nesw);
	printf("  Recoded lat:\t%f\n", lat2);

	/* hamlib function to convert decimal degrees to deg decimal minutes */
	retcode = dec2dmmm(lat2, &deg, &mmm, &nesw);
	if (retcode != RIG_OK) {
		fprintf(stderr, "dec2dmmm() failed, invalid paramter address.\n");
		exit(2);
	}
	if (nesw == 1)
		sign = '-';
	else
                sign = '\0';
	printf("  GPS lat:\t%f\t%c%d %.3f'\n", lat2, sign, deg, mmm);

        /* hamlib function to convert deg, decimal min to decimal degrees */
	lat2 = dmmm2dec(deg, mmm, nesw);
	printf("  Recoded GPS:\t%f\n", lat2);

        /* hamlib function to convert decimal degrees to maidenhead */
	retcode = longlat2locator(lon2, lat2, recodedloc, loc_len);
	if (retcode != RIG_OK) {
		fprintf(stderr, "longlat2locator() failed, precision out of range.\n");
		exit(2);
	}
	printf("  Recoded:\t%s\n", recodedloc);

	retcode = qrb(lon1, lat1, lon2, lat2, &distance, &az);
	if (retcode != RIG_OK) {
		fprintf(stderr, "QRB error: %d\n", retcode);
		exit(2);
	}

	dec2dms(az, &deg, &min, &sec, &nesw);
	printf("\nDistance: %.6fkm\n", distance);
	if (nesw == 1)
		sign = '-';
	else
		sign = '\0';
        /* Beware printf() rounding error! */
	printf("Bearing: %.2f, %c%d %d' %.2f\"\n", az, sign, deg, min, sec);

	exit(0);
}
Exemplo n.º 4
0
/** \brief Update the qth data by whatever method is appropriate.
 * \param qth the qth data structure to update
 * \param qth the time at which the qth is to be computed. this may be ignored by gps updates.
 */
gboolean qth_data_update(qth_t * qth, gdouble t)
{
    gboolean retval = FALSE;

#ifdef HAS_LIBGPS
    guint num_loops = 0;
#endif

    switch (qth->type)
    {
    case QTH_STATIC_TYPE:
        /* never changes */
        break;
    case QTH_GPSD_TYPE:
        if (((t - qth->gpsd_update) > 30.0 / 86400.0) && (t - qth->gpsd_connected > 30.0 / 86400.0))
        {
            /* if needed restart the gpsd interface */
            qth_data_update_stop(qth);
            qth_data_update_init(qth);
            qth->gpsd_connected = t;

        }

        if (qth->gps_data != NULL)
        {

#ifdef HAS_LIBGPS
            switch (GPSD_API_MAJOR_VERSION)
            {
            case 4:
#if GPSD_API_MAJOR_VERSION==4
                while (gps_waiting(qth->gps_data) == TRUE)
                {
                    /* this is a watchdog in case there is a problem with the gpsd code. */
                    /* if the server was up and has failed then gps_waiting in 2.92 confirmed */
                    /* will return 1 (supposedly fixed in later versions.) */
                    /* if we do not do this as a while loop, the gpsd packets can backup  */
                    /*   and no longer be in sync with the gps receiver */
                    num_loops++;
                    if (num_loops > 1000)
                    {
                        retval = FALSE;
                        break;
                    }
                    if (gps_poll(qth->gps_data) == 0)
                    {
                        /* handling packet_set inline with 
                           http://gpsd.berlios.de/client-howto.html
                         */
                        if (qth->gps_data->set & PACKET_SET)
                        {
                            if (qth->gps_data->fix.mode >= MODE_2D)
                            {
                                if (qth->lat != qth->gps_data->fix.latitude)
                                {
                                    qth->lat = qth->gps_data->fix.latitude;
                                    retval = TRUE;
                                }
                                if (qth->lon != qth->gps_data->fix.longitude)
                                {
                                    qth->lon = qth->gps_data->fix.longitude;
                                    retval = TRUE;
                                }
                            }

                            if (qth->gps_data->fix.mode == MODE_3D)
                            {
                                if (qth->alt != qth->gps_data->fix.altitude)
                                {
                                    qth->alt = qth->gps_data->fix.altitude;
                                    retval = TRUE;
                                }
                            }
                            else
                            {
                                if (qth->alt != 0)
                                {
                                    qth->alt = 0;
                                    retval = TRUE;
                                }
                            }
                        }
                    }
                }
#endif
                break;
            case 5:
#if GPSD_API_MAJOR_VERSION==5
                while (gps_waiting(qth->gps_data, 0) == 1)
                {
                    /* see comment from above */
                    /* hopefully not needed but does not hurt anything. */
                    num_loops++;
                    if (num_loops > 1000)
                    {
                        retval = FALSE;
                        break;
                    }
                    if (gps_read(qth->gps_data) == 0)
                    {
                        /* handling packet_set inline with 
                           http://gpsd.berlios.de/client-howto.html
                         */
                        if (qth->gps_data->set & PACKET_SET)
                        {
                            if (qth->gps_data->fix.mode >= MODE_2D)
                            {
                                if (qth->lat != qth->gps_data->fix.latitude)
                                {
                                    qth->lat = qth->gps_data->fix.latitude;
                                    retval = TRUE;
                                }
                                if (qth->lon != qth->gps_data->fix.longitude)
                                {
                                    qth->lon = qth->gps_data->fix.longitude;
                                    retval = TRUE;
                                }
                            }

                            if (qth->gps_data->fix.mode == MODE_3D)
                            {
                                if (qth->alt != qth->gps_data->fix.altitude)
                                {
                                    qth->alt = qth->gps_data->fix.altitude;
                                    retval = TRUE;
                                }
                            }
                            else
                            {
                                if (qth->alt != 0)
                                {
                                    qth->alt = 0;
                                    retval = TRUE;
                                }
                            }
                        }
                    }
                }
#endif
                break;
            default:
                break;
            }
#endif
            if (retval == TRUE)
            {
                qth->gpsd_update = t;
            }
        }
        break;
    default:
        break;
    }
    /* check that data is valid */
    qth_validate(qth);

    /* update qra */
    if (longlat2locator(qth->lon, qth->lat, qth->qra, 2) != RIG_OK)
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s: Could not set QRA for %s at %f, %f."),
                    __FUNCTION__, qth->name, qth->lon, qth->lat);
    }

    return retval;
}
Exemplo n.º 5
0
/** \brief Show details about a satellite pass.
 *  \param parent The parent widget.
 *  \param satname The name of the satellite.
 *  \param qth Pointer to the QTH data.
 *  \param pass The pass info.
 *  \param toplevel The toplevel window or NULL.
 *
 * This function creates a dialog window containing a notebook with three pages:
 *   1. A list showing the details of a pass
 *   2. Polar plot of the pass
 *   3. Az/El plot of the pass
 *
 * Reference to the parent widget is needed to acquire the correct top-level
 * window, otherwise simply using the main window would bring that to front
 * covering any possible module windows. This would be unfortunate in the case
 * of fullscreen modules.
 *
 */
void show_pass (const gchar *satname, qth_t *qth, pass_t *pass, GtkWidget *toplevel)
{
    GtkWidget         *dialog;      /* the dialogue window */
    GtkWidget         *notebook;    /* the notebook widet */
    GtkWidget         *list;
    GtkListStore      *liststore;
    GtkCellRenderer   *renderer;
    GtkTreeViewColumn *column;
    GtkTreeIter        item;
    GtkWidget         *swin;        /* scrolled window containing the list view */
    GtkWidget         *polar;       /* polar plot */
    GtkWidget         *azel;        /* Az/El plot */
    GtkWidget         *hbox;        /* hbox used in tab headers */
    GtkWidget         *image;       /* icon used in tab header */
    gchar             *title;
    guint              flags;
    guint              i, num;
    pass_detail_t     *detail;
    gchar             *buff;
    gint               retcode;
    gdouble            doppler;
    gdouble            delay;
    gdouble            loss;
    obs_astro_t        astro;
    gdouble            ra,dec;


    /* get columns flags */
    flags = sat_cfg_get_int (SAT_CFG_INT_PRED_SINGLE_COL);

    /* create list */
    list = gtk_tree_view_new ();
    gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (list), TRUE);

    for (i = 0; i < SINGLE_PASS_COL_NUMBER; i++) {

        renderer = gtk_cell_renderer_text_new ();
        g_object_set (G_OBJECT (renderer), "xalign", SINGLE_PASS_COL_XALIGN[i], NULL);
        column = gtk_tree_view_column_new_with_attributes (_(SINGLE_PASS_COL_TITLE[i]),
                                                           renderer,
                                                           "text", i,
                                                           NULL);
        gtk_tree_view_insert_column (GTK_TREE_VIEW (list), column, -1);

        /* only aligns the headers */
        gtk_tree_view_column_set_alignment (column, 0.5);

        /* set cell data function; allows to format data before rendering */
        check_and_set_single_cell_renderer (column, renderer, i);

        /* hide columns that have not been specified */
        if (!(flags & (1 << i))) {
            gtk_tree_view_column_set_visible (column, FALSE);
        }


    }

    /* create and fill model */
    liststore = gtk_list_store_new (SINGLE_PASS_COL_NUMBER,
                                    G_TYPE_DOUBLE,   // time
                                    G_TYPE_DOUBLE,   // az
                                    G_TYPE_DOUBLE,   // el
                                    G_TYPE_DOUBLE,   // ra
                                    G_TYPE_DOUBLE,   // dec
                                    G_TYPE_DOUBLE,   // range
                                    G_TYPE_DOUBLE,   // range rate
                                    G_TYPE_DOUBLE,   // lat
                                    G_TYPE_DOUBLE,   // lon
                                    G_TYPE_STRING,   // SSP
                                    G_TYPE_DOUBLE,   // footprint
                                    G_TYPE_DOUBLE,   // alt
                                    G_TYPE_DOUBLE,   // vel
                                    G_TYPE_DOUBLE,   // doppler
                                    G_TYPE_DOUBLE,   // loss
                                    G_TYPE_DOUBLE,   // delay
                                    G_TYPE_DOUBLE,   // ma
                                    G_TYPE_DOUBLE,   // phase
                                    G_TYPE_STRING);  // visibility

    /* add rows to list store */
    num = g_slist_length (pass->details);

    
    for (i = 0; i < num; i++) {

        detail = PASS_DETAIL(g_slist_nth_data (pass->details, i));

        gtk_list_store_append (liststore, &item);
        gtk_list_store_set (liststore, &item,
                            SINGLE_PASS_COL_TIME, detail->time,
                            SINGLE_PASS_COL_AZ, detail->az,
                            SINGLE_PASS_COL_EL, detail->el,
                            SINGLE_PASS_COL_RANGE, detail->range,
                            SINGLE_PASS_COL_RANGE_RATE, detail->range_rate,
                            SINGLE_PASS_COL_LAT, detail->lat,
                            SINGLE_PASS_COL_LON, detail->lon,
                            SINGLE_PASS_COL_FOOTPRINT, detail->footprint,
                            SINGLE_PASS_COL_ALT, detail->alt,
                            SINGLE_PASS_COL_VEL, detail->velo,
                            SINGLE_PASS_COL_MA, detail->ma,
                            SINGLE_PASS_COL_PHASE, detail->phase,
                            -1);

        /*     SINGLE_PASS_COL_RA */
        /*     SINGLE_PASS_COL_DEC */
        if (flags & (SINGLE_PASS_FLAG_RA | SINGLE_PASS_FLAG_DEC)) {

            Calc_RADec (detail->time, detail->az, detail->el, qth, &astro);

            ra = Degrees(astro.ra);
            dec = Degrees(astro.dec);

            gtk_list_store_set (liststore, &item,
                                SINGLE_PASS_COL_RA, ra,
                                SINGLE_PASS_COL_DEC, dec,
                                -1);
        }
        
        /*     SINGLE_PASS_COL_SSP */
        if (flags & SINGLE_PASS_FLAG_SSP) {
            
            buff = g_try_malloc (7);

            retcode = longlat2locator (detail->lon, detail->lat, buff, 3);
            if (retcode == RIG_OK) {
                buff[6] = '\0';
                gtk_list_store_set (liststore, &item,
                                    SINGLE_PASS_COL_SSP, buff,
                                    -1);
            }
            g_free (buff);
        }
        
        /*      SINGLE_PASS_COL_DOPPLER */
        if (flags & SINGLE_PASS_FLAG_DOPPLER) {
            doppler = -100.0e06 * (detail->range_rate / 299792.4580); // Hz
            gtk_list_store_set (liststore, &item,
                                SINGLE_PASS_COL_DOPPLER, doppler,
                                -1);                        
        }

        /*     SINGLE_PASS_COL_LOSS */
        if (flags & SINGLE_PASS_FLAG_LOSS) {
            loss = 72.4 + 20.0*log10(detail->range);               // dB
            gtk_list_store_set (liststore, &item,
                                SINGLE_PASS_COL_LOSS, loss,
                                -1);                        
        }

        /*     SINGLE_PASS_COL_DELAY */
        if (flags & SINGLE_PASS_FLAG_DELAY) {
            delay   = detail->range / 299.7924580;         // msec 
            gtk_list_store_set (liststore, &item,
                                SINGLE_PASS_COL_DELAY, delay,
                                -1);                        
        }

        /*     SINGLE_PASS_COL_VIS */
        if (flags & SINGLE_PASS_FLAG_VIS) {
            buff = g_strdup_printf ("%c", vis_to_chr (detail->vis));
            gtk_list_store_set (liststore, &item,
                                SINGLE_PASS_COL_VIS, buff,
                                -1);                        
            g_free (buff);
        }

    }

    /* connect model to tree view */
    gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (liststore));
    g_object_unref (liststore);

    /* scrolled window */
    swin = gtk_scrolled_window_new (NULL, NULL);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
                                    GTK_POLICY_NEVER,
                                    GTK_POLICY_AUTOMATIC);



    gtk_container_add (GTK_CONTAINER (swin), list);


    /* create notebook and add pages */
    notebook = gtk_notebook_new ();
    image = gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_MENU);
    hbox = gtk_hbox_new (FALSE, 0);
    gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0);
    gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("Data")), FALSE, TRUE, 5);
    gtk_widget_show_all (hbox);
    gtk_notebook_append_page (GTK_NOTEBOOK (notebook), swin, hbox);

    /* polar plot */
    polar = gtk_polar_plot_new (qth, pass);
    buff = icon_file_name ("gpredict-polar-small.png");
    image = gtk_image_new_from_file (buff);
    g_free (buff);
    hbox = gtk_hbox_new (FALSE, 0);
    gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0);
    gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("Polar")), FALSE, TRUE, 5);
    gtk_widget_show_all (hbox);
    gtk_notebook_append_page (GTK_NOTEBOOK (notebook), polar, hbox);

    /* Az/El plot */
    azel = gtk_azel_plot_new (qth, pass);
    buff = icon_file_name ("gpredict-azel-small.png");
    image = gtk_image_new_from_file (buff);
    g_free (buff);
    hbox = gtk_hbox_new (FALSE, 0);
    gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0);
    gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("Az/El")), FALSE, TRUE, 5);
    gtk_widget_show_all (hbox);
    gtk_notebook_append_page (GTK_NOTEBOOK (notebook), azel, hbox);


    /* create dialog */
    title = g_strdup_printf (_("Pass details for %s (orbit %d)"),
                             satname, pass->orbit);

    /* use NULL as parent to avoid conflict when using undocked windows
       as parents.
    */
    dialog = gtk_dialog_new_with_buttons (title,
                                          GTK_WINDOW (toplevel),
                                          GTK_DIALOG_DESTROY_WITH_PARENT,
                                          GTK_STOCK_PRINT, RESPONSE_PRINT,
                                          GTK_STOCK_SAVE, RESPONSE_SAVE,
                                          GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
                                          NULL);
    g_free (title);

    /* Make Close button default */
    gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);

    /* window icon */
    buff = icon_file_name ("gpredict-sat-list.png");
    gtk_window_set_icon_from_file (GTK_WINDOW (dialog), buff, NULL);
    g_free (buff);

    /* allow interaction with other windows */
    gtk_window_set_modal (GTK_WINDOW (dialog), FALSE);

    g_object_set_data (G_OBJECT (dialog), "sat", (gpointer) satname);
    g_object_set_data (G_OBJECT (dialog), "qth", qth);
    g_object_set_data (G_OBJECT (dialog), "pass", pass);


    g_signal_connect (dialog, "response",
                      G_CALLBACK (single_pass_response), NULL);
    g_signal_connect (dialog, "destroy",
                      G_CALLBACK (single_pass_dialog_destroy), NULL);
    g_signal_connect (dialog, "delete_event",
                      G_CALLBACK (single_pass_dialog_delete), NULL);    


    gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area(GTK_DIALOG(dialog))), notebook);

    gtk_window_set_default_size (GTK_WINDOW (dialog), -1, 300);
    gtk_widget_show_all (dialog);

}