static GooCanvasItemModel *
        create_time_tick (GtkPolarView *pv, gdouble time, gfloat x, gfloat y)
{
    GooCanvasItemModel *item;
    time_t             t;
    gchar              buff[7];
    GtkAnchorType      anchor;
    GooCanvasItemModel *root;
    guint32            col;

    root = goo_canvas_get_root_item_model (GOO_CANVAS (pv->canvas));

    col = mod_cfg_get_int (pv->cfgdata,
                           MOD_CFG_POLAR_SECTION,
                           MOD_CFG_POLAR_TRACK_COL,
                           SAT_CFG_INT_POLAR_TRACK_COL);

    /* convert julian date to struct tm */
    t = (time - 2440587.5)*86400.;

    /* format either local time or UTC depending on check box */
    if (sat_cfg_get_bool (SAT_CFG_BOOL_USE_LOCAL_TIME))
        strftime (buff, 8, "%H:%M", localtime (&t));
    else
        strftime (buff, 8, "%H:%M", gmtime (&t));

    buff[6]='\0';

    if (x > pv->cx) {
        anchor = GTK_ANCHOR_EAST;
        x -= 5;
    }
    else {
        anchor = GTK_ANCHOR_WEST;
        x += 5;
    }

    item = goo_canvas_text_model_new (root, buff,
                                      (gdouble) x, (gdouble) y,
                                      -1, anchor,
                                      "font", "Sans 7",
                                      "fill-color-rgba", col,
                                      NULL);

    goo_canvas_item_model_lower (item, NULL);

    return item;
}
Пример #2
0
/** \brief Create and initialise widgets for the refresh rates tab.
 *
 * The widgets must be preloaded with values from config. If a config value
 * is NULL, sensible default values, eg. those from defaults.h should
 * be laoded.
 */
GtkWidget *sat_pref_refresh_create (GKeyFile *cfg)
{
     GtkWidget *table;
     GtkWidget *vbox;
     GtkWidget *label;
     gint       val;

     dirty = FALSE;
     reset = FALSE;

     /* create table */
     table = gtk_table_new (6, 3, FALSE);
     gtk_table_set_row_spacings (GTK_TABLE (table), 10);
     gtk_table_set_col_spacings (GTK_TABLE (table), 5);

     /* data refresh */
     label = gtk_label_new (_("Refresh data every"));
     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
                 GTK_FILL,
                 GTK_SHRINK,
                 0, 0);

     dataspin = gtk_spin_button_new_with_range (100, 10000, 1);
     gtk_spin_button_set_increments (GTK_SPIN_BUTTON (dataspin), 1, 100);
     gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (dataspin), TRUE);
     gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (dataspin),
                            GTK_UPDATE_IF_VALID);
     if (cfg != NULL) {
          val = mod_cfg_get_int (cfg,
                           MOD_CFG_GLOBAL_SECTION,
                           MOD_CFG_TIMEOUT_KEY,
                           SAT_CFG_INT_MODULE_TIMEOUT);
     }
     else {
          val = sat_cfg_get_int (SAT_CFG_INT_MODULE_TIMEOUT);
     }
     gtk_spin_button_set_value (GTK_SPIN_BUTTON (dataspin), val);
     g_signal_connect (G_OBJECT (dataspin), "value-changed",
                 G_CALLBACK (spin_changed_cb), NULL);
     gtk_table_attach (GTK_TABLE (table), dataspin, 1, 2, 0, 1,
                 GTK_FILL,
                 GTK_SHRINK,
                 0, 0);

     label = gtk_label_new (_("[msec]"));
     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
     gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
                 GTK_FILL | GTK_EXPAND,
                 GTK_SHRINK,
                 0, 0);

     /* separator */
     gtk_table_attach (GTK_TABLE (table),
                 gtk_hseparator_new (),
                 0, 3, 1, 2,
                 GTK_FILL | GTK_EXPAND,
                 GTK_SHRINK,
                 0, 0);


     /* List View */
     label = gtk_label_new (_("Refresh list view every"));
     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
                 GTK_FILL,
                 GTK_SHRINK,
                 0, 0);

     listspin = gtk_spin_button_new_with_range (1, 50, 1);
     gtk_spin_button_set_increments (GTK_SPIN_BUTTON (listspin), 1, 5);
     gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (listspin), TRUE);
     gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (listspin),
                            GTK_UPDATE_IF_VALID);
     if (cfg != NULL) {
          val = mod_cfg_get_int (cfg,
                           MOD_CFG_LIST_SECTION,
                           MOD_CFG_LIST_REFRESH,
                           SAT_CFG_INT_LIST_REFRESH);
     }
     else {
          val = sat_cfg_get_int (SAT_CFG_INT_LIST_REFRESH);
     }
     gtk_spin_button_set_value (GTK_SPIN_BUTTON (listspin), val);
     g_signal_connect (G_OBJECT (listspin), "value-changed",
                 G_CALLBACK (spin_changed_cb), NULL);
     gtk_table_attach (GTK_TABLE (table), listspin, 1, 2, 2, 3,
                 GTK_FILL,
                 GTK_SHRINK,
                 0, 0);

     label = gtk_label_new (_("[cycle]"));
     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
     gtk_table_attach (GTK_TABLE (table), label, 2, 3, 2, 3,
                 GTK_FILL | GTK_EXPAND,
                 GTK_SHRINK,
                 0, 0);


     /* Map View */
     label = gtk_label_new (_("Refresh map view every"));
     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
                 GTK_FILL,
                 GTK_SHRINK,
                 0, 0);

     mapspin = gtk_spin_button_new_with_range (1, 50, 1);
     gtk_spin_button_set_increments (GTK_SPIN_BUTTON (mapspin), 1, 5);
     gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (mapspin), TRUE);
     gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (mapspin),
                            GTK_UPDATE_IF_VALID);
     if (cfg != NULL) {
          val = mod_cfg_get_int (cfg,
                           MOD_CFG_MAP_SECTION,
                           MOD_CFG_MAP_REFRESH,
                           SAT_CFG_INT_MAP_REFRESH);
     }
     else {
          val = sat_cfg_get_int (SAT_CFG_INT_MAP_REFRESH);
     }
     gtk_spin_button_set_value (GTK_SPIN_BUTTON (mapspin), val);
     g_signal_connect (G_OBJECT (mapspin), "value-changed",
                 G_CALLBACK (spin_changed_cb), NULL);
     gtk_table_attach (GTK_TABLE (table), mapspin, 1, 2, 3, 4,
                 GTK_FILL,
                 GTK_SHRINK,
                 0, 0);

     label = gtk_label_new (_("[cycle]"));
     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
     gtk_table_attach (GTK_TABLE (table), label, 2, 3, 3, 4,
                 GTK_FILL | GTK_EXPAND,
                 GTK_SHRINK,
                 0, 0);


     /* Polar View */
     label = gtk_label_new (_("Refresh polar view every"));
     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5,
                 GTK_FILL,
                 GTK_SHRINK,
                 0, 0);

     polarspin = gtk_spin_button_new_with_range (1, 50, 1);
     gtk_spin_button_set_increments (GTK_SPIN_BUTTON (polarspin), 1, 5);
     gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (polarspin), TRUE);
     gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (polarspin),
                            GTK_UPDATE_IF_VALID);
     if (cfg != NULL) {
          val = mod_cfg_get_int (cfg,
                           MOD_CFG_POLAR_SECTION,
                           MOD_CFG_POLAR_REFRESH,
                           SAT_CFG_INT_POLAR_REFRESH);
     }
     else {
          val = sat_cfg_get_int (SAT_CFG_INT_POLAR_REFRESH);
     }
     gtk_spin_button_set_value (GTK_SPIN_BUTTON (polarspin), val);
     g_signal_connect (G_OBJECT (polarspin), "value-changed",
                 G_CALLBACK (spin_changed_cb), NULL);
     gtk_table_attach (GTK_TABLE (table), polarspin, 1, 2, 4, 5,
                 GTK_FILL,
                 GTK_SHRINK,
                 0, 0);

     label = gtk_label_new (_("[cycle]"));
     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
     gtk_table_attach (GTK_TABLE (table), label, 2, 3, 4, 5,
                 GTK_FILL | GTK_EXPAND,
                 GTK_SHRINK,
                 0, 0);


     /* Single-Sat View */
     label = gtk_label_new (_("Refresh single-sat view every"));
     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6,
                 GTK_FILL,
                 GTK_SHRINK,
                 0, 0);

     singlespin = gtk_spin_button_new_with_range (1, 50, 1);
     gtk_spin_button_set_increments (GTK_SPIN_BUTTON (singlespin), 1, 5);
     gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (singlespin), TRUE);
     gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (singlespin),
                            GTK_UPDATE_IF_VALID);
     if (cfg != NULL) {
          val = mod_cfg_get_int (cfg,
                           MOD_CFG_SINGLE_SAT_SECTION,
                           MOD_CFG_SINGLE_SAT_REFRESH,
                           SAT_CFG_INT_SINGLE_SAT_REFRESH);
     }
     else {
          val = sat_cfg_get_int (SAT_CFG_INT_SINGLE_SAT_REFRESH);
     }
     gtk_spin_button_set_value (GTK_SPIN_BUTTON (singlespin), val);
     g_signal_connect (G_OBJECT (singlespin), "value-changed",
                 G_CALLBACK (spin_changed_cb), NULL);
     gtk_table_attach (GTK_TABLE (table), singlespin, 1, 2, 5, 6,
                 GTK_FILL,
                 GTK_SHRINK,
                 0, 0);

     label = gtk_label_new (_("[cycle]"));
     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
     gtk_table_attach (GTK_TABLE (table), label, 2, 3, 5, 6,
                 GTK_FILL | GTK_EXPAND,
                 GTK_SHRINK,
                 0, 0);


     /* create vertical box */
     vbox = gtk_vbox_new (FALSE, 0);
     gtk_container_set_border_width (GTK_CONTAINER (vbox), 20);
     gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);

     /* create RESET button */
     create_reset_button (cfg, GTK_BOX (vbox));
     

        return vbox;
}
/** \brief Create and initialise widgets for the single pass cfg tab.
 *
 * The widgets must be preloaded with values from config. If a config value
 * is NULL, sensible default values, eg. those from defaults.h should
 * be laoded.
 */
GtkWidget *sat_pref_single_sat_create (GKeyFile *cfg)
{
    GtkWidget *table;
    GtkWidget *vbox;
    GtkWidget *label;
    guint      i;


    /* create the table */
    table = gtk_table_new ((SINGLE_SAT_FIELD_NUMBER+1)/COLUMNS + 1, COLUMNS, TRUE);
    //gtk_container_set_border_width (GTK_CONTAINER (table), 20);
    gtk_table_set_row_spacings (GTK_TABLE (table), 10);
    gtk_table_set_col_spacings (GTK_TABLE (table), 5);

    /* create header */
    label = gtk_label_new (NULL);
    gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
    gtk_label_set_markup (GTK_LABEL (label), 
                  _("<b>Visible Fields:</b>"));

    gtk_table_attach (GTK_TABLE (table), label, 0, 2, 0, 1,
              GTK_FILL,  GTK_SHRINK, 0, 0);

    /* get visible column flags */
    if (cfg != NULL) {
        flags = mod_cfg_get_int (cfg,
                     MOD_CFG_SINGLE_SAT_SECTION,
                     MOD_CFG_SINGLE_SAT_FIELDS,
                     SAT_CFG_INT_SINGLE_SAT_FIELDS);
    }
    else {
        flags = sat_cfg_get_int (SAT_CFG_INT_SINGLE_SAT_FIELDS);
    }

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

        check[i] = gtk_check_button_new_with_label (SINGLE_SAT_FIELD_TITLE[i]);
        gtk_widget_set_tooltip_text (check[i], SINGLE_SAT_FIELD_HINT[i]);

        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check[i]),
                          flags & (1 << i));

        gtk_table_attach (GTK_TABLE (table), check[i],
                  i % COLUMNS, (i % COLUMNS) + 1,
                  Y0 + i / COLUMNS, Y0 + i / COLUMNS + 1,
                  GTK_FILL,  GTK_SHRINK, 0, 0);

        g_signal_connect (check[i], "toggled",
                  G_CALLBACK (toggle_cb),
                  GUINT_TO_POINTER (i));

    }


    /* create vertical box */
    vbox = gtk_vbox_new (FALSE, 5);
    gtk_container_set_border_width (GTK_CONTAINER (vbox), 20);
    gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);

    /* create RESET button */
    create_reset_button (cfg, GTK_BOX (vbox));

    startflags = flags;
    dirty = FALSE;
    reset = FALSE;


    return vbox;
}
/** \brief Create and show ground track for a satellite.
 *  \param satmap The satellite map widget.
 *  \param sat Pointer to the satellite object.
 *  \param qth Pointer to the QTH data.
 *  \param obj the satellite object.
 *  
 * Gpredict allows the user to require the ground track for any number of orbits
 * ahead. Therfore, the resulting ground track may cross the map boundaries many
 * times, and using one single polyline for the whole ground track would look very
 * silly. To avoid this, the points will be split into several polylines.
 *
 */
void
ground_track_create (GtkSatMap *satmap, sat_t *sat, qth_t *qth, sat_map_obj_t *obj)
{
     long  this_orbit;  /* current orbit number */
     long  max_orbit;   /* target orbit number, ie. this + num - 1 */
     double         t0;          /* time when this_orbit starts */
     double         t;
     ssp_t         *this_ssp;


     sat_log_log (SAT_LOG_LEVEL_DEBUG,
                     _("%s: Creating ground track for %s"),
                 __FUNCTION__, sat->nickname);

     /* just to be safe... if empty GSList is not NULL => segfault */
     obj->track_data.latlon = NULL;

     /* get configuration parameters */
     this_orbit = sat->orbit;
     max_orbit = sat->orbit -1 + mod_cfg_get_int (satmap->cfgdata,
                                                             MOD_CFG_MAP_SECTION,
                                                             MOD_CFG_MAP_TRACK_NUM,
                                                             SAT_CFG_INT_MAP_TRACK_NUM);

                               
     sat_log_log (SAT_LOG_LEVEL_DEBUG,
                     _("%s: Start orbit: %d"),
                     __FUNCTION__, this_orbit);
     sat_log_log (SAT_LOG_LEVEL_DEBUG,
                     _("%s: End orbit %d"),
                     __FUNCTION__, max_orbit);


     /* find the time when the current orbit started */

     /* Iterate backwards in time until we reach sat->orbit < this_orbit.
        Use predict_calc from predict-tools.c as SGP/SDP driver.
        As a built-in safety, we stop iteration if the orbit crossing is
        more than 24 hours back in time.
     */
     t0 = satmap->tstamp;//get_current_daynum ();
     /* use == instead of >= as it is more robust */
     for (t = t0; (sat->orbit == this_orbit) && ((t + 1.0) > t0); t -= 0.0007) {

          predict_calc (sat, qth, t);

     }

     /* set it so that we are in the same orbit as this_orbit
        and not a different one */
     t += 2*0.0007;
     t0 = t;     
     predict_calc (sat, qth, t0);

     sat_log_log (SAT_LOG_LEVEL_DEBUG,
                     _("%s: T0: %f (%d)"),
                     __FUNCTION__, t0, sat->orbit);


     /* calculate (lat,lon) for the required orbits */
     while ((sat->orbit <= max_orbit) &&
            (sat->orbit >= this_orbit) &&
            (!decayed(sat))) {

          /* We use 30 sec time steps. If resolution is too fine, the
             line drawing routine will filter out unnecessary points
          */
          t += 0.00035;
          predict_calc (sat, qth, t);

          /* store this SSP */

          /* Note: g_slist_append() has to traverse the entire list to find the end, which
             is inefficient when adding multiple elements. Therefore, we use g_slist_prepend()
             and reverse the entire list when we are done.
          */
          this_ssp = g_try_new (ssp_t, 1);

          if (this_ssp == NULL) {
               sat_log_log (SAT_LOG_LEVEL_ERROR,
                               _("%s: MAYDAY: Insufficient memory for ground track!"),
                               __FUNCTION__);
               return;
          }
          
          this_ssp->lat = sat->ssplat;
          this_ssp->lon = sat->ssplon;
          obj->track_data.latlon = g_slist_prepend (obj->track_data.latlon, this_ssp);
          
     }
     /* log if there is a problem with the orbit calculation */
     if (sat->orbit != (max_orbit+1)) {
         sat_log_log (SAT_LOG_LEVEL_ERROR,
                      _("%s: Problem computing ground track for %s"),
                      __FUNCTION__, sat->nickname);
         return;
     }
     

     /* Reset satellite structure to eliminate glitches in single sat 
        view and other places when new ground track is layed out */
     predict_calc(sat, qth, satmap->tstamp);

     /* reverse GSList */
     obj->track_data.latlon = g_slist_reverse (obj->track_data.latlon);

     /* split points into polylines */
     create_polylines (satmap, sat, qth, obj);

     /* misc book-keeping */
     obj->track_orbit = this_orbit;

}
/** \brief Create polylines. */
static void
create_polylines (GtkSatMap *satmap, sat_t *sat, qth_t *qth, sat_map_obj_t *obj)
{
     ssp_t              *ssp,*buff;     /* map coordinates */
     double             lastx,lasty;
     GSList             *points = NULL;
     GooCanvasItemModel *root;
     GooCanvasItemModel *line;
     GooCanvasPoints    *gpoints;
     guint              start;
     guint              i,j,n,num_points;
     guint32            col;

     (void) sat; /* prevent unused parameter compiler warning */
     (void) qth; /* prevent unused parameter compiler warning */

     /* initialise parameters */
     lastx = -50.0;
     lasty = -50.0;
     start = 0;
     num_points = 0;
     n = g_slist_length (obj->track_data.latlon);
     col = mod_cfg_get_int (satmap->cfgdata,
                                 MOD_CFG_MAP_SECTION,
                                 MOD_CFG_MAP_TRACK_COL,
                                 SAT_CFG_INT_MAP_TRACK_COL);

     /* loop over each SSP */
     for (i = 0; i < n; i++) {

          buff = (ssp_t *) g_slist_nth_data (obj->track_data.latlon, i);
          ssp = g_try_new (ssp_t, 1);
          gtk_sat_map_lonlat_to_xy (satmap, buff->lon, buff->lat, &ssp->lon, &ssp->lat);

          /* if this is the first point, just add it to the list */
          if (i == start) {
               points = g_slist_prepend (points, ssp);
               lastx = ssp->lon;
               lasty = ssp->lat;
          }

          else {

               /* if SSP is on the other side of the map */
               if (ssp_wrap_detected (satmap, lastx, ssp->lon)) {

                    points = g_slist_reverse (points);
                    num_points = g_slist_length (points);

                    /* we need at least 2 points to draw a line */
                    if (num_points > 1) {

                         /* convert SSPs to GooCanvasPoints */
                         gpoints = goo_canvas_points_new (num_points);
                         for (j = 0; j < num_points; j++) {
                              buff = (ssp_t *) g_slist_nth_data (points, j);
                              gpoints->coords[2*j] = buff->lon;
                              gpoints->coords[2*j+1] = buff->lat;
                         }

                         /* create a new polyline using the current set of points */
                         root = goo_canvas_get_root_item_model (GOO_CANVAS (satmap->canvas));

                         line = goo_canvas_polyline_model_new (root, FALSE, 0,
                                                                        "points", gpoints,
                                                                        "line-width", 1.0,
                                                                        "stroke-color-rgba", col,
                                                                        "line-cap", CAIRO_LINE_CAP_SQUARE,
                                                                        "line-join", CAIRO_LINE_JOIN_MITER,
                                                                        NULL);
                         goo_canvas_points_unref (gpoints);
                         goo_canvas_item_model_lower (line, obj->marker);
                         
                         /* store line in sat object */
                         obj->track_data.lines = g_slist_append (obj->track_data.lines, line);

                    }

                    /* reset parameters and continue with a new set */
                    g_slist_foreach (points, free_ssp, NULL);
                    g_slist_free (points);
                    points = NULL;
                    start = i;
                    lastx = ssp->lon;
                    lasty = ssp->lat;
                    num_points = 0;

                    /* Add current SSP to the new list */
                    points = g_slist_prepend (points, ssp);
                    lastx = ssp->lon;
                    lasty = ssp->lat;

               }

               /* else if this SSP is separable from the previous */
               else if ((fabs (lastx - ssp->lon) > 1.0 ) || (fabs(lasty - ssp->lat)>1.0)){

                    /* add SSP to list */
                    points = g_slist_prepend (points, ssp);
                    lastx = ssp->lon;
                    lasty = ssp->lon;

               }

               /* else if  do nothing */

          }
     }

     /* create (last) line if we have at least two points */
     points = g_slist_reverse (points);
     num_points = g_slist_length (points);

     if (num_points > 1) {

          /* convert SSPs to GooCanvasPoints */
          gpoints = goo_canvas_points_new (num_points);
          for (j = 0; j < num_points; j++) {
               buff = (ssp_t *) g_slist_nth_data (points, j);
               gpoints->coords[2*j] = buff->lon;
               gpoints->coords[2*j+1] = buff->lat;
          }

          /* create a new polyline using the current set of points */
          root = goo_canvas_get_root_item_model (GOO_CANVAS (satmap->canvas));

          line = goo_canvas_polyline_model_new (root, FALSE, 0,
                                                         "points", gpoints,
                                                         "line-width", 1.0,
                                                         "stroke-color-rgba", col,
                                                         "line-cap", CAIRO_LINE_CAP_SQUARE,
                                                         "line-join", CAIRO_LINE_JOIN_MITER,
                                                         NULL);
          goo_canvas_points_unref (gpoints);
          goo_canvas_item_model_lower (line, obj->marker);

          /* store line in sat object */
          obj->track_data.lines = g_slist_append (obj->track_data.lines, line);

          /* reset parameters and continue with a new set */
          g_slist_foreach (points, free_ssp, NULL);
          g_slist_free (points);

     }
}
/** \brief Manage toggling of Ground Track.
 *  \param item The menu item that was toggled.
 *  \param data Pointer to the GtkPolarView structure.
 *
 */
static void
        track_toggled (GtkCheckMenuItem *item, gpointer data)
{
    GtkPolarView       *pv = GTK_POLAR_VIEW (data);
    sat_obj_t          *obj = NULL;
    sat_t              *sat;
    qth_t              *qth;
    gint               idx,i;
    GooCanvasItemModel *root;
    pass_detail_t      *detail;
    guint              num;
    GooCanvasPoints    *points;
    gfloat             x,y;
    guint32            col;
    guint              tres,ttidx;


    /* get satellite object */
    obj = SAT_OBJ(g_object_get_data (G_OBJECT (item), "obj"));
    sat = SAT(g_object_get_data (G_OBJECT (item), "sat"));
    qth = (qth_t *)(g_object_get_data (G_OBJECT (item), "qth"));

    if (obj == NULL) {
        sat_log_log (SAT_LOG_LEVEL_BUG,
                     _("%s:%d: Failed to get satellite object."),
                     __FILE__, __LINE__);
        return;
    }

    /* toggle flag */
    obj->showtrack = !obj->showtrack;
    gtk_check_menu_item_set_active (item, obj->showtrack);

    root = goo_canvas_get_root_item_model (GOO_CANVAS (pv->canvas));

    if (obj->showtrack) {
        /* add sky track */

        /* create points */
        num = g_slist_length (obj->pass->details);
        if (num == 0) {
            sat_log_log (SAT_LOG_LEVEL_BUG,
                         _("%s:%d: Pass has no details."),
                         __FILE__, __LINE__);
            return;
        }

        /* time resolution for time ticks; we need
                   3 additional points to AOS and LOS ticks.
                */
        tres = (num-2) / (TRACK_TICK_NUM-1);

        points = goo_canvas_points_new (num);

        /* first point should be (aos_az,0.0) */
        azel_to_xy (pv, obj->pass->aos_az, 0.0, &x, &y);
        points->coords[0] = (double) x;
        points->coords[1] = (double) y;
        obj->trtick[0] = create_time_tick (pv, obj->pass->aos, x, y);

        ttidx = 1;

        for (i = 1; i < num-1; i++) {
            detail = PASS_DETAIL(g_slist_nth_data (obj->pass->details, i));
            if (detail->el >=0.0)
                azel_to_xy (pv, detail->az, detail->el, &x, &y);
            points->coords[2*i] = (double) x;
            points->coords[2*i+1] = (double) y;

            if (!(i % tres)) {
                /* create a time tick */
                if (ttidx<TRACK_TICK_NUM)
                    obj->trtick[ttidx] = create_time_tick (pv, detail->time, x, y);
                ttidx++;
            }
        }

        /* last point should be (los_az, 0.0)  */
        azel_to_xy (pv, obj->pass->los_az, 0.0, &x, &y);
        points->coords[2*(num-1)] = (double) x;
        points->coords[2*(num-1)+1] = (double) y;

        /* create poly-line */
        col = mod_cfg_get_int (pv->cfgdata,
                               MOD_CFG_POLAR_SECTION,
                               MOD_CFG_POLAR_TRACK_COL,
                               SAT_CFG_INT_POLAR_TRACK_COL);

        obj->track = goo_canvas_polyline_model_new (root, FALSE, 0,
                                                    "points", points,
                                                    "line-width", 1.0,
                                                    "stroke-color-rgba", col,
                                                    "line-cap", CAIRO_LINE_CAP_SQUARE,
                                                    "line-join", CAIRO_LINE_JOIN_MITER,
                                                    NULL);
        goo_canvas_points_unref (points);

        /* put track on the bottom of the sack */
        goo_canvas_item_model_lower (obj->track, NULL);

    }
    else {
        /* delete sky track */
        idx = goo_canvas_item_model_find_child (root, obj->track);

        if (idx != -1) {
            goo_canvas_item_model_remove_child (root, idx);
        }

        for (i = 0; i < TRACK_TICK_NUM; i++) {
            idx = goo_canvas_item_model_find_child (root, obj->trtick[i]);

            if (idx != -1) {
                goo_canvas_item_model_remove_child (root, idx);
            }
        }
    }

}