/* $Id: engineering.c,v 1.3 2010/06/12 09:43:40 khorben Exp $ */ /* Copyright (c) 2010 Pierre Pronchery */ /* This file is part of DeforaOS Desktop Phone */ /* This program 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, version 3 of the License. * * This program 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 this program. If not, see . */ #include #include #include #include #include #include #include "Phone.h" /* Engineering */ /* private */ /* types */ typedef enum _EngineeringNeighborCellInformation { ENCI_ARFCN = 0, ENCI_C1, ENCI_C2, ENCI_RXLEV, ENCI_BSIC, ENCI_CELL_ID, ENCI_LAC, ENCI_FRAME_OFFSET, ENCI_TIME_ALIGNMENT, ENCI_CBA, ENCI_CBQ, ENCI_CELL_TYPE_IND, ENCI_RAC, ENCI_CELL_RESEL_OFFSET, ENCI_TEMP_OFFSET, ENCI_RXLEV_ACC_MIN } EngineeringNeighborCellInformation; typedef struct _Engineering { PhonePluginHelper * helper; guint source; EngineeringNeighborCellInformation enci; size_t enci_cnt; /* widgets */ GtkWidget * window; GtkToolItem * play; GtkListStore * store; GtkWidget * view; } Engineering; /* constants */ enum { COL_FREQUENCY, COL_C1, COL_C2, COL_RXLEV, COL_BSIC, COL_CELL_ID, COL_LAC, COL_FRAME_OFFSET, COL_CELL_TYPE_IND }; #define COL_LAST COL_CELL_TYPE_IND #define COL_COUNT (COL_LAST + 1) static struct { int col; char const * title; } _engineering_columns[COL_COUNT + 1] = { { COL_FREQUENCY, "Frequency" }, { COL_C1, "C1" }, { COL_C2, "C2" }, { COL_RXLEV, "RX level" }, { COL_BSIC, "Station ID" }, { COL_CELL_ID, "Cell ID" }, { COL_LAC, "Area code" }, { COL_FRAME_OFFSET, "Frame offset" }, { COL_CELL_TYPE_IND, "Cell type" }, { 0, NULL } }; /* prototypes */ static int _engineering_init(PhonePlugin * plugin); static int _engineering_destroy(PhonePlugin * plugin); /* callbacks */ static gboolean _on_engineering_closex(gpointer data); static void _on_engineering_play_toggled(gpointer data); static gboolean _on_engineering_timeout(gpointer data); static int _on_engineering_trigger_em(PhonePlugin * plugin, char const * result); /* public */ /* variables */ PhonePlugin plugin = { NULL, "Engineering", NULL, _engineering_init, _engineering_destroy, NULL, NULL, NULL }; /* private */ /* functions */ /* engineering_init */ static int _engineering_init(PhonePlugin * plugin) { Engineering * engineering; GtkWidget * vbox; GtkWidget * toolbar; GtkWidget * scrolled; size_t i; GtkCellRenderer * renderer; GtkTreeViewColumn * column; if((engineering = malloc(sizeof(*engineering))) == NULL) return error_set_code(1, "%s", strerror(errno)); plugin->priv = engineering; engineering->helper = plugin->helper; engineering->source = 0; engineering->enci = 0; engineering->enci_cnt = 0; /* widgets */ /* window */ engineering->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(engineering->window), 200, 300); #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(engineering->window), "stock_compile"); #endif gtk_window_set_title(GTK_WINDOW(engineering->window), "Engineering mode"); g_signal_connect_swapped(G_OBJECT(engineering->window), "delete-event", G_CALLBACK(_on_engineering_closex), engineering); vbox = gtk_vbox_new(FALSE, 0); /* toolbar */ toolbar = gtk_toolbar_new(); engineering->play = gtk_toggle_tool_button_new_from_stock( GTK_STOCK_MEDIA_PLAY); g_signal_connect_swapped(G_OBJECT(engineering->play), "toggled", G_CALLBACK(_on_engineering_play_toggled), engineering); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), engineering->play, -1); gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, TRUE, 0); /* store */ engineering->store = gtk_list_store_new(COL_COUNT, G_TYPE_STRING, /* COL_FREQUENCY */ G_TYPE_STRING, /* COL_C1 */ G_TYPE_STRING, /* COL_C2 */ G_TYPE_STRING, /* COL_RXLEV */ G_TYPE_STRING, /* COL_BSIC */ G_TYPE_STRING, /* COL_CELL_ID */ G_TYPE_STRING, /* COL_LAC */ G_TYPE_STRING, /* COL_FRAME_OFFSET */ G_TYPE_STRING); /* COL_CELL_TYPE_IND */ /* view */ scrolled = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); engineering->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL( engineering->store)); /* columns */ for(i = 0; _engineering_columns[i].title != NULL; i++) { renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _engineering_columns[i].title, renderer, "text", _engineering_columns[i].col, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(engineering->view), column); } gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), engineering->view); gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(engineering->window), vbox); gtk_widget_show_all(engineering->window); /* trigger */ plugin->helper->register_trigger(plugin->helper->phone, plugin, "%EM", _on_engineering_trigger_em); return 0; } /* engineering_destroy */ static int _engineering_destroy(PhonePlugin * plugin) { Engineering * engineering = plugin->priv; if(engineering->source != 0) g_source_remove(engineering->source); gtk_widget_destroy(engineering->window); free(engineering); return 0; } /* callbacks */ /* on_engineering_closex */ static gboolean _on_engineering_closex(gpointer data) { Engineering * engineering = data; gtk_widget_hide(engineering->window); /* FIXME unload the plugin */ return TRUE; } /* on_engineering_play_toggled */ static void _on_engineering_play_toggled(gpointer data) { Engineering * engineering = data; if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON( engineering->play))) engineering->source = _on_engineering_timeout(engineering); else if(engineering->source != 0) { g_source_remove(engineering->source); engineering->source = 0; } } /* on_engineering_timeout */ static gboolean _on_engineering_timeout(gpointer data) { Engineering * engineering = data; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif engineering->source = 0; engineering->helper->queue(engineering->helper->phone, "AT%EM=2,3"); return FALSE; } /* on_engineering_trigger_em */ static int _trigger_em_do(Engineering * engineering, char const * buf, GtkTreeIter * iter); static int _do_arfcn(Engineering * engineering, unsigned int arfcn, GtkTreeIter * iter); static int _do_c1(Engineering * engineering, unsigned int c1, GtkTreeIter * iter); static int _do_c2(Engineering * engineering, unsigned int c2, GtkTreeIter * iter); static int _do_rxlev(Engineering * engineering, unsigned int rxlev, GtkTreeIter * iter); static int _do_bsic(Engineering * engineering, unsigned int bsic, GtkTreeIter * iter); static int _do_cell_id(Engineering * engineering, unsigned int cell_id, GtkTreeIter * iter); static int _do_lac(Engineering * engineering, unsigned int lac, GtkTreeIter * iter); static int _do_frame_offset(Engineering * engineering, unsigned int frame_offset, GtkTreeIter * iter); static int _do_time_alignment(Engineering * engineering, unsigned int time_alignment); static int _do_cba(Engineering * engineering, unsigned int cba); static int _do_cbq(Engineering * engineering, unsigned int cbq); static int _do_cell_type_ind(Engineering * engineering, unsigned int cell_type_ind, GtkTreeIter * iter); static int _do_rac(Engineering * engineering, unsigned int rac); static int _do_cell_resel_offset(Engineering * engineering, unsigned int cell_resel_offset); static int _do_temp_offset(Engineering * engineering, unsigned int temp_offset); static int _do_rxlev_acc_min(Engineering * engineering, unsigned int rxlev_acc_min); static int _on_engineering_trigger_em(PhonePlugin * plugin, char const * result) { Engineering * engineering = plugin->priv; unsigned int u; size_t i; size_t j; gboolean valid; GtkTreeIter iter; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, result); #endif if(engineering->source == 0) engineering->source = g_timeout_add(5000, _on_engineering_timeout, engineering); if(sscanf(result, "%%EM: %u", &u) == 1) { engineering->enci = 0; engineering->enci_cnt = u; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\") => %u\n", __func__, result, u); #endif return 0; } valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL( engineering->store), &iter); for(i = 0; i < engineering->enci_cnt; i++) { if(valid == FALSE) gtk_list_store_append(engineering->store, &iter); for(j = 0; result[j] != ',' && result[j] != '\0'; j++); _trigger_em_do(engineering, result, &iter); if(result[j] == '\0') break; /* XXX report when did not parse as many cells */ result += j + 1; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL( engineering->store), &iter); } /* FIXME remove the following lines */ engineering->enci++; return 0; } static int _trigger_em_do(Engineering * engineering, char const * buf, GtkTreeIter * iter) { unsigned int u; if(sscanf(buf, "%u", &u) != 1) return 1; switch(engineering->enci) { case ENCI_ARFCN: return _do_arfcn(engineering, u, iter); case ENCI_C1: return _do_c1(engineering, u, iter); case ENCI_C2: return _do_c2(engineering, u, iter); case ENCI_RXLEV: return _do_rxlev(engineering, u, iter); case ENCI_BSIC: return _do_bsic(engineering, u, iter); case ENCI_CELL_ID: return _do_cell_id(engineering, u, iter); case ENCI_LAC: return _do_lac(engineering, u, iter); case ENCI_FRAME_OFFSET: return _do_frame_offset(engineering, u, iter); case ENCI_TIME_ALIGNMENT: return _do_time_alignment(engineering, u); case ENCI_CBA: return _do_cba(engineering, u); case ENCI_CBQ: return _do_cbq(engineering, u); case ENCI_CELL_TYPE_IND: return _do_cell_type_ind(engineering, u, iter); case ENCI_RAC: return _do_rac(engineering, u); case ENCI_CELL_RESEL_OFFSET: return _do_cell_resel_offset(engineering, u); case ENCI_TEMP_OFFSET: return _do_temp_offset(engineering, u); case ENCI_RXLEV_ACC_MIN: return _do_rxlev_acc_min(engineering, u); } return 1; } static int _do_arfcn(Engineering * engineering, unsigned int arfcn, GtkTreeIter * iter) { double freq; char buf[32]; if(arfcn == 0) freq = 0 / 0.0; else if(arfcn < 124) /* 900 MHz */ freq = 935 + (arfcn - 511) * 0.2; else if(arfcn <= 172) freq = 0 / 0.0; else if(arfcn < 252) /* 850 MHz */ freq = 869 + (arfcn - 127) * 0.2; else if(arfcn <= 511) freq = 0 / 0.0; else if(arfcn < 811) /* 1900 MHz */ freq = 1930 + (arfcn - 511) * 0.2; else if(arfcn <= 974) freq = 0 / 0.0; else if(arfcn < 1023) /* 900 MHz */ freq = 925 + (arfcn - 974) * 0.2; else freq = 0 / 0.0; snprintf(buf, sizeof(buf), "%.1lf", freq); gtk_list_store_set(engineering->store, iter, 0, buf, -1); return 0; } static int _do_c1(Engineering * engineering, unsigned int c1, GtkTreeIter * iter) { char buf[32]; snprintf(buf, sizeof(buf), "%u", c1); gtk_list_store_set(engineering->store, iter, COL_C1, buf, -1); return 0; } static int _do_c2(Engineering * engineering, unsigned int c2, GtkTreeIter * iter) { char buf[32]; snprintf(buf, sizeof(buf), "%u", c2); gtk_list_store_set(engineering->store, iter, COL_C2, buf, -1); return 0; } static int _do_rxlev(Engineering * engineering, unsigned int rxlev, GtkTreeIter * iter) { char buf[32]; /* FIXME implement properly */ snprintf(buf, sizeof(buf), "%u", (rxlev / 2) + 2); gtk_list_store_set(engineering->store, iter, COL_RXLEV, buf, -1); return 0; } static int _do_bsic(Engineering * engineering, unsigned int bsic, GtkTreeIter * iter) { char buf[32]; snprintf(buf, sizeof(buf), "%u", bsic); gtk_list_store_set(engineering->store, iter, COL_BSIC, buf, -1); return 0; } static int _do_cell_id(Engineering * engineering, unsigned int cell_id, GtkTreeIter * iter) { char buf[32]; snprintf(buf, sizeof(buf), "%u", cell_id); gtk_list_store_set(engineering->store, iter, COL_CELL_ID, buf, -1); return 0; } static int _do_lac(Engineering * engineering, unsigned int lac, GtkTreeIter * iter) { char buf[32]; snprintf(buf, sizeof(buf), "%u", lac); gtk_list_store_set(engineering->store, iter, COL_LAC, buf, -1); return 0; } static int _do_frame_offset(Engineering * engineering, unsigned int frame_offset, GtkTreeIter * iter) { char buf[32]; snprintf(buf, sizeof(buf), "%u", frame_offset); gtk_list_store_set(engineering->store, iter, COL_FRAME_OFFSET, buf, -1); return 0; } static int _do_time_alignment(Engineering * engineering, unsigned int time_alignment) { return 0; } static int _do_cba(Engineering * engineering, unsigned int cba) { return 0; } static int _do_cbq(Engineering * engineering, unsigned int cbq) { return 0; } static int _do_cell_type_ind(Engineering * engineering, unsigned int cell_type_ind, GtkTreeIter * iter) { char const * type = "NA"; switch(cell_type_ind) { case 1: type = "GSM"; break; case 2: type = "GPRS"; break; } gtk_list_store_set(engineering->store, iter, COL_CELL_TYPE_IND, type, -1); return 0; } static int _do_rac(Engineering * engineering, unsigned int rac) { return 0; } static int _do_cell_resel_offset(Engineering * engineering, unsigned int cell_resel_offset) { return 0; } static int _do_temp_offset(Engineering * engineering, unsigned int temp_offset) { return 0; } static int _do_rxlev_acc_min(Engineering * engineering, unsigned int rxlev_acc_min) { return 0; }