diff options
Diffstat (limited to 'src/engines')
-rw-r--r-- | src/engines/Makefile.am | 20 | ||||
-rw-r--r-- | src/engines/engine.c | 178 | ||||
-rw-r--r-- | src/engines/engine.h | 89 | ||||
-rw-r--r-- | src/engines/gnucap.c | 767 | ||||
-rw-r--r-- | src/engines/gnucap.h | 59 | ||||
-rw-r--r-- | src/engines/netlist.c | 689 | ||||
-rw-r--r-- | src/engines/netlist.h | 71 | ||||
-rw-r--r-- | src/engines/ngspice.c | 633 | ||||
-rw-r--r-- | src/engines/ngspice.h | 59 |
9 files changed, 2565 insertions, 0 deletions
diff --git a/src/engines/Makefile.am b/src/engines/Makefile.am new file mode 100644 index 0000000..ccdf011 --- /dev/null +++ b/src/engines/Makefile.am @@ -0,0 +1,20 @@ +oreganodir = $(datadir)/oregano +INCLUDES = \ + $(OREGANO_CFLAGS) -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/model -I$(top_srcdir)/src/sheet \ + -DOREGANO_XMLDIR=\""$(oreganodir)/xml"\" \ + -DOREGANO_LIBRARYDIR=\""$(oreganodir)/libraries"\" \ + -DOREGANO_MODELDIR=\""$(oreganodir)/models"\" + +noinst_LIBRARIES = libengines.a +libengines_a_SOURCES = \ + engine.c \ + engine.h \ + gnucap.c \ + gnucap.h \ + netlist.c \ + netlist.h \ + ngspice.c \ + ngspice.h + +libengines_a_LIBADD = libengines.a diff --git a/src/engines/engine.c b/src/engines/engine.c new file mode 100644 index 0000000..9958608 --- /dev/null +++ b/src/engines/engine.c @@ -0,0 +1,178 @@ +/* + * engine.c + * + * Authors: + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * + * Web page: http://oregano.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * 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; either version 2 of the + * License, or (at your option) any later version. + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "engine.h" +#include "gnucap.h" +#include "ngspice.h" + +static gchar *analysis_names[] = { + N_("Operating Point"), + N_("Transient Analysis"), + N_("DC transfer characteristic"), + N_("AC Analysis"), + N_("Transfer Function"), + N_("Distortion Analysis"), + N_("Noise Analysis"), + N_("Pole-Zero Analysis"), + N_("Sensitivity Analysis"), + N_("Unknown Analysis"), + NULL +}; + +/* Signals */ +enum { + DONE, + ABORTED, + LAST_SIGNAL +}; +static guint engine_signals[LAST_SIGNAL] = { 0 }; + +static void +oregano_engine_base_init (gpointer g_class) +{ + static gboolean initialized = FALSE; + + if (!initialized) { + /* create interface signals here. */ + engine_signals[DONE] = g_signal_new ("done", G_TYPE_FROM_CLASS (g_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (OreganoEngineClass, done), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + engine_signals[ABORTED] = g_signal_new ("aborted", G_TYPE_FROM_CLASS (g_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (OreganoEngineClass, abort), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + initialized = TRUE; + } +} + +GType +oregano_engine_get_type (void) +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof (OreganoEngineClass), + oregano_engine_base_init, /* base_init */ + NULL, /* base_finalize */ + NULL, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL /* instance_init */ + }; + type = g_type_register_static (G_TYPE_INTERFACE, "OreganoEngine", &info, 0); + } + return type; +} + +void +oregano_engine_start (OreganoEngine *self) +{ + OREGANO_ENGINE_GET_CLASS (self)->start (self); +} + +void +oregano_engine_stop (OreganoEngine *self) +{ + OREGANO_ENGINE_GET_CLASS (self)->stop (self); +} + +gboolean +oregano_engine_has_warnings (OreganoEngine *self) +{ + return OREGANO_ENGINE_GET_CLASS (self)->has_warnings (self); +} + +gboolean +oregano_engine_is_available (OreganoEngine *self) +{ + return OREGANO_ENGINE_GET_CLASS (self)->is_available (self); +} + +void +oregano_engine_get_progress (OreganoEngine *self, double *p) +{ + OREGANO_ENGINE_GET_CLASS (self)->progress (self, p); +} + +void +oregano_engine_generate_netlist (OreganoEngine *self, const gchar *file, GError **error) +{ + OREGANO_ENGINE_GET_CLASS (self)->get_netlist (self, file, error); +} + +GList* +oregano_engine_get_results (OreganoEngine *self) +{ + return OREGANO_ENGINE_GET_CLASS (self)->get_results (self); +} + +gchar* +oregano_engine_get_current_operation (OreganoEngine *self) +{ + return OREGANO_ENGINE_GET_CLASS (self)->get_operation (self); +} +OreganoEngine* +oregano_engine_factory_create_engine (gint type, Schematic *sm) +{ + OreganoEngine *engine; + + switch (type) { + case OREGANO_ENGINE_GNUCAP: + engine = oregano_gnucap_new (sm); + break; + case OREGANO_ENGINE_NGSPICE: + engine = oregano_ngspice_new (sm); + break; + default: + engine = NULL; + } + + return engine; +} + +gchar * +oregano_engine_get_analysis_name (SimulationData *sdat) +{ + if (sdat == NULL) + return g_strdup (_(analysis_names[ANALYSIS_UNKNOWN])); + + return g_strdup (_(analysis_names[sdat->type])); +} diff --git a/src/engines/engine.h b/src/engines/engine.h new file mode 100644 index 0000000..b5e6a9f --- /dev/null +++ b/src/engines/engine.h @@ -0,0 +1,89 @@ +/* + * engine.h + * + * Authors: + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * + * Web page: http://oregano.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * 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; either version 2 of the + * License, or (at your option) any later version. + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __ENGINE_H +#define __ENGINE_H 1 + +#include <gtk/gtk.h> +#include "sim-settings.h" +#include "schematic.h" +#include "simulation.h" + +#define OREGANO_TYPE_ENGINE (oregano_engine_get_type ()) +#define OREGANO_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), OREGANO_TYPE_ENGINE, OreganoEngine)) +#define OREGANO_ENGINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), OREGANO_TYPE_ENGINE, OreganoEngineClass)) +#define OREGANO_IS_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), OREGANO_TYPE_ENGINE)) +#define OREGANO_IS_ENGINE_CLASS(klass) (G_TYPE_CLASS_TYPE((klass), OREGANO_TYPE_ENGINE, OreganoEngineClass)) +#define OREGANO_ENGINE_GET_CLASS(klass) (G_TYPE_INSTANCE_GET_INTERFACE((klass), OREGANO_TYPE_ENGINE, OreganoEngineClass)) + +typedef struct _OreganoEngine OreganoEngine; +typedef struct _OreganoEngineClass OreganoEngineClass; + +struct _OreganoEngineClass { + GTypeInterface parent; + + void (*start) (OreganoEngine *engine); + void (*stop) (OreganoEngine *engine); + void (*progress) (OreganoEngine *engine, double *p); + void (*get_netlist) (OreganoEngine *engine, const gchar *sm, GError **error); + GList* (*get_results) (OreganoEngine *engine); + gchar* (*get_operation) (OreganoEngine *engine); + gboolean (*has_warnings) (OreganoEngine *engine); + gboolean (*is_available) (OreganoEngine *engine); + + /* Signals */ + void (*done) (); + void (*abort) (); +}; + +/* Engines IDs */ +enum { + OREGANO_ENGINE_GNUCAP=0, + OREGANO_ENGINE_NGSPICE, + OREGANO_ENGINE_COUNT +}; + +/* Engines Titles */ +static const gchar* +engines[] = { + "GnuCap", + "NgSpice" +}; + +OreganoEngine *oregano_engine_factory_create_engine (gint type, Schematic *sm); + +GType oregano_engine_get_type (void); +void oregano_engine_start (OreganoEngine *engine); +void oregano_engine_stop (OreganoEngine *engine); +gboolean oregano_engine_has_warnings (OreganoEngine *engine); +void oregano_engine_get_progress (OreganoEngine *engine, double *p); +void oregano_engine_generate_netlist (OreganoEngine *engine, const gchar *file, GError **error); +GList *oregano_engine_get_results (OreganoEngine *engine); +gchar *oregano_engine_get_current_operation (OreganoEngine *); +gboolean oregano_engine_is_available (OreganoEngine *); +gchar *oregano_engine_get_analysis_name (SimulationData *id); + +#endif diff --git a/src/engines/gnucap.c b/src/engines/gnucap.c new file mode 100644 index 0000000..8e93ec6 --- /dev/null +++ b/src/engines/gnucap.c @@ -0,0 +1,767 @@ +/* + * gnucap.c + * + * Authors: + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * + * Web page: http://oregano.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * 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; either version 2 of the + * License, or (at your option) any later version. + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <glib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <ctype.h> +#include "gnucap.h" +#include "netlist.h" + +// TODO Move analisys data and result to another file +#include "simulation.h" + +typedef enum { + STATE_IDLE, + IN_VARIABLES, + IN_VALUES, + STATE_ABORT +} ParseDataState; + +struct analisys_tag { + gchar *name; + guint len; +}; + +static struct analisys_tag analisys_tags[] = { + {"#", 1}, /* OP */ + {"#Time", 5}, /* Transient */ + {"#DC", 3}, /* DC */ + {"#Freq", 5}, /* AC */ +}; + +#define IS_THIS_ITEM(str,item) (!strncmp(str,item.name,item.len)) +#define GNUCAP_TITLE '#' +#define TAGS_COUNT (sizeof (analisys_tags) / sizeof (struct analisys_tag)) + +/* Parser STATUS */ +struct _OreganoGnuCapPriv { + GPid child_pid; + gint child_stdout; + GIOChannel *child_iochannel; + gint child_iochannel_watch; + Schematic *schematic; + + gboolean aborted; + + GList *analysis; + gint num_analysis; + SimulationData *current; + double progress; + gboolean char_last_newline; + guint status; + guint buf_count; + gchar buf[256]; // FIXME later +}; + +static void gnucap_class_init (OreganoGnuCapClass *klass); +static void gnucap_finalize (GObject *object); +static void gnucap_dispose (GObject *object); +static void gnucap_instance_init (GTypeInstance *instance, gpointer g_class); +static void gnucap_interface_init (gpointer g_iface, gpointer iface_data); +static gboolean gnucap_child_stdout_cb (GIOChannel *source, GIOCondition condition, OreganoGnuCap *gnucap); +static void gnucap_parse (gchar *raw, gint len, OreganoGnuCap *gnucap); + +static GObjectClass *parent_class = NULL; + +GType +oregano_gnucap_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (OreganoGnuCapClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gnucap_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (OreganoGnuCap), + 0, /* n_preallocs */ + (GInstanceInitFunc) gnucap_instance_init, /* instance_init */ + NULL + }; + + static const GInterfaceInfo gnucap_info = { + (GInterfaceInitFunc) gnucap_interface_init, /* interface_init */ + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, "OreganoGnuCap", &info, 0); + g_type_add_interface_static (type, OREGANO_TYPE_ENGINE, &gnucap_info); + } + return type; +} + +static void +gnucap_class_init (OreganoGnuCapClass *klass) +{ + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (klass); + + object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gnucap_dispose; + object_class->finalize = gnucap_finalize; +} + +static void +gnucap_finalize (GObject *object) +{ + SimulationData *data; + OreganoGnuCap *gnucap; + GList *lst; + int i; + + gnucap = OREGANO_GNUCAP (object); + + lst = gnucap->priv->analysis; + while (lst) { + data = SIM_DATA (lst->data); + for (i=0; i<data->n_variables; i++) { + g_free (data->var_names[i]); + g_free (data->var_units[i]); + } + g_free (data->var_names); + g_free (data->var_units); + for (i=0; i<data->n_points; i++) + g_array_free (data->data[i], TRUE); + g_free (data->min_data); + g_free (data->max_data); + + g_free (lst->data); + lst = lst->next; + } + g_list_free (gnucap->priv->analysis); + gnucap->priv->analysis = NULL; + + parent_class->finalize (object); +} + +static void +gnucap_dispose (GObject *object) +{ + parent_class->dispose (object); +} + +static gboolean +gnucap_has_warnings (OreganoEngine *self) +{ + return FALSE; +} + +static gboolean +gnucap_is_available (OreganoEngine *self) +{ + gchar *exe; + exe = g_find_program_in_path ("gnucap"); + + if (!exe) return FALSE; // gnucap not found + + g_free (exe); + return TRUE; +} + +static void +gnucap_generate_netlist (OreganoEngine *engine, const gchar *filename, GError **error) +{ + OreganoGnuCap *gnucap; + Netlist output; + SimOption *so; + GList *list; + FILE *file; + GError *local_error = NULL; + + gnucap = OREGANO_GNUCAP (engine); + + netlist_helper_create (gnucap->priv->schematic, &output, &local_error); + if (local_error != NULL) { + g_propagate_error (error, local_error); + return; + } + + file = fopen (filename, "w"); + if (!file) { + g_print ("No se pudo crear %s\n", filename); + return; + } + + list = sim_settings_get_options (output.settings); + + /* Prints title */ + fputs (output.title ? output.title : "Title: <unset>", file); + fputs ("\n" + "*----------------------------------------------" + "\n" + "*\tGNUCAP - NETLIST" + "\n", file); + /* Prints Options */ + fputs (".options OUT=120 ",file); + + while (list) { + so = list->data; + /* Prevent send NULL text */ + if (so->value) { + if (strlen(so->value) > 0) { + fprintf (file,"%s=%s ",so->name,so->value); + } + } + list = list->next; + } + fputc ('\n',file); + + /* Include of subckt models */ + fputs ("*------------- Models -------------------------\n",file); + list = output.models; + while (list) { + gchar *model; + model = (gchar *)list->data; + fprintf (file,".include %s/%s.model\n", OREGANO_MODELDIR, model); + list = list->next; + } + + fputs ("*------------- Circuit Description-------------\n",file); + fputs (output.template->str,file); + fputs ("\n*----------------------------------------------\n",file); + + /* Prints Transient Analisis */ + if (sim_settings_get_trans (output.settings)) { + gchar *tmp_str = netlist_helper_create_analisys_string (output.store, FALSE); + fprintf(file, ".print tran %s\n", tmp_str); + g_free (tmp_str); + + fprintf (file, ".tran %g %g ", + sim_settings_get_trans_start(output.settings), + sim_settings_get_trans_stop(output.settings)); + if (!sim_settings_get_trans_step_enable(output.settings)) + /* FIXME Do something to get the right resolution */ + fprintf(file,"%g", + (sim_settings_get_trans_stop(output.settings)- + sim_settings_get_trans_start(output.settings))/100); + else + fprintf(file,"%g", sim_settings_get_trans_step(output.settings)); + + if (sim_settings_get_trans_init_cond(output.settings)) { + fputs(" UIC\n", file); + } else { + fputs("\n", file); + } + } + + /* Print dc Analysis */ + if (sim_settings_get_dc (output.settings)) { + fprintf(file, ".print dc %s\n", netlist_helper_create_analisys_string (output.store, FALSE)); + fputs(".dc ",file); + + /* GNUCAP don t support nesting so the first or the second */ + /* Maybe a error message must be show if both are on */ + + if ( sim_settings_get_dc_vsrc (output.settings,0) ) { + fprintf (file,"%s %g %g %g", + sim_settings_get_dc_vsrc(output.settings,0), + sim_settings_get_dc_start (output.settings,0), + sim_settings_get_dc_stop (output.settings,0), + sim_settings_get_dc_step (output.settings,0) ); + } + + else if ( sim_settings_get_dc_vsrc (output.settings,1) ) { + fprintf (file,"%s %g %g %g", + sim_settings_get_dc_vsrc(output.settings,1), + sim_settings_get_dc_start (output.settings,1), + sim_settings_get_dc_stop (output.settings,1), + sim_settings_get_dc_step (output.settings,1) ); + }; + + fputc ('\n',file); + } + + /* Prints ac Analysis*/ + if ( sim_settings_get_ac (output.settings) ) { + double ac_start, ac_stop, ac_step; + /* GNUCAP dont support OCT or DEC */ + /* Maybe a error message must be show if is set in that way */ + ac_start = sim_settings_get_ac_start(output.settings) ; + ac_stop = sim_settings_get_ac_stop(output.settings); + ac_step = (ac_stop - ac_start) / sim_settings_get_ac_npoints(output.settings); + fprintf(file, ".print ac %s\n", netlist_helper_create_analisys_string (output.store, TRUE)); + /* AC format : ac start stop step_size */ + fprintf (file, ".ac %g %g %g\n", ac_start, ac_stop, ac_step); + } + + /* Debug op analysis. */ + fputs(".print op v(nodes)\n", file); + fputs(".op\n", file); + fputs(".end\n", file); + fclose (file); +} + +static void +gnucap_progress (OreganoEngine *self, double *d) +{ + OreganoGnuCap *gnucap = OREGANO_GNUCAP (self); + + gnucap->priv->progress += 0.1; + (*d) = gnucap->priv->progress; +} + +static void +gnucap_stop (OreganoEngine *self) +{ + OreganoGnuCap *gnucap = OREGANO_GNUCAP (self); + g_io_channel_shutdown (gnucap->priv->child_iochannel, TRUE, NULL); + g_source_remove (gnucap->priv->child_iochannel_watch); + g_spawn_close_pid (gnucap->priv->child_pid); + close (gnucap->priv->child_stdout); +} + +static void +gnucap_watch_cb (GPid pid, gint status, OreganoGnuCap *gnucap) +{ + /* check for status, see man waitpid(2) */ + if (WIFEXITED (status)) { + gchar *line; + gint status; + gsize len; + g_io_channel_read_to_end (gnucap->priv->child_iochannel, &line, &len, NULL); + if (len > 0) + gnucap_parse (line, len, gnucap); + g_free (line); + + /* Free stuff */ + g_io_channel_shutdown (gnucap->priv->child_iochannel, TRUE, NULL); + g_source_remove (gnucap->priv->child_iochannel_watch); + g_spawn_close_pid (gnucap->priv->child_pid); + close (gnucap->priv->child_stdout); + + gnucap->priv->current = NULL; + + if (gnucap->priv->num_analysis == 0) { + schematic_log_append_error (gnucap->priv->schematic, _("### Too few or none analysis found ###\n")); + gnucap->priv->aborted = TRUE; + g_signal_emit_by_name (G_OBJECT (gnucap), "aborted"); + } else + g_signal_emit_by_name (G_OBJECT (gnucap), "done"); + } +} + +static gboolean +gnucap_child_stdout_cb (GIOChannel *source, GIOCondition condition, OreganoGnuCap *gnucap) +{ + gchar *line; + gsize len, terminator; + GIOStatus status; + GError *error = NULL; + + status = g_io_channel_read_line (source, &line, &len, &terminator, &error); + if ((status & G_IO_STATUS_NORMAL) && (len > 0)) { + gnucap_parse (line, len, gnucap); + g_free (line); + } + + /* Let UI update */ + g_main_context_iteration (NULL, FALSE); + return TRUE; +} + +static void +gnucap_start (OreganoEngine *self) +{ + OreganoGnuCap *gnucap; + GError *error = NULL; + char *argv[] = {"gnucap", "-b", "/tmp/netlist.tmp", NULL}; + + gnucap = OREGANO_GNUCAP (self); + oregano_engine_generate_netlist (self, "/tmp/netlist.tmp", &error); + if (error != NULL) { + gnucap->priv->aborted = TRUE; + schematic_log_append_error (gnucap->priv->schematic, error->message); + g_signal_emit_by_name (G_OBJECT (gnucap), "aborted"); + g_error_free (error); + return; + } + + error = NULL; + if (g_spawn_async_with_pipes ( + NULL, /* Working directory */ + argv, + NULL, + G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, + NULL, + NULL, + &gnucap->priv->child_pid, + NULL, /* STDIN */ + &gnucap->priv->child_stdout, /* STDOUT */ + NULL, /* STDERR*/ + &error + )) { + /* Add a watch for process status */ + g_child_watch_add (gnucap->priv->child_pid, (GChildWatchFunc)gnucap_watch_cb, gnucap); + /* Add a GIOChannel to read from process stdout */ + gnucap->priv->child_iochannel = g_io_channel_unix_new (gnucap->priv->child_stdout); + /* Watch the I/O Channel to read child strout */ + gnucap->priv->child_iochannel_watch = g_io_add_watch (gnucap->priv->child_iochannel, + G_IO_IN|G_IO_PRI|G_IO_HUP|G_IO_NVAL, (GIOFunc)gnucap_child_stdout_cb, gnucap); + } else { + gnucap->priv->aborted = TRUE; + schematic_log_append_error (gnucap->priv->schematic, _("Unable to execute GnuCap.")); + g_signal_emit_by_name (G_OBJECT (gnucap), "aborted"); + } +} + +static GList* +gnucap_get_results (OreganoEngine *self) +{ + return OREGANO_GNUCAP (self)->priv->analysis; +} + +const gchar* +gnucap_get_operation (OreganoEngine *self) +{ + OreganoGnuCapPriv *priv = OREGANO_GNUCAP (self)->priv; + + if (priv->current == NULL) + return _("None"); + + return oregano_engine_get_analysis_name (priv->current); +} + +static void +gnucap_interface_init (gpointer g_iface, gpointer iface_data) +{ + OreganoEngineClass *klass = (OreganoEngineClass *)g_iface; + klass->start = gnucap_start; + klass->stop = gnucap_stop; + klass->progress = gnucap_progress; + klass->get_netlist = gnucap_generate_netlist; + klass->has_warnings = gnucap_has_warnings; + klass->get_results = gnucap_get_results; + klass->get_operation = gnucap_get_operation; + klass->is_available = gnucap_is_available; +} + +static void +gnucap_instance_init (GTypeInstance *instance, gpointer g_class) +{ + OreganoGnuCap *self = OREGANO_GNUCAP (instance); + + self->priv = g_new0 (OreganoGnuCapPriv, 1); + self->priv->progress = 0.0; + self->priv->char_last_newline = TRUE; + self->priv->status = 0; + self->priv->buf_count = 0; + self->priv->num_analysis = 0; + self->priv->analysis = NULL; + self->priv->current = NULL; + self->priv->aborted = FALSE; +} + +OreganoEngine* +oregano_gnucap_new (Schematic *sc) +{ + OreganoGnuCap *gnucap; + + gnucap = OREGANO_GNUCAP (g_object_new (OREGANO_TYPE_GNUCAP, NULL)); + gnucap->priv->schematic = sc; + + return OREGANO_ENGINE (gnucap); +} + +typedef struct { + gchar *name; + //gchar *unit; +} GCap_Variable; + +GCap_Variable *_get_variables(gchar *str, gint *count) +{ + GCap_Variable *out; + /* FIXME Improve the code */ + gchar *tmp[100]; + gchar *ini, *fin; + gint i = 0; + + // Don't USE!. Is not smarty !! + // It generate empty strings that really sucks all!!! + //arr = g_strsplit_set (str, " ", -1); + + i = 0; + ini = str; + /* saco espacios adelante */ + while (isspace(*ini)) ini++; + fin = ini; + while (*fin != '\0') { + if (isspace(*fin)) { + *fin = '\0'; + tmp[i] = g_strdup(ini); + *fin = ' '; + i++; + ini = fin; + while (isspace(*ini)) ini++; + fin = ini; + } else fin++; + } + + if (i == 0) { + g_warning ("NO COLUMNS FOUND\n"); + return NULL; + } + + out = g_new0 (GCap_Variable, i); + (*count) = i; + for ( i=0; i<(*count); i++ ) { + out[i].name = tmp[i]; + } + + return out; +} + +void +_free_variables(GCap_Variable *v, gint count) +{ + int i; + for(i=0; i<count; i++) + g_free(v[i].name); + g_free(v); +} + +gdouble +strtofloat (char *s) { + gdouble val; + char *error; + + val = strtod(s, &error); + /* If the value looks like : 100.u, adjust it */ + /* We need this because GNU Cap's or ngSpice float notation */ + switch (error[0]) { + case 'u': + val /= 1000000; + break; + case 'n': + val /= 1000000; + val /= 1000; + break; + case 'p': + val /= 1000000; + val /= 1000000; + break; + case 'f': + val /= 100; + break; + case 'K': + val *= 1000; + break; + default: + if (strcmp(error, "Meg") == 0) val *= 1000000; + } + + return val; +} + +/* Main method. Here we'll transform GnuCap output + * into SimulationResults! + */ +static void +gnucap_parse (gchar *raw, gint len, OreganoGnuCap *gnucap) +{ + static SimulationData *sdata; + static Analysis *data; + GCap_Variable *variables; + OreganoGnuCapPriv *priv = gnucap->priv; + gint i, j, n; + gdouble val; + gchar *s; + + for (j=0; j < len; j++) { + if (raw[j] != '\n') { + priv->buf[priv->buf_count++] = raw[j]; + continue; + } + priv->buf[priv->buf_count] = '\0'; + + //Got a complete line + s = priv->buf; + if (s[0] == GNUCAP_TITLE) { + SimSettings *sim_settings; + gdouble np1, np2; + + sim_settings = (SimSettings *)schematic_get_sim_settings (priv->schematic); + + data = g_new0 (Analysis, 1); + priv->current = sdata = SIM_DATA (data); + priv->analysis = g_list_append (priv->analysis, sdata); + priv->num_analysis++; + sdata->state = STATE_IDLE; + sdata->type = ANALYSIS_UNKNOWN; + sdata->functions = NULL; + + /* Calculates the quantity of variables */ + variables = _get_variables(s, &n); + + for (i = 0; i < TAGS_COUNT; i++) + if (IS_THIS_ITEM (variables[0].name, analisys_tags[i])) + sdata->type = i; + + sdata->state = IN_VALUES; + sdata->n_variables = n; + sdata->got_points = 0; + sdata->got_var = 0; + sdata->var_names = (char**) g_new0 (gpointer, n); + sdata->var_units = (char**) g_new0 (gpointer, n); + sdata->data = (GArray**) g_new0 (gpointer, n); + + for (i = 0; i < n; i++) + sdata->data[i] = g_array_new (TRUE, TRUE, sizeof (double)); + sdata->min_data = g_new (double, n); + sdata->max_data = g_new (double, n); + + for (i = 0; i < n; i++) { + sdata->min_data[i] = G_MAXDOUBLE; + sdata->max_data[i] = -G_MAXDOUBLE; + } + for (i = 0; i < n; i++) { + sdata->var_names[i] = g_strdup (variables[i].name); + switch (sdata->type) { + case TRANSIENT: + if (i==0) + sdata->var_units[i] = g_strdup (_("time")); + else { + if (strstr (sdata->var_names[i], "db") != NULL) { + sdata->var_units[i] = g_strdup ("db"); + } else + sdata->var_units[i] = g_strdup (_("voltage")); + } + break; + case AC: + if (i == 0) + sdata->var_units[i] = g_strdup (_("frequency")); + else { + if (strstr (sdata->var_names[i], "db") != NULL) { + sdata->var_units[i] = g_strdup ("db"); + } else + sdata->var_units[i] = g_strdup (_("voltage")); + } + break; + default: + sdata->var_units[i] = g_strdup (""); + } + } + sdata->n_variables = n; + + switch (sdata->type) { + case TRANSIENT: + data->transient.sim_length = + sim_settings_get_trans_stop (sim_settings) - + sim_settings_get_trans_start (sim_settings); + data->transient.step_size = + sim_settings_get_trans_step (sim_settings); + break; + case AC: + data->ac.start = sim_settings_get_ac_start (sim_settings); + data->ac.stop = sim_settings_get_ac_stop (sim_settings); + data->ac.sim_length = sim_settings_get_ac_npoints (sim_settings); + break; + case OP_POINT: + case DC_TRANSFER: + np1 = np2 = 1.; + data->dc.start1 = sim_settings_get_dc_start (sim_settings,0); + data->dc.stop1 = sim_settings_get_dc_stop (sim_settings,0); + data->dc.step1 = sim_settings_get_dc_step (sim_settings,0); + data->dc.start2 = sim_settings_get_dc_start (sim_settings,1); + data->dc.stop2 = sim_settings_get_dc_stop (sim_settings,1); + data->dc.step2 = sim_settings_get_dc_step (sim_settings,1); + np1 = (data->dc.stop1 - data->dc.start1) / data->dc.step1; + if (data->dc.step2 != 0.0) { + np2 = (data->dc.stop2 - data->dc.start2) / data->dc.step2; + } + data->dc.sim_length = np1 * np2; + break; + case TRANSFER: + case DISTORTION: + case NOISE: + case POLE_ZERO: + case SENSITIVITY: + break; + case ANALYSIS_UNKNOWN: + g_error (_("Unknown analysis")); + break; + } + } else { + if ((priv->analysis == NULL) || (isalpha (s[0]))) { + if (priv->buf_count > 1) { + schematic_log_append (priv->schematic, s); + schematic_log_append (priv->schematic, "\n"); + } + priv->buf_count = 0; + continue; + } + + switch (sdata->state) { + case IN_VALUES: + val = strtofloat (s); + switch (sdata->type) { + case TRANSIENT: + priv->progress = val / data->transient.sim_length; + break; + case AC: + priv->progress = (val - data->ac.start) / data->ac.sim_length; + break; + case DC_TRANSFER: + priv->progress = val / data->ac.sim_length; + } + if (priv->progress > 1.0) + priv->progress = 1.0; + + variables = _get_variables (s, &n); + for (i = 0; i < n; i++) { + val = strtofloat (variables[i].name); + + sdata->data[i] = g_array_append_val (sdata->data[i], val); + + /* Update the minimal and maximal values so far. */ + if (val < sdata->min_data[i]) + sdata->min_data[i] = val; + if (val > sdata->max_data[i]) + sdata->max_data[i] = val; + } + + _free_variables(variables, n); + sdata->got_points++; + sdata->got_var = n; + break; + default: + if (priv->buf_count > 1) { + if (strstr (s, _("abort"))) + sdata->state = STATE_ABORT; + schematic_log_append_error (priv->schematic, s); + } + } + } + priv->buf_count = 0; + } +} + diff --git a/src/engines/gnucap.h b/src/engines/gnucap.h new file mode 100644 index 0000000..d65a33b --- /dev/null +++ b/src/engines/gnucap.h @@ -0,0 +1,59 @@ +/* + * engine.c + * + * Authors: + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * + * Web page: http://oregano.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * 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; either version 2 of the + * License, or (at your option) any later version. + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GNUCAP_ENGINE +#define __GNUCAP_ENGINE + +#include <gtk/gtk.h> +#include "engine.h" + +#define OREGANO_TYPE_GNUCAP (oregano_gnucap_get_type ()) +#define OREGANO_GNUCAP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), OREGANO_TYPE_GNUCAP, OreganoGnuCap)) +#define OREGANO_GNUCAP_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), OREGANO_TYPE_GNUCAP, OreganoGnuCapClass)) +#define OREGANO_IS_GNUCAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OREGANO_TYPE_GNUCAP)) +#define OREGANO_IS_GNUCAP_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), OREGANO_TYPE_GNUCAP)) +#define OREGANO_GNUCAP_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), OREGANO_TYPE_GNUCAP, OreganoGnuCapClass)) + +typedef struct _OreganoGnuCap OreganoGnuCap; +typedef struct _OreganoGnuCapPriv OreganoGnuCapPriv; +typedef struct _OreganoGnuCapClass OreganoGnuCapClass; + +struct _OreganoGnuCap { + GObject parent; + + OreganoGnuCapPriv *priv; +}; + +struct _OreganoGnuCapClass { + GObjectClass parent; +}; + +GType oregano_gnucap_get_type (void); +OreganoEngine *oregano_gnucap_new (Schematic *sm); + +#endif + diff --git a/src/engines/netlist.c b/src/engines/netlist.c new file mode 100644 index 0000000..05c370f --- /dev/null +++ b/src/engines/netlist.c @@ -0,0 +1,689 @@ +/* + * netlist.c + * + * + * Author: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * 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; either version 2 of the + * License, or (at your option) any later version. + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gnome.h> +#include <stdio.h> +#include <string.h> +#include <string.h> +#include "main.h" +#include "schematic.h" +#include "node-store.h" +#include "node.h" +#include "wire.h" +#include "part-private.h" +#include "part-property.h" +#include "netlist.h" +#include "errors.h" + +static void netlist_helper_node_foreach_reset (gpointer key, gpointer value, gpointer user_data); +static void netlist_helper_wire_traverse (Wire *wire, NetlistData *data); +static void netlist_helper_node_traverse (Node *node, NetlistData *data); +static void netlist_helper_node_foreach_traverse (gpointer key, gpointer value, NetlistData *data); +static gboolean foreach_model_free (gpointer key, gpointer model, gpointer user_data); +static gboolean foreach_model_save (gpointer key, gpointer model, gpointer user_data); +static char *linebreak (char *str); +static void nl_node_traverse (Node *node, GSList **lst); + +static void +nl_wire_traverse (Wire *wire, GSList **lst) +{ + GSList *nodes; + + g_return_if_fail (wire != NULL); + g_return_if_fail (IS_WIRE (wire)); + + if (wire_is_visited (wire)) + return; + + wire_set_visited (wire, TRUE); + + for (nodes = wire_get_nodes (wire); nodes; nodes = nodes->next) { + GSList *pins; + Part *part; + Node *node = nodes->data; + + for(pins=node->pins; pins; pins=pins->next) { + char *template, *tmp; + char **template_split; + + part = PART (((Pin *)pins->data)->part); + + tmp = part_get_property (part, "template"); + + if (!tmp) continue; + + template = part_property_expand_macros (part, tmp); + + template_split = g_strsplit (template, " ", 0); + + (*lst) = g_slist_prepend (*lst, g_strdup (template_split[0])); + + g_strfreev (template_split); + g_free (tmp); + g_free (template); + } + + nl_node_traverse (node, lst); + } +} + +static void +nl_node_traverse (Node *node, GSList **lst) +{ + GSList *wires; + + g_return_if_fail (node != NULL); + g_return_if_fail (IS_NODE (node)); + + if (node_is_visited (node)) + return; + + node_set_visited (node, TRUE); + + for (wires = node->wires; wires; wires = wires->next) { + Wire *wire = wires->data; + nl_wire_traverse (wire, lst); + } +} + +static GSList * +netlist_helper_get_clamp_parts (NodeStore *store, Node *node) +{ + GList *wires; + GSList *lst; + Wire *wire; + GSList *ret=NULL; + + if (!node) return NULL; + + node_store_node_foreach (store, (GHFunc *)netlist_helper_node_foreach_reset, NULL); + for (wires = store->wires; wires; wires = wires->next) { + wire = wires->data; + wire_set_visited (wire, FALSE); + } + + lst = node->wires; + while (lst) { + nl_wire_traverse (lst->data, &ret); + lst = lst->next; + } + + return ret; +} + +void +netlist_helper_init_data (NetlistData *data) +{ + data->pins = g_hash_table_new (g_direct_hash, g_direct_equal); + data->models = g_hash_table_new (g_str_hash, g_str_equal); + data->node_nr = 1; + data->gnd_list = NULL; + data->clamp_list = NULL; + data->mark_list = NULL; + data->node_and_number_list = NULL; +} + +void +netlist_helper_node_foreach_reset (gpointer key, gpointer value, gpointer user_data) +{ + Node *node = value; + + node_set_visited (node, FALSE); +} + +void +netlist_helper_wire_traverse (Wire *wire, NetlistData *data) +{ + GSList *nodes; + + g_return_if_fail (wire != NULL); + g_return_if_fail (IS_WIRE (wire)); + + if (wire_is_visited (wire)) + return; + + wire_set_visited (wire, TRUE); + + for (nodes = wire_get_nodes (wire); nodes; nodes = nodes->next) { + Node *node = nodes->data; + + netlist_helper_node_traverse (node, data); + } +} + +void +netlist_helper_node_traverse (Node *node, NetlistData *data) +{ + GSList *wires, *pins; + gchar *prop; + NodeAndNumber *nan; + + g_return_if_fail (node != NULL); + g_return_if_fail (IS_NODE (node)); + + if (node_is_visited (node)) + return; + + node_set_visited (node, TRUE); + + /* Keep track of netlist nr <---> Node. */ + nan = g_new0 (NodeAndNumber, 1); + nan->node_nr = data->node_nr; + nan->node = node; + data->node_and_number_list = g_list_prepend (data->node_and_number_list, + nan); + + /* Traverse the pins at this node. */ + for (pins = node->pins; pins; pins = pins->next) { + Pin *pin = pins->data; + + /* First see if the pin belongs to an "internal", special part. */ + prop = part_get_property (pin->part, "internal"); + if (prop) { + if (!g_strcasecmp (prop, "marker")) { + Marker *marker; + gchar *name, *value; + + name = part_get_property (pin->part, "name"); + + if (!name) { + g_free (prop); + continue; + } + + value = part_property_expand_macros (pin->part, name); + g_free (name); + + if (!value) + continue; + + marker = g_new0 (Marker, 1); + marker->node_nr = data->node_nr; + marker->name = value; + data->mark_list = g_slist_prepend (data->mark_list, marker); + } else if (!g_strcasecmp (prop, "ground")) { + data->gnd_list = g_slist_prepend (data->gnd_list, GINT_TO_POINTER (data->node_nr)); + } else if (!g_strcasecmp (prop, "point")) { + data->clamp_list = g_slist_prepend (data->clamp_list, GINT_TO_POINTER (data->node_nr)); + } else if (!g_strncasecmp (prop, "jumper", 5)) { + /* Either jumper2 or jumper4. */ + Node *opposite_node; + Pin opposite_pin; + gint pin_nr, opposite_pin_nr; + SheetPos pos; + Pin *jumper_pins; + gint num_pins; + + opposite_pin_nr = -1; + num_pins = part_get_num_pins (pin->part); + jumper_pins = part_get_pins (pin->part); + for (pin_nr = 0; pin_nr < num_pins; pin_nr++) { + if (&jumper_pins[pin_nr] == pin) { + opposite_pin_nr = pin_nr; + break; + } + } + + switch (opposite_pin_nr) { + case 0: + opposite_pin_nr = 1; + break; + case 1: + opposite_pin_nr = 0; + break; + case 2: + opposite_pin_nr = 3; + break; + case 3: + opposite_pin_nr = 2; + break; + default: + g_assert (TRUE); + break; + } + + opposite_pin = jumper_pins[opposite_pin_nr]; + + item_data_get_pos (ITEM_DATA (pin->part), &pos); + pos.x += opposite_pin.offset.x; + pos.y += opposite_pin.offset.y; + + opposite_node = node_store_get_node (data->store, pos); + +#if 0 + if (node_is_visited (opposite_node)) { + GList *list; + + /* Set the node name on the current node to the same as the + already visited node. */ + for (list = data->node_and_number_list; list; list = list->next) { + NodeAndNumber *opposite_nan = list->data; + + if (opposite_nan->node == opposite_node) + nan->node_nr = opposite_nan->node_nr; + } + } +#endif + netlist_helper_node_traverse (opposite_node, data); + } + + if (g_strcasecmp (prop, "point")) { + g_free (prop); + continue; + } + g_free(prop); + } + + /* Keep track of models to include. Needs to be freed when the + * hash table is no longer needed. + */ + prop = part_get_property (pin->part, "model"); + if (prop) { + if (!g_hash_table_lookup (data->models, prop)) + g_hash_table_insert (data->models, prop, NULL); + } + + g_hash_table_insert (data->pins, pin, GINT_TO_POINTER (data->node_nr)); + } + + /* Traverse the wires at this node. */ + for (wires = node->wires; wires; wires = wires->next) { + Wire *wire = wires->data; + netlist_helper_wire_traverse (wire, data); + } +} + +void +netlist_helper_node_foreach_traverse (gpointer key, gpointer value, NetlistData *data) +{ + Node *node = value; + + /* Only visit nodes that are not already visited. */ + if (node_is_visited (node)) + return; + + netlist_helper_node_traverse (node, data); + + data->node_nr++; +} + +gint +compare_marker (gconstpointer a, gconstpointer b) +{ + const Marker *ma; + gint node_nr; + + ma = a; + node_nr = GPOINTER_TO_INT (b); + + if (ma->node_nr == node_nr) + return 0; + else + return 1; +} + +gboolean +foreach_model_save (gpointer key, gpointer model, gpointer user_data) +{ + GList **l = (GList **)user_data; + + (*l) = g_list_prepend (*l, g_strdup ((gchar *)key)); + + return TRUE; +} + +gboolean +foreach_model_free (gpointer key, gpointer model, gpointer user_data) +{ + g_free (key); + + return FALSE; +} + +char * +linebreak (char *str) +{ + char **split, *tmp; + GString *out; + int i; + + split = g_strsplit (str, "\\", 0); + + out = g_string_new (""); + + i = 0; + while (split[i] != NULL) { + if (split[i][0] == 'n') { + if (strlen (split[i]) > 1) { + out = g_string_append_c (out, '\n'); + out = g_string_append (out, split[i] + 1); + } + } else { + out = g_string_append (out, split[i]); + } + + i++; + } + + g_strfreev (split); + tmp = out->str; + g_string_free (out, FALSE); /* Don't free the string data. */ + return tmp; +} + +void +netlist_helper_create (Schematic *sm, Netlist *out, GError **error) +{ + NetlistData data; + GList *parts, *wires, *list; + Part *part; + gint pin_nr, num_pins, num_nodes, num_gnd_nodes, i, j, num_clamps; + Pin *pins; + gchar *template, **template_split; + NodeStore *store; + gchar **node2real; + + schematic_log_clear (sm); + + out->models = NULL; + out->title = schematic_get_filename (sm); + out->settings = schematic_get_sim_settings (sm); + store = schematic_get_store (sm); + out->store = store; + node_store_node_foreach (store, (GHFunc *)netlist_helper_node_foreach_reset, NULL); + for (wires = store->wires; wires; wires = wires->next) { + Wire *wire = wires->data; + wire_set_visited (wire, FALSE); + } + + netlist_helper_init_data (&data); + data.store = store; + + node_store_node_foreach (store, (GHFunc *)netlist_helper_node_foreach_traverse, &data); + + num_gnd_nodes = g_slist_length (data.gnd_list); + num_clamps = g_slist_length (data.clamp_list); + + /* Check if there is a Ground node */ + if (num_gnd_nodes == 0) { + schematic_log_append (sm, _("No ground node. Aborting.\n")); + schematic_log_show (sm); + g_set_error (error, + OREGANO_ERROR, + OREGANO_SIMULATE_ERROR_NO_GND, + _("Possibly due to a faulty circuit schematic. Please check that\n" + "you have a ground node and try again.") + ); + + /* FIXME!!! */ + goto bail_out; + } + + if (num_clamps == 0) { + schematic_log_append (sm, _("No test clamps found. Aborting.\n")); + schematic_log_show (sm); + g_set_error (error, + OREGANO_ERROR, + OREGANO_SIMULATE_ERROR_NO_CLAMP, + _("Possibly due to a faulty circuit schematic. Please check that\n" + "you have one o more test clamps and try again.") + ); + + goto bail_out; + } + + num_nodes = data.node_nr - 1; + + /* + * Build an array for going from node nr to "real node nr", + * where gnd nodes are nr 0 and the rest of the nodes are + * 1, 2, 3, ... + */ + node2real = g_new0 (gchar*, num_nodes + 1); + + for (i = 1, j = 1; i <= num_nodes; i++) { + GSList *mlist; + + if (g_slist_find (data.gnd_list, GINT_TO_POINTER (i))) + node2real[i] = g_strdup ("0"); + else if ((mlist = g_slist_find_custom (data.mark_list, + GINT_TO_POINTER (i), + compare_marker))) { + Marker *marker = mlist->data; + node2real[i] = g_strdup (marker->name); + } + else node2real[i] = g_strdup_printf ("%d", j++); + } + + + /* + * Fill in the netlist node names for all the used nodes. + */ + for (list = data.node_and_number_list; list; list = list->next) { + NodeAndNumber *nan = list->data; + if (nan->node->netlist_node_name != NULL) + g_free (nan->node->netlist_node_name); + if (nan->node_nr != 0) + nan->node->netlist_node_name = g_strdup (node2real[nan->node_nr]); + } + + /* Initialize out->template */ + out->template = g_string_new(""); + for (parts = store->parts; parts; parts = parts->next) { + gchar *tmp, *internal; + GString *str; + + part = parts->data; + internal = part_get_property (part, "internal"); + if (internal != NULL) { + gint node_nr; + Pin *pins; + if (g_strcasecmp (internal, "point") != 0) { + g_free (internal); + continue; + } + + /* Got a clamp!, set node number */ + pins = part_get_pins (part); + node_nr = GPOINTER_TO_INT (g_hash_table_lookup (data.pins, &pins[0])); + if (!node_nr) { + g_warning ("Couln't find part, pin_nr %d.", 0); + } else { + gchar *tmp; + tmp = node2real[node_nr]; + + /* need to substrac 1, netlist starts in 0, and node_nr in 1 */ + pins[0].node_nr = atoi(node2real[node_nr]); + } + g_free (internal); + continue; + } + + tmp = part_get_property (part, "template"); + if (!tmp) { + continue; + } + + template = part_property_expand_macros (part, tmp); + //g_print ("Template: '%s'\n" "macro : '%s'\n", tmp, template); + if (tmp != NULL) g_free (tmp); + + tmp = linebreak (template); + + if (template != NULL) g_free (template); + template = tmp; + + num_pins = part_get_num_pins (part); + pins = part_get_pins (part); + + template_split = g_strsplit (template, " ", 0); + if (template != NULL) g_free (template); + template = NULL; + + str = g_string_new (""); + + i = 0; + while (template_split[i] != NULL && template_split[i][0] != '%') { + g_string_append (str, template_split[i++]); + g_string_append_c (str , ' '); + //g_print ("str: %s\n", str->str); + } + + //g_print ("Reading %d pins.\n)", num_pins); + + for (pin_nr = 0; pin_nr < num_pins; pin_nr++) { + gint node_nr; + node_nr = GPOINTER_TO_INT (g_hash_table_lookup (data.pins, &pins[pin_nr])); + if (!node_nr) { + g_warning ("Couldn't find part, pin_nr %d.", pin_nr); + } else { + gchar *tmp; + tmp = node2real[node_nr]; + + /* need to substrac 1, netlist starts in 0, and node_nr in 1 */ + pins[pin_nr].node_nr = atoi(node2real[node_nr]); + g_string_append (str, tmp); + g_string_append_c (str, ' '); + /*g_print ("str: %s\n", str->str);*/ + i++; + } + + while (template_split[i] != NULL) { + if (template_split[i][0] == '%') + break; + + g_string_append (str, template_split[i]); + g_string_append_c (str, ' '); + //g_print ("str: %s\n", str->str); + i++; + } + } + + //g_print ("Done with pins, i = %d\n", i); + + while (template_split[i] != NULL) { + g_string_append (str, template_split[i]); + g_string_append_c (str, ' '); + //g_print ("str: %s\n", str->str); + i++; + } + + g_strfreev (template_split); + //g_print ("str: %s\n", str->str); + out->template = g_string_append(out->template, str->str); + out->template = g_string_append_c(out->template, '\n'); + g_string_free (str, TRUE); + } + + for (i = 0; i < num_nodes + 1; i++) { + g_free (node2real[i]); + } + g_free (node2real); + + g_hash_table_foreach (data.models, (GHFunc)foreach_model_save, &out->models); + + return; + +bail_out: + g_hash_table_foreach (data.models, (GHFunc)foreach_model_free, NULL); + g_hash_table_destroy (data.models); + g_hash_table_destroy (data.pins); + for (list = data.node_and_number_list; list; list = list->next) { + NodeAndNumber *nan = list->data; + if (nan != NULL) g_free (nan); + } + g_list_free (data.node_and_number_list); +} + +char * +netlist_helper_create_analisys_string (NodeStore *store, gboolean do_ac) +{ + GList *parts; + GList *p; + gchar *prop, *type, *ac; + parts = node_store_get_parts (store); + GString *out; + gchar *ret; + + out = g_string_new (""); + + for(p=parts; p; p = p->next) { + prop = part_get_property (p->data, "internal"); + if (prop) { + if (!g_strcasecmp (prop, "point")) { + Pin *pins = part_get_pins (p->data); + g_free (prop); + + prop = part_get_property (p->data, "type"); + + if (!g_strcasecmp (prop, "v")) { + if (!do_ac) { + g_string_append_printf (out, " %s(%d)", prop, pins[0].node_nr); + } else { + gchar *ac_type, *ac_db; + ac_type = part_get_property (p->data, "ac_type"); + ac_db = part_get_property (p->data, "ac_db"); + + if (!g_strcasecmp (ac_db, "true")) + g_string_append_printf (out, " %s%sdb(%d)", prop, ac_type, pins[0].node_nr); + else + g_string_append_printf (out, " %s%s(%d)", prop, ac_type, pins[0].node_nr); + } + } else { + Node *node; + SheetPos lookup_key; + SheetPos part_pos; + + item_data_get_pos (ITEM_DATA (p->data), &part_pos); + + lookup_key.x = part_pos.x + pins[0].offset.x; + lookup_key.y = part_pos.y + pins[0].offset.y; + + node = node_store_get_or_create_node (store, lookup_key); + + if (node) { + GSList *lst, *it; + it = lst = netlist_helper_get_clamp_parts (store, node); + while (it) { + g_string_append_printf (out, " i(%s)", (char *)it->data); + it = it->next; + } + g_slist_free (lst); + } + } + } + } + } + + ret = out->str; + g_string_free (out, FALSE); + return ret; +} + diff --git a/src/engines/netlist.h b/src/engines/netlist.h new file mode 100644 index 0000000..9d9ca0f --- /dev/null +++ b/src/engines/netlist.h @@ -0,0 +1,71 @@ +/* + * netlist.h + * + * + * Author: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2004 Ricardo Markiewicz + * + * 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; either version 2 of the + * License, or (at your option) any later version. + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __NETLIST_H +#define __NETLIST_H + +#include <glib.h> +#include "schematic.h" +#include "sim-settings.h" + +typedef struct { + gint node_nr; ///< Node number + GHashTable *pins; + GHashTable *models; + GSList *gnd_list; ///< Ground parts on the schematic + GSList *clamp_list; ///< Test clamps on the schematic + GSList *mark_list; + GList *node_and_number_list; + NodeStore *store; +} NetlistData; + +typedef struct { + gchar *cmd; + gchar *title; + GString *template; + SimSettings *settings; + NodeStore *store; + GList *models; +} Netlist; + +typedef struct { + gint node_nr; + gchar *name; +} Marker; + +typedef struct { + gint node_nr; + Node *node; +} NodeAndNumber; + +void netlist_helper_init_data (NetlistData *data); +void netlist_helper_create (Schematic *sm, Netlist *out, GError **error); +char *netlist_helper_create_analisys_string (NodeStore *store, gboolean do_ac); + +#endif diff --git a/src/engines/ngspice.c b/src/engines/ngspice.c new file mode 100644 index 0000000..98c2372 --- /dev/null +++ b/src/engines/ngspice.c @@ -0,0 +1,633 @@ +/* + * ngspice.c + * + * Authors: + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * + * Web page: http://oregano.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * 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; either version 2 of the + * License, or (at your option) any later version. + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <glib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <ctype.h> +#include "ngspice.h" +#include "netlist.h" + +// TODO Move analisys data and result to another file +#include "simulation.h" + +/* Parser STATUS */ +struct _OreganoNgSpicePriv { + GPid child_pid; + Schematic *schematic; + + gboolean aborted; + + GList *analysis; + gint num_analysis; + SimulationData *current; + double progress; + gboolean char_last_newline; + guint status; + guint buf_count; + gchar buf[256]; // FIXME later +}; + +static void ngspice_instance_init (GTypeInstance *instance, gpointer g_class); +static void ngspice_interface_init (gpointer g_iface, gpointer iface_data); +static gboolean ngspice_child_stdout_cb (GIOChannel *source, GIOCondition condition, OreganoNgSpice *ngspice); +static void ngspice_parse (FILE *, OreganoNgSpice *ngspice); + +GType +oregano_ngspice_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (OreganoNgSpiceClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (OreganoNgSpice), + 0, /* n_preallocs */ + ngspice_instance_init /* instance_init */ + }; + + static const GInterfaceInfo ngspice_info = { + (GInterfaceInitFunc) ngspice_interface_init, /* interface_init */ + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, "OreganoNgSpice", &info, 0); + g_type_add_interface_static (type, OREGANO_TYPE_ENGINE, &ngspice_info); + } + return type; +} + +static gboolean +ngspice_has_warnings (OreganoEngine *self) +{ + return FALSE; +} + +static gboolean +ngspice_is_available (OreganoEngine *self) +{ + gchar *exe; + exe = g_find_program_in_path ("ngspice"); + + if (!exe) return FALSE; // ngspice not found + + g_free (exe); + return TRUE; +} + +static void +ngspice_generate_netlist (OreganoEngine *engine, const gchar *filename, GError **error) +{ + OreganoNgSpice *ngspice; + Netlist output; + GList *list; + SimOption *so; + GError *local_error = NULL; + FILE *file; + + ngspice = OREGANO_NGSPICE (engine); + + netlist_helper_create (ngspice->priv->schematic, &output, &local_error); + if (local_error != NULL) { + g_propagate_error (error, local_error); + return; + } + + file = fopen (filename, "w"); + if (!file) { + g_print ("Cannot create file %s\n", filename); + return; + } + + /* Prints title */ + fputs ("* ",file); + fputs (output.title ? output.title : "Title: <unset>", file); + fputs ("\n" + "*----------------------------------------------" + "\n" + "*\tSPICE 3 - NETLIST" + "\n", file); + + /* Prints Options */ + fputs (".options\n", file); + + list = sim_settings_get_options (output.settings); + while (list) { + so = list->data; + if (so->value) + if (strlen(so->value) > 0) + fprintf (file,"+ %s=%s\n", so->name, so->value); + list = list->next; + } + fputc ('\n',file); + + /* Include of subckt models */ + fputs ("*------------- Models -------------------------\n",file); + list = output.models; + while (list) { + gchar *model; + model = (gchar *)list->data; + fprintf (file,".include %s/%s.model\n", OREGANO_MODELDIR, model); + list = list->next; + } + + /* Prints template parts */ + fputs ("\n*----------------------------------------------\n\n",file); + fputs (output.template->str, file); + fputs ("\n*----------------------------------------------\n\n",file); + + /* Prints Transient Analisis */ + if (sim_settings_get_trans (output.settings)) { + gdouble st = 0; + if (sim_settings_get_trans_step_enable (output.settings)) + st = sim_settings_get_trans_step (output.settings); + else + st = (sim_settings_get_trans_stop (output.settings) - + sim_settings_get_trans_start (output.settings)) / 50; + + fprintf (file, ".tran %g %g %g", st, + sim_settings_get_trans_stop (output.settings), + sim_settings_get_trans_start (output.settings)); + if (sim_settings_get_trans_init_cond (output.settings)) { + fputs(" UIC\n", file); + } else { + fputs("\n", file); + } + } + + /* Print dc Analysis */ + if (sim_settings_get_dc (output.settings)) { + fputs(".dc ",file); + if (sim_settings_get_dc_vsrc (output.settings, 0)) { + fprintf (file, "%s %g %g %g", + sim_settings_get_dc_vsrc (output.settings, 0), + sim_settings_get_dc_start (output.settings, 0), + sim_settings_get_dc_stop (output.settings, 0), + sim_settings_get_dc_step (output.settings, 0)); + } + if (sim_settings_get_dc_vsrc (output.settings, 1)) { + fprintf (file, "%s %g %g %g", + sim_settings_get_dc_vsrc (output.settings, 1), + sim_settings_get_dc_start (output.settings, 1), + sim_settings_get_dc_stop (output.settings, 1), + sim_settings_get_dc_step (output.settings, 1)); + } + } + + /* Prints ac Analysis*/ + if (sim_settings_get_ac (output.settings)) { + fprintf (file, ".ac %s %d %g %g\n", + sim_settings_get_ac_type (output.settings), + sim_settings_get_ac_npoints (output.settings), + sim_settings_get_ac_start (output.settings), + sim_settings_get_ac_stop (output.settings)); + } + + /* Debug op analysis. */ + fputs (".op\n", file); + fputs ("\n.END\n", file); + fclose (file); +} + +static void +ngspice_progress (OreganoEngine *self, double *d) +{ + (*d) = OREGANO_NGSPICE (self)->priv->progress; +} + +static void +ngspice_stop (OreganoEngine *self) +{ + OreganoNgSpice *ngspice = OREGANO_NGSPICE (self); + g_spawn_close_pid (ngspice->priv->child_pid); +} + +static void +ngspice_watch_cb (GPid pid, gint status, OreganoNgSpice *ngspice) +{ + /* check for status, see man waitpid(2) */ + if (WIFEXITED (status)) { + FILE *fp; + + g_spawn_close_pid (ngspice->priv->child_pid); + + /* Parse data */ + if ((fp = fopen ("/tmp/netlist.raw", "r")) != NULL) { + g_print ("File Open\n"); + while (!feof (fp)) + ngspice_parse (fp, ngspice); + } + + ngspice->priv->current = NULL; + + if (ngspice->priv->num_analysis == 0) { + schematic_log_append_error (ngspice->priv->schematic, _("### Too few or none analysis found ###\n")); + ngspice->priv->aborted = TRUE; + g_signal_emit_by_name (G_OBJECT (ngspice), "aborted"); + } else + g_signal_emit_by_name (G_OBJECT (ngspice), "done"); + } +} + +static void +ngspice_start (OreganoEngine *self) +{ + OreganoNgSpice *ngspice; + GError *error = NULL; + char *argv[] = {"ngspice", "-r", "/tmp/netlist.raw", "-b", "/tmp/netlist.tmp", NULL}; + + ngspice = OREGANO_NGSPICE (self); + oregano_engine_generate_netlist (self, "/tmp/netlist.tmp", &error); + if (error != NULL) { + ngspice->priv->aborted = TRUE; + schematic_log_append_error (ngspice->priv->schematic, error->message); + g_signal_emit_by_name (G_OBJECT (ngspice), "aborted"); + g_error_free (error); + return; + } + + error = NULL; + if (g_spawn_async_with_pipes ( + NULL, /* Working directory */ + argv, + NULL, + G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, + NULL, + NULL, + &ngspice->priv->child_pid, + NULL, /* STDIN */ + NULL, /* STDOUT */ + NULL, /* STDERR*/ + &error + )) { + /* Add a watch for process status */ + g_child_watch_add (ngspice->priv->child_pid, (GChildWatchFunc)ngspice_watch_cb, ngspice); + } else { + ngspice->priv->aborted = TRUE; + schematic_log_append_error (ngspice->priv->schematic, _("Unable to execute NgSpice.")); + g_signal_emit_by_name (G_OBJECT (ngspice), "aborted"); + } +} + +static GList* +ngspice_get_results (OreganoEngine *self) +{ + return OREGANO_NGSPICE (self)->priv->analysis; +} + +const gchar* +ngspice_get_operation (OreganoEngine *self) +{ + OreganoNgSpicePriv *priv = OREGANO_NGSPICE (self)->priv; + + if (priv->current == NULL) + return _("Waiting NgSpice backend"); + + return oregano_engine_get_analysis_name (priv->current); +} + +static void +ngspice_interface_init (gpointer g_iface, gpointer iface_data) +{ + OreganoEngineClass *klass = (OreganoEngineClass *)g_iface; + klass->start = ngspice_start; + klass->stop = ngspice_stop; + klass->progress = ngspice_progress; + klass->get_netlist = ngspice_generate_netlist; + klass->has_warnings = ngspice_has_warnings; + klass->get_results = ngspice_get_results; + klass->get_operation = ngspice_get_operation; + klass->is_available = ngspice_is_available; +} + +static void +ngspice_instance_init (GTypeInstance *instance, gpointer g_class) +{ + OreganoNgSpice *self = OREGANO_NGSPICE (instance); + + self->priv = g_new0 (OreganoNgSpicePriv, 1); +} + +OreganoEngine* +oregano_ngspice_new (Schematic *sc) +{ + OreganoNgSpice *ngspice; + + ngspice = OREGANO_NGSPICE (g_object_new (OREGANO_TYPE_NGSPICE, NULL)); + ngspice->priv->schematic = sc; + + return OREGANO_ENGINE (ngspice); +} + +/* Parser stuff */ + +/* By now, the parser read the STDOUt just like in GnuCap. In a near future I'll + * to use the raw outout feature of NgSpice wich will improve the parser + */ + +typedef enum { + STATE_IDLE, + IN_VARIABLES, + IN_VALUES, + STATE_ABORT +} ParseDataState; + +#define SP_TITLE "Title" +#define SP_DATE "Date:" +#define SP_PLOT_NAME "Plotname:" +#define SP_FLAGS "Flags:" +#define SP_N_VAR "No. Variables:" +#define SP_N_POINTS "No. Points:" +#define SP_COMMAND "Command:" +#define SP_VARIABLES "Variables:" +#define SP_BINARY "Binary:" +#define SP_VALUES "Values:" +#define IS_THIS_ITEM(str,item) (!strncmp(str,item,strlen(item))) + +static gchar *analysis_names[] = { + N_("Operating Point") , + N_("Transient Analysis") , + N_("DC transfer characteristic") , + N_("AC Analysis") , + N_("Transfer Function") , + N_("Distortion Analysis") , + N_("Noise Analysis") , + N_("Pole-Zero Analysis") , + N_("Sensitivity Analysis") , + N_("Unknown Analysis") , + NULL +}; + +#define NG_DEBUG(s) if (0) g_print ("%s\n", s) + +static void +ngspice_parse (FILE *fp, OreganoNgSpice *ngspice) +{ + static SimulationData *sdata = NULL; + static char buf[1024]; + static int len; + static is_complex = FALSE; + SimSettings *sim_settings; + gint status, iter; + gdouble val, np1, np2; + gchar **tmp = NULL; + gchar *send; + int i, c; + + sim_settings = (SimSettings *)schematic_get_sim_settings (ngspice->priv->schematic); + + if (!sdata || sdata->state != IN_VALUES) { + /* Read a line */ + len = fscanf (fp, "%[^\n]", buf); + fgetc (fp); + if (len == 0) return; + NG_DEBUG (g_strdup_printf ("(%s)", buf)); + } else { + buf[0] = '\0'; + len = 0; + } + + /* We are getting the simulation title */ + if (IS_THIS_ITEM (buf, SP_TITLE)) { + sdata = SIM_DATA (g_new0 (Analysis, 1)); + ngspice->priv->analysis = g_list_append (ngspice->priv->analysis, sdata); + ngspice->priv->num_analysis++; + NG_DEBUG ("Nuevo Analisis"); + } else if (IS_THIS_ITEM (buf, SP_DATE)) { + sdata->state = STATE_IDLE; + } else if (IS_THIS_ITEM (buf,SP_PLOT_NAME)) { + gint i; + gchar *analysis = buf+strlen(SP_PLOT_NAME)+1; + + NG_DEBUG ("Analisis Type"); + sdata->state = STATE_IDLE; + sdata->type = ANALYSIS_UNKNOWN; + for (i = 0; analysis_names[i]; i++) + if (IS_THIS_ITEM (analysis, analysis_names[i])) { + sdata->type = i; + break; + } + + switch ( sdata->type ) { + case TRANSIENT: + ANALYSIS(sdata)->transient.sim_length = + sim_settings_get_trans_stop (sim_settings) - + sim_settings_get_trans_start (sim_settings); + ANALYSIS(sdata)->transient.step_size = + sim_settings_get_trans_step (sim_settings); + break; + case AC: + ANALYSIS(sdata)->ac.start = sim_settings_get_ac_start (sim_settings); + ANALYSIS(sdata)->ac.stop = sim_settings_get_ac_stop (sim_settings); + ANALYSIS(sdata)->ac.sim_length = sim_settings_get_ac_npoints (sim_settings); + break; + case OP_POINT: + case DC_TRANSFER: + np1 = np2 = 1.; + ANALYSIS(sdata)->dc.start1 = sim_settings_get_dc_start (sim_settings, 0); + ANALYSIS(sdata)->dc.stop1 = sim_settings_get_dc_stop (sim_settings, 0); + ANALYSIS(sdata)->dc.step1 = sim_settings_get_dc_step (sim_settings, 0); + ANALYSIS(sdata)->dc.start2 = sim_settings_get_dc_start (sim_settings, 1); + ANALYSIS(sdata)->dc.stop2 = sim_settings_get_dc_stop (sim_settings, 1); + ANALYSIS(sdata)->dc.step2 = sim_settings_get_dc_step (sim_settings, 1); + + np1 = (ANALYSIS(sdata)->dc.stop1-ANALYSIS(sdata)->dc.start1) / ANALYSIS(sdata)->dc.step1; + if (ANALYSIS(sdata)->dc.step2 != 0.) { + np2 = (ANALYSIS(sdata)->dc.stop2-ANALYSIS(sdata)->dc.start2) / ANALYSIS(sdata)->dc.step2; + } + ANALYSIS(sdata)->dc.sim_length = np1 * np2; + break; + case TRANSFER: + case DISTORTION: + case NOISE: + case POLE_ZERO: + case SENSITIVITY: + break; + case ANALYSIS_UNKNOWN: + g_error ("Unknown analysis: %s", analysis); + break; + } + ngspice->priv->current = sdata; + } else if (IS_THIS_ITEM(buf, SP_FLAGS) ) { + char *f = buf + strlen (SP_FLAGS) + 1; + if (strncmp (f, "complex", 7) == 0) + is_complex = TRUE; + else + is_complex = FALSE; + } else if (IS_THIS_ITEM (buf, SP_COMMAND)) { + /* pass */ + } else if (IS_THIS_ITEM (buf, SP_N_VAR)) { + gint i, n = atoi (buf + strlen (SP_N_VAR)); + + NG_DEBUG (g_strdup_printf ("NVAR %d", n)); + sdata->state = STATE_IDLE; + sdata->n_variables = n; + sdata->got_points = 0; + sdata->got_var = 0; + sdata->var_names = (char**) g_new0 (gpointer, n); + sdata->var_units = (char**) g_new0 (gpointer, n); + sdata->data = (GArray**) g_new0 (gpointer, n); + for (i = 0; i < n; i++) + sdata->data[i] = g_array_new (TRUE, TRUE, sizeof (double)); + sdata->min_data = g_new (double, n); + sdata->max_data = g_new (double, n); + for (i = 0; i < n; i++) { + sdata->min_data[i] = G_MAXDOUBLE; + sdata->max_data[i] = -G_MAXDOUBLE; + } + } else if (IS_THIS_ITEM (buf, SP_N_POINTS)) { + sdata->state = STATE_IDLE; + sdata->n_points = atoi (buf + strlen (SP_N_POINTS)); + NG_DEBUG (g_strdup_printf ("NPOINTS %d", sdata->n_points)); + } else if (IS_THIS_ITEM (buf, SP_VARIABLES)) { + NG_DEBUG ("In variables"); + sdata->state = IN_VARIABLES; + } else if (IS_THIS_ITEM (buf, SP_BINARY)) { + NG_DEBUG ("Data Bynari"); + sdata->state = IN_VALUES; + sdata->binary = TRUE; + sdata->got_var = 0; + sdata->got_points = 0; + } else if (IS_THIS_ITEM (buf, SP_VALUES)) { + sdata->state = IN_VALUES; + sdata->binary = FALSE; + sdata->got_var = 0; + sdata->got_points = 0; + } else { + if (ngspice->priv->analysis == NULL) { + if (len > 1) + schematic_log_append (ngspice->priv->schematic, buf); + return; + } + + switch (sdata->state) { + case IN_VARIABLES: + tmp = g_strsplit (buf, "\t", 0); + sdata->var_names[sdata->got_var] = g_strdup (tmp[2]); + sdata->var_units[sdata->got_var] = g_strdup (tmp[3]); + send = strchr (sdata->var_units[sdata->got_var], '\n'); + if (send) + *send = 0; + g_strfreev (tmp); + + if ((sdata->got_var + 1) < sdata->n_variables) + sdata->got_var++; + break; + case IN_VALUES: + if (sdata->binary) { + int i, j; + double d, dimg; + NG_DEBUG ("Reading Binary"); + for (i=0; i<sdata->n_points; i++) { + for (j=0; j<sdata->n_variables; j++) { + /* TODO : This show always the real part only. We need to detect + * the probe settings for this node, and show the correct + * value : real, imaginary, module or phase, of the complex number. + */ + fread (&d, sizeof (double), 1, fp); + if (is_complex) + fread (&dimg, sizeof (double), 1, fp); + if (j == 0) { + switch (sdata->type) { + case TRANSIENT: + ngspice->priv->progress = d / ANALYSIS(sdata)->transient.sim_length; + break; + case AC: + ngspice->priv->progress = (d - ANALYSIS(sdata)->ac.start) / ANALYSIS(sdata)->ac.sim_length; + break; + case DC_TRANSFER: + ngspice->priv->progress = ((gdouble) iter) / ANALYSIS(sdata)->ac.sim_length; + } + if (ngspice->priv->progress > 1.0) + ngspice->priv->progress = 1.0; + if (ngspice->priv->progress < 0.0) + ngspice->priv->progress = 0.0; + g_main_context_iteration (NULL, FALSE); + } + sdata->data[j] = g_array_append_val (sdata->data[j], d); + + /* Update the minimal and maximal values so far. */ + if (d < sdata->min_data[j]) + sdata->min_data[j] = d; + if (d > sdata->max_data[j]) + sdata->max_data[j] = d; + } + } + sdata->state = STATE_IDLE; + NG_DEBUG ("Reading Binary Done"); + } else { + if (sdata->got_var) + sscanf(buf, "\t%lf", &val); + else + sscanf(buf, "%d\t\t%lf", &iter, &val); + if (sdata->got_var == 0) { + switch (sdata->type) { + case TRANSIENT: + ngspice->priv->progress = val / ANALYSIS(sdata)->transient.sim_length; + break; + case AC: + ngspice->priv->progress = (val - ANALYSIS(sdata)->ac.start) / ANALYSIS(sdata)->ac.sim_length; + break; + case DC_TRANSFER: + ngspice->priv->progress = ((gdouble) iter) / ANALYSIS(sdata)->ac.sim_length; + } + if (ngspice->priv->progress > 1.0) + ngspice->priv->progress = 1.0; + } + + sdata->data[sdata->got_var] = g_array_append_val (sdata->data[sdata->got_var], val); + + /* Update the minimal and maximal values so far. */ + if (val < sdata->min_data[sdata->got_var]) + sdata->min_data[sdata->got_var] = val; + if (val > sdata->max_data[sdata->got_var]) + sdata->max_data[sdata->got_var] = val; + + /* Check for the end of the point. */ + if (sdata->got_var + 1 == sdata->n_variables) { + sdata->got_var = 0; + sdata->got_points++; + } else + sdata->got_var++; + } + break; + default: + if (len > 1) { + if (strstr (buf,"abort")) + sdata->state = STATE_ABORT; + schematic_log_append (ngspice->priv->schematic, buf); + } + break; + } + } +} + diff --git a/src/engines/ngspice.h b/src/engines/ngspice.h new file mode 100644 index 0000000..b422c15 --- /dev/null +++ b/src/engines/ngspice.h @@ -0,0 +1,59 @@ +/* + * ngspice.c + * + * Authors: + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * + * Web page: http://oregano.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * 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; either version 2 of the + * License, or (at your option) any later version. + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __NGSPICE_ENGINE +#define __NGSPICE_ENGINE + +#include <gtk/gtk.h> +#include "engine.h" + +#define OREGANO_TYPE_NGSPICE (oregano_ngspice_get_type ()) +#define OREGANO_NGSPICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), OREGANO_TYPE_NGSPICE, OreganoNgSpice)) +#define OREGANO_NGSPICE_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), OREGANO_TYPE_NGSPICE, OreganoNgSpiceClass)) +#define OREGANO_IS_NGSPICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OREGANO_TYPE_NGSPICE)) +#define OREGANO_IS_NGSPICE_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), OREGANO_TYPE_NGSPICE)) +#define OREGANO_NGSPICE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), OREGANO_TYPE_NGSPICE, OreganoNgSpiceClass)) + +typedef struct _OreganoNgSpice OreganoNgSpice; +typedef struct _OreganoNgSpicePriv OreganoNgSpicePriv; +typedef struct _OreganoNgSpiceClass OreganoNgSpiceClass; + +struct _OreganoNgSpice { + GObject parent; + + OreganoNgSpicePriv *priv; +}; + +struct _OreganoNgSpiceClass { + GObjectClass parent; +}; + +GType oregano_ngspice_get_type (void); +OreganoEngine *oregano_ngspice_new (Schematic *sm); + +#endif + |