/** \brief Read fresh TLE data into hash table. * \param dir The directory to read from. * \param fnam The name of the file to read from. * \param fresh_data Hash table where the data should be stored. * \return The number of satellites successfully read. * * This function will read fresh TLE data from local files into memory. * If there is a saetllite category (.cat file) with the same name as the * input file it will also update the satellites in that category. */ static gint read_fresh_tle (const gchar *dir, const gchar *fnam, GHashTable *data) { new_tle_t *ntle; tle_t tle; gchar *path; gchar tle_str[3][80]; gchar tle_working[3][80]; gchar linetmp[80]; guint linesneeded = 3; gchar catstr[6]; gchar idstr[7]="\0\0\0\0\0\0\0",idyearstr[3]; gchar *b; FILE *fp; gint retcode = 0; guint catnr,i,idyear; guint *key = NULL; /* category sync related */ gchar *catname, *catpath, *buff, **buffv; FILE *catfile; gchar category[80]; gboolean catsync = FALSE; /* whether .cat file should be synced */ /* Normal cases to check 1. 3 line tle file as in amatuer.txt from celestrak 2. 2 line tle file as in .... from celestrak corner cases to check 1. 3 line tle with something at the end. (nasa.all from amsat) 2. 2 line tle with only one in the file 3. 2 line tle file reading the last one. */ path = g_strconcat (dir, G_DIR_SEPARATOR_S, fnam, NULL); fp = g_fopen (path, "r"); if (fp != NULL) { /* Prepare .cat file for sync while we read data */ buffv = g_strsplit (fnam, ".", 0); catname = g_strconcat (buffv[0], ".cat", NULL); g_strfreev (buffv); catpath = sat_file_name (catname); g_free (catname); /* read category name for catfile */ catfile = g_fopen (catpath, "r"); if (catfile!=NULL) { b = fgets (category, 80, catfile); if (b == NULL) { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s:%s: There is no category in %s"), __FILE__, __FUNCTION__, catpath); } fclose (catfile); catsync = TRUE; } else { /* There is no category with this name (could be update from custom file) */ sat_log_log (SAT_LOG_LEVEL_INFO, _("%s:%s: There is no category called %s"), __FILE__, __FUNCTION__, fnam); } /* reopen a new catfile and write category name */ if (catsync) { catfile = g_fopen (catpath, "w"); if (catfile != NULL) { fputs (category, catfile); } else { catsync = FALSE; sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s:%s: Could not reopen .cat file while reading TLE from %s"), __FILE__, __FUNCTION__, fnam); } /* .cat file now contains the category name; satellite catnums will be added during update in the while loop */ } /* set b to non-null as a flag */ b = path; /* read lines from tle file */ while (fgets (linetmp, 80, fp)) { /*read in the number of lines needed to potentially get to a new tle*/ switch (linesneeded) { case 3: strncpy(tle_working[0],linetmp,80); /* b is being used a flag here if b==NULL then we only have one line read in and there is no way we have a full tle as there is only one line in the buffer. A TLE must be two or three lines. */ b = fgets (tle_working[1], 80, fp); if (b==NULL) { /* make sure there is no junk in tle_working[1] */ tle_working[1][0]='\0'; break; } /* make sure there is no junk in tle_working[2] */ if (fgets (tle_working[2], 80, fp)==NULL) { tle_working[2][0]='\0'; } break; case 2: strncpy(tle_working[0],tle_working[2],80); strncpy(tle_working[1],linetmp,80); /* make sure there is no junk in tle_working[2] */ if (fgets (tle_working[2], 80, fp)==NULL) { tle_working[2][0]='\0'; } break; case 1: strncpy(tle_working[0],tle_working[1],80); strncpy(tle_working[1],tle_working[2],80); strncpy(tle_working[2],linetmp,80); break; default: sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s:%s: Something wrote linesneeded to an illegal value %d"), __FILE__, __FUNCTION__, linesneeded); break; } /* b can only be null if there is only one line in the buffer*/ /* a tle must be two or three */ if (b == NULL) { break; } /* remove leading and trailing whitespace to be more forgiving */ g_strstrip(tle_working[0]); g_strstrip(tle_working[1]); g_strstrip(tle_working[2]); /* there are three possibilities at this point */ /* first is that line 0 is a name and normal text for three line element and that lines 1 and 2 are the corresponding tle */ /* second is that line 0 and line 1 are a tle for a bare tle */ /* third is that neither of these is true and we are consuming either text at the top of the file or a text file that happens to be in the update directory */ if ((tle_working[1][0] == '1') && (tle_working[2][0] == '2') && Checksum_Good(tle_working[1]) && Checksum_Good(tle_working[2])) { sat_log_log (SAT_LOG_LEVEL_DEBUG, _("%s:%s: Processing a three line TLE"), __FILE__, __FUNCTION__); /* it appears that the first line may be a name followed by a tle */ strncpy(tle_str[0],tle_working[0],80); strncpy(tle_str[1],tle_working[1],80); strncpy(tle_str[2],tle_working[2],80); /* we consumed three lines so we need three lines */ linesneeded = 3; } else if ((tle_working[0][0] == '1') && (tle_working[1][0] == '2') && Checksum_Good(tle_working[0]) && Checksum_Good(tle_working[1])) { sat_log_log (SAT_LOG_LEVEL_DEBUG, _("%s:%s: Processing a bare two line TLE"), __FILE__, __FUNCTION__); /* first line appears to belong to the start of bare TLE */ /* put in a dummy name of form yyyy-nnaa base on international id */ /* this special form will be overwritten if a three line tle ever has another name */ strncpy(idstr,&tle_working[0][11],6); g_strstrip(idstr); strncpy(idyearstr,&tle_working[0][9],2); idstr[6]= '\0'; idyearstr[2]= '\0'; idyear = g_ascii_strtod(idyearstr,NULL); /* there is a two digit year field that started around sputnik */ if (idyear >= 57) idyear += 1900; else idyear += 2000; snprintf(tle_str[0],79,"%d-%s",idyear,idstr); strncpy(tle_str[1],tle_working[0],80); strncpy(tle_str[2],tle_working[1],80); /* we consumed two lines so we need two lines */ linesneeded = 2; } else { /* we appear to have junk read another line in and do nothing else */ linesneeded = 1; /* skip back to beginning of loop */ continue; } tle_str[1][69] = '\0'; tle_str[2][69] = '\0'; /* copy catnum and convert to integer */ for (i = 2; i < 7; i++) { catstr[i-2] = tle_str[1][i]; } catstr[5] = '\0'; catnr = (guint) g_ascii_strtod (catstr, NULL); if (Get_Next_Tle_Set (tle_str, &tle) != 1) { /* TLE data not good */ sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s:%s: Invalid data for %d"), __FILE__, __FUNCTION__, catnr); } else { if (catsync) { /* store catalog number in catfile */ buff = g_strdup_printf ("%d\n", catnr); fputs (buff, catfile); g_free (buff); } /* add data to hash table */ key = g_try_new0 (guint, 1); *key = catnr; ntle = g_hash_table_lookup (data, key); /* check if satellite already in hash table */ if ( ntle == NULL) { /* create new_tle structure */ ntle = g_try_new (new_tle_t, 1); ntle->catnum = catnr; ntle->epoch = tle.epoch; ntle->status = tle.status; ntle->satname = g_strdup (tle.sat_name); ntle->line1 = g_strdup (tle_str[1]); ntle->line2 = g_strdup (tle_str[2]); ntle->srcfile = g_strdup (fnam); ntle->isnew = TRUE; /* flag will be reset when using data */ g_hash_table_insert (data, key, ntle); retcode++; } else { /* satellite is already in hash */ /* apply various merge routines */ /* time merge */ if (ntle->epoch == tle.epoch) { /* if satellite epoch has the same time, merge status as appropriate */ if (ntle->status != tle.status) { /* log if there is something funny about the data coming in */ sat_log_log (SAT_LOG_LEVEL_WARN, _("%s:%s: Two different statuses for %d (%s) at the same time."), __FILE__, __FUNCTION__, ntle->catnum, ntle->satname); if ( tle.status != OP_STAT_UNKNOWN ) ntle->status = tle.status; } } else if ( ntle->epoch < tle.epoch ) { /* if the satellite in the hash is older than the one just loaded, copy the values over. */ ntle->catnum = catnr; ntle->epoch = tle.epoch; ntle->status = tle.status; g_free (ntle->line1); ntle->line1 = g_strdup (tle_str[1]); g_free (ntle->line2); ntle->line2 = g_strdup (tle_str[2]); g_free (ntle->srcfile); ntle->srcfile = g_strdup (fnam); ntle->isnew = TRUE; /* flag will be reset when using data */ } /* merge based on name */ if (is_computer_generated_name (ntle->satname) && !is_computer_generated_name(tle_str[0])) { g_free (ntle->satname); ntle->satname = g_strdup (tle.sat_name); } /* free the key since we do not commit it to the cache */ g_free (key); } } } if (catsync) { /* close category file */ fclose (catfile); } g_free (catpath); /* close input TLE file */ fclose (fp); } else { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s:%s: Failed to open %s"), __FILE__, __FUNCTION__, path); } g_free (path); return retcode; }
/** \brief Check if satellite is new, if so, add it to local database */ static void check_and_add_sat (gpointer key, gpointer value, gpointer user_data) { new_tle_t *ntle = (new_tle_t *) value; guint *num = user_data; GKeyFile *satdata; GIOChannel *satfile; gchar *cfgstr, *cfgfile; GError *err = NULL; (void) key; /* avoid unused parameter compiler warning */ /* check if sat is new */ if (ntle->isnew) { /* create config data */ satdata = g_key_file_new (); /* store data */ g_key_file_set_string (satdata, "Satellite", "VERSION", "1.1"); g_key_file_set_string (satdata, "Satellite", "NAME", ntle->satname); g_key_file_set_string (satdata, "Satellite", "NICKNAME", ntle->satname); g_key_file_set_string (satdata, "Satellite", "TLE1", ntle->line1); g_key_file_set_string (satdata, "Satellite", "TLE2", ntle->line2); g_key_file_set_integer (satdata, "Satellite", "STATUS", ntle->status); /* create an I/O channel and store data */ cfgfile = sat_file_name_from_catnum (ntle->catnum); if (!gpredict_save_key_file (satdata, cfgfile)){ *num += 1; } /* clean up memory */ g_free (cfgfile); g_key_file_free (satdata); /**** FIXME: NEED TO CREATE COPY of cache */ /* finally, new satellite must be added to proper category */ gchar *catfile; gchar **buff; gint statretval; struct stat temp; buff = g_strsplit (ntle->srcfile, ".", 0); cfgfile = g_strconcat (buff[0], ".cat", NULL); catfile = sat_file_name (cfgfile); /* call stat on file before opening it incase file does not exist and we need to add a group name. */ statretval = stat (catfile,&temp); /* g_io_channel */ satfile = g_io_channel_new_file (catfile, "a", &err); if (err != NULL) { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s: Could not open category file file %s (%s)."), __FUNCTION__, cfgfile, err->message); g_clear_error (&err); } else { if (statretval == -1) { /* file did not exist before creating handle */ /* use the file name as the group description */ cfgstr = g_strdup_printf ("%s\n", buff[0]); g_io_channel_write_chars (satfile, cfgstr, -1, NULL, &err); g_free (cfgstr); } cfgstr = g_strdup_printf ("%d\n", ntle->catnum); g_io_channel_write_chars (satfile, cfgstr, -1, NULL, &err); g_io_channel_shutdown (satfile, TRUE, NULL); g_io_channel_unref (satfile); g_free (cfgstr); if (err != NULL) { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s: Error adding %d to %s (%s)."), __FUNCTION__, ntle->catnum, cfgfile, err->message); g_clear_error (&err); } else { sat_log_log (SAT_LOG_LEVEL_INFO, _("%s: Added satellite %d to %s."), __FUNCTION__, ntle->catnum, cfgfile); } } g_free (catfile); g_free (cfgfile); g_strfreev (buff); } }
/** \brief Update TLE files from network. * \param silent TRUE if function should execute without graphical status indicator. * \param progress Pointer to a GtkProgressBar progress indicator (can be NULL) * \param label1 GtkLabel for activity string. * \param label2 GtkLabel for statistics string. */ void tle_update_from_network (gboolean silent, GtkWidget *progress, GtkWidget *label1, GtkWidget *label2) { static GMutex tle_in_progress; gchar *server; gchar *proxy = NULL; gchar *files_tmp; gchar **files; guint numfiles,i; gchar *curfile; gchar *locfile; gchar *userconfdir; CURL *curl; CURLcode res; gdouble fraction,start=0; FILE *outfile; GDir *dir; gchar *cache; const gchar *fname; gchar *text; GError *err = NULL; guint success = 0; /* no. of successfull downloads */ /* bail out if we are already in an update process */ if (g_mutex_trylock(&tle_in_progress) == FALSE) { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s: A TLE update process is already running. Aborting."), __FUNCTION__); return; } /* get server, proxy, and list of files */ server = sat_cfg_get_str (SAT_CFG_STR_TLE_SERVER); proxy = sat_cfg_get_str (SAT_CFG_STR_TLE_PROXY); files_tmp = sat_cfg_get_str (SAT_CFG_STR_TLE_FILES); files = g_strsplit (files_tmp, ";", 0); numfiles = g_strv_length (files); if (numfiles < 1) { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s: No files to fetch from network."), __FUNCTION__); /* set activity string, so user knows why nothing happens */ if (!silent && (label1 != NULL)) { gtk_label_set_text (GTK_LABEL (label1), _("No files to fetch from network")); } } else { /* initialise progress bar */ if (!silent && (progress != NULL)) start = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR (progress)); /* initialise curl */ curl = curl_easy_init(); if (proxy != NULL) curl_easy_setopt (curl, CURLOPT_PROXY, proxy); curl_easy_setopt (curl, CURLOPT_USERAGENT, "gpredict/curl"); curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 10); /* get files */ for (i = 0; i < numfiles; i++) { /* set URL */ curfile = g_strconcat (server, files[i], NULL); curl_easy_setopt (curl, CURLOPT_URL, curfile); /* set activity message */ if (!silent && (label1 != NULL)) { text = g_strdup_printf (_("Fetching %s"), files[i]); gtk_label_set_text (GTK_LABEL (label1), text); g_free (text); /* Force the drawing queue to be processed otherwise there will not be any visual feedback, ie. frozen GUI - see Gtk+ FAQ http://www.gtk.org/faq/#AEN602 */ while (g_main_context_iteration (NULL, FALSE)); } /* create local cache file */ userconfdir = get_user_conf_dir (); locfile = g_strconcat (userconfdir, G_DIR_SEPARATOR_S, "satdata", G_DIR_SEPARATOR_S, "cache", G_DIR_SEPARATOR_S, files[i], NULL); outfile = g_fopen (locfile, "wb"); if (outfile != NULL) { curl_easy_setopt (curl, CURLOPT_WRITEDATA, outfile); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, my_write_func); /* get file */ res = curl_easy_perform (curl); if (res != CURLE_OK) { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s: Error fetching %s (%s)"), __FUNCTION__, curfile, curl_easy_strerror (res)); } else { sat_log_log (SAT_LOG_LEVEL_INFO, _("%s: Successfully fetched %s"), __FUNCTION__, curfile); success++; } fclose (outfile); } else { sat_log_log (SAT_LOG_LEVEL_INFO, _("%s: Failed to open %s preventing update"), __FUNCTION__, locfile); } /* update progress indicator */ if (!silent && (progress != NULL)) { /* complete download corresponds to 50% */ fraction = start + (0.5-start) * i / (1.0 * numfiles); gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress), fraction); /* Force the drawing queue to be processed otherwise there will not be any visual feedback, ie. frozen GUI - see Gtk+ FAQ http://www.gtk.org/faq/#AEN602 */ while (g_main_context_iteration (NULL, FALSE)); } g_free (userconfdir); g_free (curfile); g_free (locfile); } curl_easy_cleanup (curl); /* continue update if we have fetched at least one file */ if (success > 0) { sat_log_log (SAT_LOG_LEVEL_INFO, _("%s: Fetched %d files from network; updating..."), __FUNCTION__, success); /* call update_from_files */ cache = sat_file_name ("cache"); tle_update_from_files (cache, NULL, silent, progress, label1, label2); g_free (cache); } else { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s: Could not fetch any new TLE files from network; aborting..."), __FUNCTION__); } } /* clear cache and memory */ g_free (server); g_strfreev (files); g_free (files_tmp); if (proxy != NULL) g_free (proxy); /* open cache */ cache = sat_file_name ("cache"); dir = g_dir_open (cache, 0, &err); if (err != NULL) { /* send an error message */ sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s: Error opening %s (%s)"), __FUNCTION__, dir, err->message); g_clear_error (&err); } else { /* delete files in cache one by one */ while ((fname = g_dir_read_name (dir)) != NULL) { locfile = g_strconcat (cache, G_DIR_SEPARATOR_S, fname, NULL); g_remove (locfile); g_free (locfile); } /* close cache */ g_dir_close (dir); } g_free (cache); g_mutex_unlock(&tle_in_progress); }
/** * Update TRSP files from network. * * @param silent TRUE if function should execute without graphical status indicator. * @param progress Pointer to a GtkProgressBar progress indicator (can be NULL) * @param label1 GtkLabel for activity string. * @param label2 GtkLabel for statistics string. */ void trsp_update_from_network(gboolean silent, GtkWidget * progress, GtkWidget * label1, GtkWidget * label2) { static GMutex trsp_in_progress; gchar *server; gchar *proxy = NULL; gchar *freq_file; gchar *file_url; gchar *locfile_trsp; gchar *userconfdir; CURL *curl; CURLcode res; gdouble fraction; FILE *outfile; gchar *cache; gchar *text; guint success = 0; /* no. of successfull downloads */ /* bail out if we are already in an update process */ if (g_mutex_trylock(&trsp_in_progress) == FALSE) { sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s: A frequency update process is already running"), __func__); return; } /* get list with modes */ modes_update_from_network(); /* get server, proxy, and list of files */ //server = sprintf("%stransmitters/?format=json", sat_cfg_get_str(SAT_CFG_STR_TRSP_SERVER)); server = sat_cfg_get_str(SAT_CFG_STR_TRSP_SERVER); proxy = sat_cfg_get_str(SAT_CFG_STR_TRSP_PROXY); freq_file = sat_cfg_get_str(SAT_CFG_STR_TRSP_FREQ_FILE); /* initialise curl */ curl = curl_easy_init(); if (proxy != NULL) curl_easy_setopt(curl, CURLOPT_PROXY, proxy); curl_easy_setopt(curl, CURLOPT_USERAGENT, "gpredict/curl"); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); /* get files */ /* set URL */ file_url = g_strconcat(server, freq_file, NULL); curl_easy_setopt(curl, CURLOPT_URL, file_url); sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: Ready to fetch transponder list from %s"), __func__, file_url); /* set activity message */ if (!silent && (label1 != NULL)) { text = g_strdup_printf(_("Fetching %s"), "transmitters.json"); gtk_label_set_text(GTK_LABEL(label1), text); g_free(text); /* Force the drawing queue to be processed otherwise there will not be any visual feedback, ie. frozen GUI - see Gtk+ FAQ http://www.gtk.org/faq/#AEN602 */ while (g_main_context_iteration(NULL, FALSE)); } /* create local cache file */ userconfdir = get_user_conf_dir(); locfile_trsp = g_strconcat(userconfdir, G_DIR_SEPARATOR_S, "trsp", G_DIR_SEPARATOR_S, "transmitters.json", NULL); sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: File to open %s "), __func__, locfile_trsp); outfile = g_fopen(locfile_trsp, "wb"); if (outfile != NULL) { curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_write_func); /* get file */ res = curl_easy_perform(curl); if (res != CURLE_OK) { sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s: Error fetching %s (%s)"), __func__, file_url, curl_easy_strerror(res)); } else { sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: Successfully fetched %s"), __func__, file_url); success++; } fclose(outfile); } else { sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: Failed to open %s preventing update"), __func__, locfile_trsp); } /* update progress indicator */ if (!silent && (progress != NULL)) { /* complete download corresponds to 50% */ fraction = 1; gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction); /* Force the drawing queue to be processed otherwise there will not be any visual feedback, ie. frozen GUI - see Gtk+ FAQ http://www.gtk.org/faq/#AEN602 */ while (g_main_context_iteration(NULL, FALSE)); } g_free(userconfdir); g_free(file_url); g_free(server); g_free(freq_file); g_free(proxy); curl_easy_cleanup(curl); /* continue update if we have fetched at least one file */ if (success > 0) { sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: Fetched %d files from network; updating..."), __func__, success); /* call update_from_files */ cache = sat_file_name("cache"); trsp_update_files(locfile_trsp); g_free(cache); } else { sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s: Could not fetch frequency files from network"), __func__); } g_mutex_unlock(&trsp_in_progress); }