summaryrefslogtreecommitdiff
path: root/src/engines
diff options
context:
space:
mode:
Diffstat (limited to 'src/engines')
-rw-r--r--src/engines/Makefile.am20
-rw-r--r--src/engines/engine.c178
-rw-r--r--src/engines/engine.h89
-rw-r--r--src/engines/gnucap.c767
-rw-r--r--src/engines/gnucap.h59
-rw-r--r--src/engines/netlist.c689
-rw-r--r--src/engines/netlist.h71
-rw-r--r--src/engines/ngspice.c633
-rw-r--r--src/engines/ngspice.h59
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
+