r5827 - in trunk: eda eda/boom gta02-core/bom gta02-core/bom/fic

werner at docs.openmoko.org werner at docs.openmoko.org
Sun Feb 7 15:33:41 CET 2010


Author: werner
Date: 2010-02-07 15:33:40 +0100 (Sun, 07 Feb 2010)
New Revision: 5827

Added:
   trunk/eda/boom/
   trunk/eda/boom/CHARACTERISTICS
   trunk/eda/boom/Makefile
   trunk/eda/boom/README
   trunk/eda/boom/annotate
   trunk/eda/boom/bom2part
   trunk/eda/boom/boom
   trunk/eda/boom/gen2chr
   trunk/eda/boom/match.pl
   trunk/eda/boom/misc.pl
   trunk/eda/boom/pardup.pl
   trunk/eda/boom/parser.pl
   trunk/eda/boom/part2order
   trunk/eda/boom/prettyord
   trunk/eda/boom/test/
   trunk/eda/boom/workflow.fig
Removed:
   trunk/gta02-core/bom/CHARACTERISTICS
   trunk/gta02-core/bom/README
   trunk/gta02-core/bom/annotate
   trunk/gta02-core/bom/bom2part
   trunk/gta02-core/bom/gen2chr
   trunk/gta02-core/bom/match.pl
   trunk/gta02-core/bom/misc.pl
   trunk/gta02-core/bom/parser.pl
   trunk/gta02-core/bom/part2order
   trunk/gta02-core/bom/prettyord
   trunk/gta02-core/bom/test/
   trunk/gta02-core/bom/workflow.fig
Modified:
   trunk/gta02-core/bom/Makefile
   trunk/gta02-core/bom/fic/Makefile
   trunk/gta02-core/bom/fic/equ2equ
Log:
Moved BOM processor over to eda/boom/

- bom/: moved project-independent files to eda/boom/
- eda/boom/pardup.pl: oops, forget to add it to SVN
- eda/boom/boom: wrapper for the other scripts. Sets the include path.
- bom/Makefile, bom/fic/Makefile: updated to use boom wrapper



Copied: trunk/eda/boom/CHARACTERISTICS (from rev 5821, trunk/gta02-core/bom/CHARACTERISTICS)
===================================================================
--- trunk/eda/boom/CHARACTERISTICS	                        (rev 0)
+++ trunk/eda/boom/CHARACTERISTICS	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,140 @@
+*** This is an older draft of the concept - differs sometimes from the way
+    things are done in gta02-core. ***
+
+
+BOM matching
+============
+
+BOMs are matched with inventories in the following way:
+
+- a .lst file with the BOM is generated by KiCad
+
+- using a ruleset, component characteristics are translated to a
+  canonical format and default values may be used for unspecified
+  characteristics
+
+- part catalogs are searched for matches with the canonical component
+  descriptions. This yields a list of supplier-specific part numbers
+  for each component.
+
+  Parts can be characterized by either specifying their properties or
+  by equating them to another part. E.g., a Digi-Key part may be
+  defined as an NXP part which in turn is equivalent to a TI part.
+
+- this list is then matched against inventories, using a suitable
+  optimization strategy (e.g., prioritize inventories and try to
+  pick as many suitable components as possible higher priority ones
+  before moving to lower priority ones)
+
+  E.g., local stock could be the first-level inventory, followed by
+  more distant warehouses, followed by distributors, followed by
+  manufacturers.
+
+  Inventories could also include pricing information.
+
+- TBD: it would be good if parameters gathered in the matching process
+  could be fed back into KiCad (as some sort of annotations, similar
+  to the expanded view of schematic symbols), such that under-specifed
+  parts yielding mismatches can be spotted by manual review.
+
+
+Catalog
+=======
+
+A catalog contains part characteristics and the reference number(s)
+assigned to them.
+
+
+Basic syntax
+------------
+
+Catalog entries consist of "words" in the sense that each word does
+not contain any whitespace and words are separated from each other by
+whitespace. Whitespace can be included in a word if it is enclosed in
+double quotes.
+
+Each entry begins in the first column of a line. If an entry needs
+more than one line, the words on the continuation line(s) must be
+indented by whitespace.
+
+Trailing whitespace is ignored, and so are comments beginning with a
+hash mark. Blank lines end any entry and are also ignored.
+
+Each catalog entry begins with the part number followed by a part type
+designator. 
+
+Characteristics have the form <field>=<value>, where
+the fields follow the pattern outlined below. The value is some
+description of the value of that characteristic, typically a number
+and a unit (e.g., 4.7uF) or a name (e.g., X5R).
+
+Numbers use a decimal point where necessary. Mantissas are normalized
+such that they fall into the range 1 <= n < 1000. E.g., instead of
+0.1uF, write 100nF. There is no space between number and unit. The
+Omega of Ohm is written as "R".
+
+
+Fields
+------
+
+Each 
+
+General fields
+- - - - - - -
+
+FP	Footprint
+H	Height (overrides any height implied by footprint)
+TOL	Tolerance, with percent sign. Split tolerances are indicated as n/m%
+DSC	Free-format description
+
+
+Resistors
+- - - - -
+
+RES	Part type designator
+R	Resistance, with unit
+P	Maximum power dissipation
+V	Maximum volatage
+
+
+Capacitors
+- - - - -
+
+CAP	Part type designator
+C	Capacitance, with unit
+M	Material, e.g., TANT, NP0, X5R, etc.
+V	Maximum voltage
+ESR	ESR, with unit
+
+
+Inductors
+-- - - -
+
+Diodes
+- - -
+
+DIODE	Regular diode
+STKY	Schottky diode
+
+Vf	Maximum forward voltage
+Vr	Maximum reverse voltage
+If	Maximum forward current
+Ir	Maximum reverse current
+C	Capacitance
+
+LED	Ligh-emitting diode
+
+COL	Color, multiple colors are separated by /, e.g., blue/red
+ARRAY	If multiple diodes form an array, this parameter describes
+	its structure: CA = common anode, CC = common cathode,
+	SEQ = tap-A-C-tap-A-C-tap sequence
+
+ZENER	Zener diode
+
+Vz	Zener voltage
+
+TVS	Transient voltage suppressor
+
+Vac	Working voltage, AC
+Vdc	Working voltage, DC
+E	Energy

Added: trunk/eda/boom/Makefile
===================================================================
--- trunk/eda/boom/Makefile	                        (rev 0)
+++ trunk/eda/boom/Makefile	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,19 @@
+UPLOAD=werner at sita.openmoko.org:public_html/gta02-core/
+COPY=rsync -e ssh --progress
+
+.PHONY:		all xpdf spotless upload
+
+all:
+		@echo "make what ? xpdf, upload, or spotless ?" 1>&2
+
+workflow.pdf:	workflow.fig
+		fig2dev -L pdf $< >$@ || { rm -f $@; exit 1; }
+
+xpdf:		workflow.pdf
+		xpdf workflow.pdf
+
+upload:		workflow.pdf
+		$(COPY) workflow.pdf $(UPLOAD)/bom-workflow.pdf
+
+spotless:
+		rm -f workflow.pdf

Copied: trunk/eda/boom/README (from rev 5823, trunk/gta02-core/bom/README)
===================================================================
--- trunk/eda/boom/README	                        (rev 0)
+++ trunk/eda/boom/README	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,336 @@
+The BOM processing system
+=========================
+
+The BOM processing system takes a bill of material generated by
+KiCad and converts it in various steps into a "shopping list"
+that can be used to order from various providers.
+
+
+Introduction
+============
+
+The following sections describe how to use the basic elements of
+the BOM processing system.
+
+
+A simple BOM translation
+------------------------
+
+KiCad identifies components by a so-called component reference,
+e.g., R1001, U5, etc. In addition to this, each component can have
+various parameters, such as a "value", its footprint, and further
+user-defined items. These parameters can be shown in the schematics
+(e.g., the value usually is) or they can be hidden (e.g., the
+footprint).
+
+At the end of the process, we want a "shopping list" that can be
+used to order items or to find them in an inventory or catalog.
+Components in the shopping list are identified by a part number.
+
+...
+- BOM
+- inventory
+- ID matching
+
+
+Equivalences
+------------
+
+A single component can be associated with multiple part numbers.
+For example, a chip its manufacturer calls "XYZ-R1" may be listed in
+a distributor's catalog with a completely different order number,
+such as "20-1234-8". The BOM processing system therefore
+distinguishes multiple so-called name spaces. A name space is
+identified by a (unique) name and a part number is generally
+qualified by the name of the name space.
+
+E.g., if the manufacturer is called "ACME" and the distributor of
+electronical components calls itself "DIST-EL", the part in our
+example may have the equivalent names "ACME XYZ-R1" and "DIST-EL
+20-1234-8".
+
+...
+- revise .inv
+
+example.equ:
+
+#INV
+DIST-EL 20-1234-8
+#EQU
+ACME XYZ-R1 DIST-EL 20-1234-8
+
+
+Adding stock and cost
+---------------------
+
+- .inv, more fields
+- quanta
+
+Substituting component names
+----------------------------
+
+- intro to .sub
+- ad-hoc fixes
+
+
+Selecting characteristics
+-------------------------
+
+- .sub
+- .chr
+...
+
+
+Generating characteristics
+--------------------------
+
+- .gen
+
+
+Advanced topics
+===============
+
+- generating .inv files
+- different presentations (e.g., CT, TR, ...)
+- component substitution (one-way equivalence)
+- problem reports
+- hiding known problems (while sourcing)
+
+
+File formats
+============
+
+The BOM processing system uses a large number of different files to
+store information retrieved from the BOM, inventories, intermediate
+results, etc. The following sections describe the various formats.
+
+
+Part characteristics (.chr)
+---------------------------
+
+A part characteristics file lists the parameters of components.
+This information is then matched with the parameters specified in
+the schematics.
+
+The part characteristics file begins with a line containing only
+#CHR
+
+After this, each line contains the manufacturer (namespace), the
+part number, and a list of parameter=value entries. Fields are
+separated by spaces.
+
+Long lines can be wrapped by indenting the continuation lines.
+
+Blank lines and comments (#) are ignored.
+
+
+Substitutions (.sub)
+--------------------
+
+A substitutions file specifies rules for translating component
+parameters in schematics to part characteristics.
+
+A substitution rule consists of zero or more conditions and zero or
+more assignments. The conditions are of the form field=pattern. The
+field can be a per-component fields KiCad provides or any parameter
+set by substitutions.
+
+KiCad fields are named as follows:
+
+KiCad field  Field name
+-----------  ----------
+Reference    REF (*)
+Value        VAL
+FP           Footprint
+Field1       F1
+...          ...
+
+(*) As a shortcut, REF= can be omitted.
+
+Note that fields with a user-defined name currently still only appear
+as F1, F2, etc.
+
+The special field name FN can be used to look for a match in all of
+F1, F2, ... This way, it's sufficient to use a consistent syntax for
+additional parameters, without having to assign also a fixed location
+for them. If more than one field matches, the first match is taken.
+
+Field names are case-insensitive.
+
+The pattern is uses a notation similar to filename globbing. There
+are the following special constructs:
+
+- * matches a string of any length
+- ? matches a single character
+- (...) matches the pattern between the parentheses and records the
+  string matched
+- $X marks a value in nXn notation, e.g., 4u7 or 100R. Such values
+  are converted to SI-like notation.
+
+A rule is applied when all conditions are fulfilled. In this case,
+assignments of the form field=value are executed. Strings obtained
+in the match can be included in a value as follows:
+
+- $field and ${field} are replaced by the respective field
+- $field:n and ${field:n} are replaced by the n-th (...) pattern in
+  the match of the respective field
+
+If a rule ends with an exclamation mark, the substitution process stops
+after the rule is applied. Otherwise, further rules are processed.
+
+Examples:
+
+R* val=$R -> R=$val
+
+This rule translates the values of all resistors to SI notation.
+
+D* FN=(*)Vdc -> T=TSV Vdc=FN:1
+
+This rule sets the parameters T and Vdc for Zeners acting as TSVs.
+
+If a set of rules has a common set of conditions or assignments, the
+more compact block notation can be used instead of repeating them for
+each rule:
+
+common-conditions -> common-assignments {
+    rule-specific-conditions -> rule-specific-assignments
+    ...
+}
+
+Rules in a block only match if both the common and the rule-specific
+conditions are met. Then the common and the rule-specific assignments
+are performed. If a condition or an assignment appears both in the
+common and the rule-specific part, only the latter is used.
+
+Long lines can be wrapped by indenting the continuation lines. Note
+that { and ! are also considered to be part of the same line as the
+rest of the rule. In particular, the following construct wouldn't
+work:
+
+X=Y
+{
+    ...
+}
+
+With proper indentation, this would:
+
+X=Y
+  {
+    ...
+}
+
+
+Characteristics generation (.gen)
+---------------------------------
+
+The substitution mechanism can also be used to automatically generate
+characteristics from part numbers, e.g., for resistors or capacitors.
+
+.gen files are exactly .sub files, with the exception that the only
+field used is the REF field and that it contains the part number.
+
+Once the rule set has been processed, all fields (except REF) whose
+name doesn't begin with an underscore are placed in the characteristics
+entry as parameters.
+
+An entry is only produced if the rule set is explicitly terminated.
+
+
+Parts list (.par)
+------------------
+
+A parts file lists the parts that are suitable for a given BOM item.
+The file begins with a line containing only
+#PAR
+
+After this, each line contains the component reference, a space, and
+then one or more namespace part-number groups, separated by spaces as
+well.
+
+Blank lines and comments (#) are ignored.
+
+
+Order list (.ord)
+-----------------
+
+An order file lists the quantities to order from inventories, along
+with the cost and the component references the item is used for. The
+file begins with a line containing only
+#ORD
+
+After this, each line contains the supplier (namespace), the reference
+number, the number of items to order, the currency code, the cost,
+and one or more component references.
+
+Blank lines and comments (#) are ignored.
+
+
+Equivalence (.equ)
+------------------
+
+Equivalence files establish equivalences between parts numbers in the
+same or in different name spaces. An equivalence file begins with a
+line containing only
+#EQU
+
+After this, each line consists of the following four space-separated
+fields:
+
+namespace-1 part-number-1 namespace-2 part-number-2
+
+Blank lines and comments (#) are ignored.
+
+
+Inventory (.inv)
+----------------
+
+Inventory files list inventory and component cost. An inventory file
+begins with a line containing only
+#INV
+
+After this, each line contains the namespace and the part number,
+followed by the number of items in stock, the currency code, and one
+or more pricing entries.
+
+Each pricing entry consists of two fields: the number of items in an
+order, and the per item price at that quantity. A sequence of
+increasing order sizes indicates that they are quanta. A sequence of
+decreasing order sizes indicates that smaller quanta are possible
+after a previous larger threshold has been met.
+
+Example:
+
+... USD 1 0.5 10 0.4 100 0.2
+
+Means that an order of at least 170 units would be made either as
+2 * 100 units, costing USD 40, or as 1 * 100 + 7 * 10 units, costing
+USD 20 + USD 28 = USD 48.
+
+If the entry is
+
+... USD 1 0.5 10 0.4 100 0.2 1 0.2
+
+Then the USD 0.2 per unit cost would apply to any any quantity of at
+least 100 units. So a 170 units order would cost USD 34.
+
+Blank lines and comments (#) are ignored.
+
+The number of items in stock and the pricing data can be omitted. We
+call this "virtual inventory". In this case, the numer of items in
+stock and the price default to large numbers (e.g., 999999). Virtual
+inventory is used to suppress warnings for parts that have not been
+sourced yet, but where sourcing is in progress.
+
+
+Description (.dsc)
+------------------
+
+A description file contains plain text descriptions of parts. The file
+begins with a like containing only
+#DSC
+
+Each line contains the name space, a space, the part number, another
+space, and the description. The description can contain any printable
+character and ends with a newline.
+
+Blank lines and comments (#) are ignored.

Copied: trunk/eda/boom/annotate (from rev 5826, trunk/gta02-core/bom/annotate)
===================================================================
--- trunk/eda/boom/annotate	                        (rev 0)
+++ trunk/eda/boom/annotate	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,188 @@
+#!/usr/bin/perl
+
+require "parser.pl";
+require "misc.pl";
+
+
+&parse;
+
+
+$H = 50;	# character height
+$W = $H*0.9;	# character width
+$L = $H+20;	# line skip
+
+
+sub normalize
+{
+    my @t = @_;
+
+    # convert from (x0, y0, w, h) to (x0, y0, x1, y1)
+    $t[2] += $t[0];
+    $t[3] = $t[1]-$t[3];
+    return ($t[0], $t[3], $t[2], $t[1]);
+}
+
+
+#
+# 2x2 matrix inversion
+# http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_2.C3.972_matrices
+#
+
+sub invert
+{
+    my @m = @_;
+    my $f = 1/($m[0]*$m[3]-$m[1]*$m[2]);
+    return ($f*$m[3], -$f*$m[1], -$f*$m[2], $f*$m[0]);
+}
+
+
+sub block
+{
+    my @t = &normalize(@_);
+    push(@block, [ @t ]);
+    $wnl .= "Wire Notes Line\n\t$t[0] $t[1] $t[2] $t[3]\n";
+}
+
+
+sub pass
+{
+    my @t = &normalize(@_);
+
+    for (@block) {
+	my @b = @{ $_ };
+	next if $t[0] > $b[2];
+	next if $t[2] < $b[0];
+	next if $t[1] > $b[3];
+	next if $t[3] < $b[1];
+	return 0;
+    }
+    return 1;
+}
+
+
+sub put
+{
+    local ($x0, $y0, $ref, @s) = @_;
+
+    my $h = @s*$L;
+    my $w = 0;
+    for (@s) {
+	my $t = $W*length $_;
+	$w = $t if $t > $w;
+    }
+    my $a = 270;
+    my $r = 100;
+    my $x, $y;
+    my $ym = $y0-$h+$H/2;
+    for ($i = 0; $i != 128; $i++) {
+	$x = int($x0+$r*cos($a/180*3.14159));
+	$y = int($ym+$r*sin($a/180*3.14159));
+	last if &pass($x, $y, $w, $h);
+	$a += 22.5;
+	$r += $L/8;
+    }
+    warn "no place found for \"$s[0]\"" if $i == 128;
+
+    my @m = &invert( @{ $m{$ref} });
+    &block($x, $y+$H/2, $w, $h);
+    my $n = 10;
+    for my $s (reverse @s) {
+	my $dx = $x-$x0;
+	my $dy = $y-$y0;
+	my $sx = $x0+$dx*$m[0]+$dy*$m[1];
+	my $sy = $y0+$dx*$m[2]+$dy*$m[3];
+	($hv, $hj, $vj) = ("H", "L", "C") if $m[0] == 1;
+	($hv, $hj, $vj) = ("H", "R", "C") if $m[0] == -1;
+	($hv, $hj, $vj) = ("V", "C", "B") if $m[1] == 1;
+	($hv, $hj, $vj) = ("V", "C", "T") if $m[1] == -1;
+	$s =~ s/~/-/g;
+	print "F $n \"$s\" $hv $sx $sy $H  0000 $hj ${vj}NN\n";
+	$y -= $L;
+	$n++;
+    }
+}
+
+
+#
+# pass 1: find the orientation of all parts
+#
+
+for (@eeschema) {
+    $ref = $1 if /^L \S+ (\S+)/;
+    undef $ref if /^\$EndComp/;
+    next unless /^\s+(-?[01])\s+(-?[01])\s+(-?[01])\s+(-?[01])\s*$/;
+    my @m = split(/\s+/);
+    shift @m;
+    $m{$ref} = [ @m ];
+}
+
+
+#
+# pass 2: block the spaces occupied by fields
+#
+
+for (@eeschema) {
+    $ref = $1 if /^L \S+ (\S+)/;
+    if (/^P (\d+) (\d+)/) {
+	$x0 = $1;
+	$y0 = $2;
+    }
+    next unless /^F /;
+    die "$_" unless
+      /^F \d+ "([^"]*)" ([HV]) (\d+) (\d+) (\d+) +(\d+) ([LC]) (C)/;
+    ($s, $hv, $x, $y, $size, $flag, $hj, $vj) =
+      ($1, $2, $3, $4, $5, $6, $7, $8);
+    $dx = $x-$x0;
+    $dy = $y-$y0;
+    $x = $x0+$dx*$m{$ref}[0]+$dy*$m{$ref}[1];
+    $y = $y0+$dx*$m{$ref}[2]+$dy*$m{$ref}[3];
+    next if $flag != 0;
+    $w = $size*0.8*length $s;
+    # we don't need to consider H/V
+    &block($hj eq "L" ? $x : $x-$w/2, $y+$size/2, $w, $size);
+}
+
+#
+# pass 3:
+#
+
+for (@eeschema) {
+    undef @f if /^\$Comp/;
+    if (/^L \S+ (\S+)/) {
+	$ref = $1;
+	my @p = @{ $parts{$ref} };
+	while (@p) {
+	    my @id = splice(@p, 0, 2);
+	    my $id = "$id[0] $id[1]";
+	    for ($id, &eq($id)) {
+		next unless defined $dsc{$_};
+		push(@f, $dsc{$_});
+		last;
+	    }
+	}
+    }
+    if (/^P (\d+) (\d+)/) {
+	$x = $1;
+	$y = $2;
+    }
+    if (/^\s+/) {
+	my %seen;
+	my @u = ();
+	for (@f) {
+	    next if $seen{$_};
+	    push(@u, $_);
+	    $seen{$_} = 1;
+	}
+	undef @f;
+	# $m{$ref}[0] == 1	OK
+	# $m{$ref}[0] == -1	OK
+	# $m{$ref}[1] == 1	OK
+	# $m{$ref}[1] == -1	OK (small deviations found)
+	&put($x, $y, $ref, @u) if 1 || $m{$ref}[1] == -1;
+    }
+    if (/\$EndSCHEMATC/) {
+	# uncomment for debugging
+#	print $wnl;
+    }
+    print "$_\n";
+}

Copied: trunk/eda/boom/bom2part (from rev 5819, trunk/gta02-core/bom/bom2part)
===================================================================
--- trunk/eda/boom/bom2part	                        (rev 0)
+++ trunk/eda/boom/bom2part	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,92 @@
+#!/usr/bin/perl
+
+require "parser.pl";
+require "match.pl";
+require "misc.pl";
+
+
+sub issue
+{
+    print shift(@_), " ", join(" ", @_, &eq(@_)), "\n";
+}
+
+
+if ($ARGV[0] eq "-d") {
+    $debug = 1;
+    shift @ARGV;
+}
+&parse;
+
+$total = 0;
+$bad = 0;
+
+print "#PAR\n";
+for $ref (keys %cmp) {
+    @f = @{ $cmp{$ref} };
+    $total++;
+
+    print STDERR "REF $ref\n" if $debug;
+
+    # if we're lucky, we get a direct ID match
+
+    if (defined $id{$f[0]}) {
+	print STDERR "FIRST ID\n" if $debug;
+	&issue($ref, $id{$f[0]});
+	next;
+    }
+
+    # no such luck. Let's roll up our sleeves and to the substitutions.
+
+    undef %field;
+    $field{"REF"} = $ref;
+    $field{"VAL"} = $f[0];
+    if ($f[1] eq "") {
+	print STDERR "warning: $ref ($f[0]) has no footprint\n";
+    } else {
+	$field{"FP"} = $f[1];
+    }
+    for (my $i = 1; $i != 10; $i++) {
+	$field{"F$i"} = $f[$i+1];
+    }
+    &apply_rules();
+
+    # try our luck again
+
+    if (defined $id{$field{"VAL"}}) {
+	print STDERR "SECOND ID\n" if $debug;
+	&issue($ref, $id{$field{"VAL"}});
+	next;
+    }
+
+    # still nothing. Let's match characteristics then.
+
+    my @p = ();
+    COMP: for my $c (keys %chr) {
+	print STDERR "PART $c\n" if $debug;
+	for (keys %field) {
+	    next if $_ eq "REF" || $_ eq "VAL" || $_ =~ /^F\d$/;
+	    print STDERR "  $_=",$field{$_}," " if $debug;
+	    if (!defined $chr{$c}{$_}) {
+		print STDERR "NO FIELD\n" if $debug;
+		next COMP;
+		next;
+	    }
+	    if ($chr{$c}{$_} eq $field{$_}) {
+		print STDERR "== $chr{$c}{$_}\n" if $debug;
+	    } else {
+		print STDERR "!= $chr{$c}{$_}\n" if $debug;
+		next COMP;
+	    }
+	}
+	push(@p, $c);
+    }
+    if (@p) {
+	&issue($ref, @p);
+	next;
+    }
+
+    print STDERR "unmatched: $ref (", join(", ", @f), ")\n";
+    $bad++;
+#    print join("#", ($ref, @f)), " -> $id{$f[0]}\n";
+}
+print STDERR "$bad/$total unmatched\n" if $bad;

Added: trunk/eda/boom/boom
===================================================================
--- trunk/eda/boom/boom	                        (rev 0)
+++ trunk/eda/boom/boom	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,26 @@
+#!/usr/bin/perl
+
+
+sub usage
+{
+    print STDERR "usage: $0 command [arg ...]\n";
+    exit(1);
+}
+
+
+&usage unless @ARGV;
+
+($d = $0) =~ s|/[^/]*$||;
+if ($d eq "") {
+    $p = "/";
+} elsif ($d =~ /^\//) {
+    $p = "$d";
+} else {
+    chomp($cwd = `pwd`);
+    $p = "$cwd/$d";
+}
+
+$cmd = shift @ARGV;
+$cmd = "$p/$cmd" unless $cmd =~ m|/|;
+exec("perl", "-I", $p, $cmd, @ARGV);
+die "exec perl: $!";


Property changes on: trunk/eda/boom/boom
___________________________________________________________________
Name: svn:executable
   + *

Copied: trunk/eda/boom/gen2chr (from rev 5804, trunk/gta02-core/bom/gen2chr)
===================================================================
--- trunk/eda/boom/gen2chr	                        (rev 0)
+++ trunk/eda/boom/gen2chr	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,35 @@
+#!/usr/bin/perl
+
+require "parser.pl";
+require "match.pl";
+
+
+if ($ARGV[0] eq "-d") {
+    $debug = 1;
+    shift @ARGV;
+}
+if ($ARGV[0] eq "-n") {
+    $negate = 1;
+    shift @ARGV;
+}
+$key = shift @ARGV;
+&parse;
+
+print "#CHR\n";
+for (keys %id) {
+    next unless $id{$_} eq "$key $_";
+    undef %field;
+    $field{"REF"} = $_;
+    if (!&apply_rules()) {
+	print "$id{$_}\n" if $negate;
+	next;
+    }
+    next if $negate;
+    print $id{$_};
+    for (sort keys %field) {
+	next if $_ =~ /^_/;
+	next if $_ eq "REF";
+	print " $_=$field{$_}";
+    }
+    print "\n";
+}

Copied: trunk/eda/boom/match.pl (from rev 5823, trunk/gta02-core/bom/match.pl)
===================================================================
--- trunk/eda/boom/match.pl	                        (rev 0)
+++ trunk/eda/boom/match.pl	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,200 @@
+#!/usr/bin/perl
+
+use re 'eval';
+
+
+#
+# "sub" populates the following global variables:
+#
+# $end[rule-number] = 0 / 1
+# $match[rule-number]{field}[0] = original-pattern
+# $match[rule-number]{field}[1] = RE1 
+# $match[rule-number]{field}[2] = RE2
+# $action[rule-number]{field} = value
+#
+# $match_stack[depth]{field}[0] = original-pattern
+# $match_stack[depth]{field}[1] = RE1
+# $match_stack[depth]{field}[2] = RE2
+# $action_stack[depth]{field} = value
+# $may_cont = 0 / 1
+# $last
+# $last_action
+#
+
+#
+# $cvn_from{internal-handle} = index
+# $cvn_to{internal-handle} = index
+# $cvn_unit{internal-handle} = unit-name
+# $cvn_num = internal-handle
+# $found{field-or-subfield} = string
+
+
+#
+# We convert each input pattern into two regular expressions: the first matches
+# units in the nXn notation, e.g., 4u7 or 100R. The second matches them in SI
+# notation (sans space).
+#
+# When matching (sub_match), we first apply the first expression. Each time we
+# encounter a unit ($R, $F, etc.), __cvn is called. __cvn stores the index of
+# the unit in %cvn_from and %cvn_to.
+#
+# We then pick these substrings from the input string and convert the units to
+# SI notation. At the same time, we normalize the mantissa. Once done, we run
+# the second expression. This one always matches (hopefully :-)
+#
+# All (...) ranges in the original pattern have been replaced with named
+# capture buffers in the second expression, so all these subfields are now
+# gathered in the $+ array. (The same also happened in the first pass, but we
+# ignore it.)
+#
+# Finally, when expanding a value (sub_expand), we look for $field and
+# $field:index, and expand accordingly.
+#
+
+
+sub __cvn
+{
+    local ($num) = @_;
+
+    $cvn_from{$num} = $-[$#-];
+    $cvn_to{$num} = $+[$#+];
+}
+
+
+sub sub_match
+{
+    local ($s, $field, $m1, $m2) = @_;
+
+    #
+    # Perform the first match and record where we saw $<unit> patterns.
+    #
+    undef %cvn_from;
+    undef %cvn_to;
+    return undef unless $s =~ $m1;
+
+    #
+    # Convert the unit patterns to almost-SI notation. (We don't put a space
+    # after the number, but the rest is SI-compliant.)
+    #
+    my $off = 0;
+    for (keys %cvn_from) {
+	my $unit = $cvn_unit{$_};
+	my $from = $cvn_from{$_}+$off;
+	my $len = $cvn_to{$_}-$cvn_from{$_};
+	die unless substr($s, $from, $len) =~
+	    /(\d+)$unit(\d*)|(\d+)([GMkmunpf])(\d*)/;
+
+	#
+	# Normalize to \d+.\d*
+	#
+	my $v = "$1$3.$2$5";
+	my $exp = $4 eq "" ? " " : $4;
+
+	#
+	# Remove leading zeroes.
+	#
+	$v =~ s/^0*(\d+)/\1/;
+
+	#
+	# Mantissa must be < 1000.
+	# Do the math as string operation to avoid rounding errors.
+	#
+	while ($v =~ /(\d+)(\d{3})\./) {
+	    $v = "$1.$2$'";
+	    $exp =~ tr/GMk munpf/TGMk munp/;
+	}
+
+	#
+	# Mantissa must be >= 1.
+	#
+	while ($v =~ /\b0\.(\d+)/) {
+	    if (length $1 < 3) {
+		$v = $1.("0" x (3-length $1)).".";
+	    } else {
+		$v = substr($1, 0, 3).".".substr($1, 3);
+	    }
+	    $exp =~ tr/GMk munpf/Mk munpa/;
+	}
+
+	#
+	# Remove trailing zeroes
+	#
+	$v =~ s/(\.[1-9]*)0*/\1/;
+
+	$exp =~ s/ //;
+	$v =~ s/\.$//;
+	$v = $v.$exp.$unit;
+	$off += length($v)-$len;
+	substr($s, $from, $len, $v);
+    }
+
+    #
+    # Run the second match on the string to process any (...) patterns
+    #
+    $found{$field} = $s;
+    die $m2 unless $s =~ $m2;
+    for (keys %+) {
+	$found{$_} = $+{$_};
+    }
+    return $s;
+}
+
+
+sub sub_expand
+{
+    local ($s) = @_;
+
+    while ($s =~ /^([^\$]*)\$([A-Za-z_]\w*)(:(\d+))?|^([^\$]*)\${([A-Za-z_]\w*)(:(\d+))?}/) {
+	my $name = "$2$6";
+	$name .= "__$4$8" if defined($4) || defined($8);
+	die "don't know \"$name\"" unless defined $found{$name};
+	$s = $1.$5.$found{$name}.$';
+    }
+    return $s;
+}
+
+
+#
+# return 0 if all rules have been exhausted, 1 if there was an explicit halt.
+#
+
+sub apply_rules
+{
+    RULE: for (my $i = 0; $i <= $#match; $i++) {
+	print STDERR "RULE #$i\n" if $debug;
+	%found = %field;
+	FIELD: for my $f (keys %{ $match[$i] }) {
+	    my @f = $f ne "FN" ? ($f) :
+	      ("F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9");
+	    for (@f) {
+		print STDERR "  MATCH $_=$match[$i]{$f}[0] " if $debug;
+		if (!defined $found{$_}) {
+		    print STDERR "NO FIELD\n" if $debug;
+		    next;
+		}
+		print STDERR "FIELD $found{$_} " if $debug;
+		if (!defined &sub_match($found{$_}, $f,
+		  $match[$i]{$f}[1], $match[$i]{$f}[2])) {
+		    print STDERR "MISS\n" if $debug;
+		    next;
+		}
+		print STDERR "MATCH\n" if $debug;
+		next FIELD;
+	    }
+	    next RULE;
+	}
+	for (keys %{ $action[$i] }) {
+	    my $s = &sub_expand($action[$i]{$_});
+	    print STDERR "  SET $_=$action[$i]{$_} => $s\n" if $debug;
+	    $field{$_} = $s;
+	}
+	if ($end[$i]) {
+	    print STDERR "  END\n" if $debug;
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+
+return 1;

Copied: trunk/eda/boom/misc.pl (from rev 5819, trunk/gta02-core/bom/misc.pl)
===================================================================
--- trunk/eda/boom/misc.pl	                        (rev 0)
+++ trunk/eda/boom/misc.pl	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,26 @@
+#!/usr/bin/perl
+
+
+#
+# determine the equivalent parts, taking into account that %eq is transitive
+#
+
+sub eq
+{
+    my %seen;
+    my @p = @_;	# parts to consider
+    my @r = ();	# new equivalences we've found
+    my $skip = @p;
+
+    while (@p) {
+	my $p = shift @p;
+	next if $seen{$p};
+	$seen{$p} = 1;
+	push(@r, $p) if $skip-- <= 0;
+	push(@p, @{ $eq{$p} });
+    }
+    return @r;
+}
+
+
+return 1;

Added: trunk/eda/boom/pardup.pl
===================================================================
--- trunk/eda/boom/pardup.pl	                        (rev 0)
+++ trunk/eda/boom/pardup.pl	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,16 @@
+#!/usr/bin/perl
+while (<>) {
+    @f = split(/\s+/);
+    $ref = shift @f;
+    for ($i = 0; $i != @f; $i++) {
+	next unless $f[$i] eq "FIC" || $f[$i] eq "MISSING" ||
+	  $f[$i] eq "DIGI-KEY";
+	splice(@f, $i, 2);
+	$i--;
+    }
+    next if @f < 3;
+    push(@{ $multi{join(" ", @f)} }, $ref);
+}
+for (sort keys %multi) {
+    print "$_ -- ", join(" ", @{ $multi{$_} }), "\n";
+}


Property changes on: trunk/eda/boom/pardup.pl
___________________________________________________________________
Name: svn:executable
   + *

Copied: trunk/eda/boom/parser.pl (from rev 5825, trunk/gta02-core/bom/parser.pl)
===================================================================
--- trunk/eda/boom/parser.pl	                        (rev 0)
+++ trunk/eda/boom/parser.pl	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,392 @@
+#!/usr/bin/perl
+
+use re 'eval';
+
+
+sub skip
+{
+    # do nothing
+}
+
+
+#
+# "bom" populates the following global variable:
+#
+# $cmp{component-reference}[0] = value
+# $cmp{component-reference}[1] = footprint
+# $cmp{component-reference}[2] = field1
+# ...
+#
+
+sub bom
+{
+    if (/^#End Cmp/) {
+	$mode = *skip;
+	return;
+    }
+    die unless /^\|\s+(\S+)\s+/;
+    my $ref = $1;
+    my @f = split(/\s*;\s*/, $');
+    next if $f[0] eq "NC";
+    $cmp{$ref} = [ @f ];
+}
+
+
+#
+# "equ" populates the following global variables:
+#
+# $id{item-number} = "namespace item-number"
+#   This is used for heuristics that look up parts commonly referred to by
+#   their part number.
+#
+# $eq{"namespace0 item-number0"}[] = ("namespace1 item-number1", ...)
+#   List of all parts a given part is equivalent to.
+#
+
+sub equ
+{
+    my @f = split(/\s+/);
+    my $a = "$f[0] $f[1]";
+    my $b = "$f[2] $f[3]";
+    $id{$f[1]} = $a;
+    $id{$f[3]} = $b;
+    push @{ $eq{$a} }, $b;
+    push @{ $eq{$b} }, $a;
+}
+
+
+#
+# "inv" populates the following global variables:
+#
+# $id{item-number} = "namespace item-number"
+#   This is used for heuristics that look up parts commonly referred to by
+#   their part number.
+#
+# $inv{"namespace item-number"}[0] = items-in-stock
+# $inv{"namespace item-number"}[1] = currency
+# $inv{"namespace item-number"}[2] = order-quantity
+# $inv{"namespace item-number"}[3] = unit-price
+#   [2] and [3] may repeat.
+#
+
+sub inv
+{
+    my @f = split(/\s+/);
+    my $id = "$f[0] $f[1]";
+    shift @f;
+    my $ref = shift @f;
+    die "duplicate inventory entry for \"$id\"" if defined $inv{$id};
+    $id{$ref} = $id;
+    $inv{$id} = [ @f ];
+    $inv{$id}[0] = 999999 unless defined $inv{$id}[0];
+    $inv{$id}[1] = "N/A" unless defined $inv{$id}[1];
+    $inv{$id}[2] = 1 unless defined $inv{$id}[2];
+    $inv{$id}[3] = 999999 unless defined $inv{$id}[3];
+}
+
+
+#
+# "par" populates the following global variables:
+#
+# $parts{component-ref}[0] = namespace
+# $parts{component-ref}[1] = item-number
+# [0] and [1] may repeat
+#
+# $want{"namespace item"} = number of times we may use the part. If multiple
+#   parts are eligible for a component, each of them is counted as desirable
+#   for each component.
+#
+# $comps{"namespace item"}{component-ref} = 1
+#   Set of components a part may be used for.
+#
+
+sub par
+{
+    my @f = split(/\s+/);
+    my $ref = shift @f;
+    $parts{$ref} = [ @f ];
+    while (@f) {
+	my @id = splice(@f, 0, 2);
+	my $id = "$id[0] $id[1]";
+	$want{$id}++;
+	$comps{$id}{$ref} = 1;
+    }
+}
+
+
+#
+# "chr" populates the following global variable:
+#
+# $chr{"namespace item-number"}{parameter} = value
+#
+# $last is used internally for continuation lines.
+#
+
+sub chr
+{
+    my @f;
+    if (/^\s+/) {
+	@f = split(/\s+/, $');
+    } else {
+	@f = split(/\s+/);
+	my $ref = shift @f;
+	my $num = shift @f;
+	$last = "$ref $num";
+    }
+    for (@f) {
+	die "\"=\" missing in $_" unless /=/;
+	$chr{$last}{uc($`)} = $';
+    }
+}
+
+
+#
+# "sub" populates the following global variables:
+#
+# $end[rule-number] = 0 / 1
+# $match[rule-number]{field}[0] = original-pattern
+# $match[rule-number]{field}[1] = RE1 
+# $match[rule-number]{field}[2] = RE2
+# $action[rule-number]{field} = value
+#
+# $match_stack[depth]{field}[0] = original-pattern
+# $match_stack[depth]{field}[1] = RE1
+# $match_stack[depth]{field}[2] = RE2
+# $action_stack[depth]{field} = value
+# $may_cont = 0 / 1
+# $last
+# $last_action
+#
+
+#
+# $cvn_from{internal-handle} = index
+# $cvn_to{internal-handle} = index
+# $cvn_unit{internal-handle} = unit-name
+# $cvn_num = internal-handle
+# $found{field-or-subfield} = string
+
+
+sub sub_pattern
+{
+    local ($field, $p) = @_;
+    my $n = 0;
+    $p =~ s/\./\\./g;
+    $p =~ s/\+/\\+/g;
+    $p =~ s/\?/./g;
+    $p =~ s/\*/.*/g;
+    my $tmp = "";
+    while ($p =~ /^([^\(]*)\(/) {
+	$n++;
+	$tmp .= "$1(?'${field}__$n'";
+	$p = $';
+    }
+    $p = "^".$tmp.$p."\$";
+    my $q = $p;
+    while ($p =~ /^([^\$]*)\$(.)/) {
+	$p = "$1(\\d+$2\\d*|\\d+[GMkmunpf$2]\\d*)(?{ &__cvn($cvn_num); })$'";
+	$cvn_unit{$cvn_num} = $2;
+	die unless $q =~ /^([^\$]*)\$(.)/;
+	$q = "$1(\\d+(\.\\d+)?[GMkmunpf]?$2)$'";
+	$cvn_num++;
+    }
+    return ($p, $q);
+}
+
+
+sub sub_value
+{
+    return $_[0];
+}
+
+
+sub sub
+{
+    /^(\s*)/;
+    my $indent = $1;
+    my @f = split(/\s+/, $');
+    my $f;
+    my $in = 0;		# indentation level
+    while (length $indent) {
+	my $c = substr($indent, 0, 1, "");
+	if ($c eq " ") {
+	    $in++;
+	} elsif ($c eq "\t") {
+	    $in = ($in+8) & ~7;
+	} else {
+	    die;
+	}
+    }
+    if ($may_cont && $in > $last) {
+	pop(@match);
+	pop(@action);
+	pop(@end);
+    } else {
+	$match_stack[0] = undef;
+	$action_stack[0] = undef;
+	$last_action = 0;
+	$last = $in;
+    }
+    if (!$last_action) {
+	while (@f) {
+	    $f = shift @f;
+	    last if $f eq "->" || $f eq "{" || $f eq "}" || $f eq "!";
+	    if ($f =~ /=/) {
+		$match_stack[0]{uc($`)} = [ $', &sub_pattern(uc($`), $') ];
+	    } else {
+		$match_stack[0]{"REF"} = [ &sub_pattern("REF", $f) ];
+	    }
+	}
+	$last_action = 1 if $f eq "->";
+    }
+    if ($last_action) {
+	while (@f) {
+	    $f = shift @f;
+	    last if $f eq "{" || $f eq "!";
+	    die unless $f =~ /=/;
+	    $action_stack[0]{uc($`)} = &sub_value($');
+	}
+    }
+    $may_cont = 0;
+    if ($f eq "{") {
+	unshift(@match_stack, undef);
+	unshift(@action_stack, undef);
+	die "items following {" if @f;
+    } elsif ($f eq "}") {
+	shift @match_stack;
+	shift @action_stack;
+	die "items following }" if @f;
+    } else {
+	die "items following !" if @f && $f eq "!";
+	push(@end, $f eq "!");
+	$may_cont = $f ne "!";
+	my $n = $#end;
+	push(@match, undef);
+	push(@action, undef);
+	for my $m (reverse @match_stack) {
+	    for (keys %{ $m }) {
+		$match[$n]{$_} = $m->{$_};
+	    }
+	}
+	for my $a (reverse @action_stack) {
+	    for (keys %{ $a }) {
+		$action[$n]{$_} = $a->{$_};
+	    }
+	}
+    }
+}
+
+
+#
+# "ord" populates the following global variables:
+#
+# $order{"namespace item-number"}[0] = quantity to order
+# $order{"namespace item-number"}[1] = currency
+# $order{"namespace item-number"}[2] = total cost in above currency
+# $order{"namespace item-number"}[3] = component reference
+# ...
+#
+
+sub ord
+{
+    my @f = split(/\s+/);
+    my @id = splice(@f, 0, 2);
+    @{ $order{"$id[0] $id[1]"} } = @f;
+}
+
+
+#
+# "dsc" populates the following global variable:
+#
+# $dsc{"namespace item-number"} = description
+#
+
+sub dsc
+{
+    my @f = split(/\s+/);
+    my @id = splice(@f, 0, 2);
+    $dsc{"$id[0] $id[1]"} = join(" ", @f);
+}
+
+
+#
+# "eeschema" populates the following global variable:
+#
+# $eeschema[] = line
+#
+
+
+sub eeschema
+{
+    push(@eeschema, $_[0]);
+    if ($_[0] =~ /^\$EndSCHEMATC/) {
+	$mode = *skip;
+	undef $raw;
+    }
+}
+
+
+sub parse
+{
+    $mode = *skip;
+    while (<>) {
+	chop;
+	if (/^#Cmp.*order = Reference/) {
+	    $mode = *bom;
+	    next;
+	}
+	if (/^#Cmp.*order = Value/) {
+	    $mode = *skip;
+	    next;
+	}
+	if (/^eeschema \(/) {	# hack to allow loading in any order
+	    $mode = *skip;
+	    next;
+	}
+	if (/^EESchema Schematic/) {
+	    $mode = *eeschema;
+	    $raw = 1;
+	    die "only one schematic allowed" if defined @eeschema;
+	    &eeschema($_);
+	    next;
+	}
+	if (/^#EQU\b/) {
+	    $mode = *equ;
+	    next;
+	}
+	if (/^#INV\b/) {
+	    $mode = *inv;
+	    next;
+	}
+	if (/^#PAR\b/) {
+	    $mode = *par;
+	    next;
+	}
+	if (/^#CHR\b/) {
+	    $mode = *chr;
+	    undef $last;
+	    next;
+	}
+	if (/^#(SUB|GEN)\b/) {
+	    $mode = *sub;
+	    undef $last;
+	    undef $last_action;
+	    undef $may_cont;
+	    next;
+	}
+	if (/^#ORD\b/) {
+	    $mode = *ord;
+	    next;
+	}
+	if (/^#DSC\b/) {
+	    $mode = *dsc;
+	    next;
+	}
+	if (!$raw) {
+	    s/#.*//;
+	    next if /^\s*$/;
+	}
+	&$mode($_);
+    }
+}
+
+return 1;

Copied: trunk/eda/boom/part2order (from rev 5819, trunk/gta02-core/bom/part2order)
===================================================================
--- trunk/eda/boom/part2order	                        (rev 0)
+++ trunk/eda/boom/part2order	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,133 @@
+#!/usr/bin/perl
+
+require "parser.pl";
+require "misc.pl";
+
+$mult = shift(@ARGV);
+&parse;
+
+
+sub number
+{
+    local ($id) = @_;
+
+    my $s = $inv{$id}[0];
+    my $n = $want{$id}*$mult;
+    return $n < $s ? $n : $s;
+
+}
+
+
+#
+# The heuristics here aren't very nice. We give zero-cost stock priority over
+# any other stock, when we go by stock size up to the quantity we need. The
+# idea is to exhause local stock (zero-cost) first, then try to obtain the
+# parts with as few orders as possible.
+#
+# It would be better to have some sort of priority, so that we can express a
+# preference among stock we already own. Also, if non-zero-cost stock has widly
+# different prices, the smallest order cost may not be a good indicator of
+# which source we prefer.
+#
+# Furthermore, the algorithm doesn't consider the number of sources we use in
+# total or things like lead time, shipping cost, customs, etc.
+#
+
+sub rank
+{
+    local ($a, $b) = @_;
+
+    my $na = &number($a);	# min(number wanted, available)
+    my $nb = &number($b);
+    my $pa = $inv{$a}[3];	# per unit price for smallest quantum
+    my $pb = $inv{$b}[3];
+
+#print STDERR "a=$a b=$b na=$na nb=$nb pa=$pa pb=$pb\n";
+    return 1 if $na && !$pa && $pb;
+    return -1 if $nb && $pa && !$pb;
+    return $na <=> $nb if $na != $nb;
+    return $pb <=> $pa;
+}
+
+
+for (keys %parts) {
+    $parts++;
+}
+
+print "#ORD\n";
+for (sort { &rank($b, $a) } keys %want) {
+    my $n = &number($_);
+    $n -= $n % $mult;
+    next unless $n;
+    my @f = @{ $inv{$_} };
+    my $max = shift @f;
+    my $currency = shift @f;
+    my @qty;
+    my @price;
+    my %index;
+    my $best_qty;
+    my $best_price = undef;
+    while (@f) {
+	my $q = shift @f;
+	my $p = shift @f;
+	if (defined $index{$q}) {
+	    $price[$index{$q}] = $p;
+	} else {
+	    push(@qty, $q);
+	    push(@price, $p);
+	    $index{$q} = $#qty;
+	    # @@@ this fails if smaller quantities following a large quantity
+	    # differ from the quantities preceding them. E.g., 1 10 100 25
+	    # wouldn't yield correct results.
+	}
+	for (my $i = $#qty; $i >= 0; $i--) {
+	    my $order = 0;
+	    my $price = 0;
+	    my $left = $n;
+	    for (my $j = $#qty; $j >= $i; $j--) {
+		while ($left >= ($j == $i ? 1 : $qty[$j])) {
+		    $left -= $qty[$j];
+		    $order += $qty[$j];
+		    $price += $price[$j]*$qty[$j];
+		}
+	    }
+	    next if $order > $max;
+	    if (!defined $best_price || $price < $best_price) {
+		$best_price = $price;
+		$best_qty = $order;
+	    }
+	}
+    }
+    next if !defined $best_price;
+    print "$_ $best_qty $currency $best_price";
+    my $id = $_;
+    while (keys %{ $comps{$id} }) {
+	last if $best_qty < $mult;
+	$best_qty -= $mult;
+	my $ref = (sort keys %{ $comps{$id} })[0];
+#print STDERR "$id: $ref + ", join("|", keys %{ $comps{$id} }), "\n";
+	my @f = @{ $parts{$ref} };
+	while (@f) {
+	    my @id2 = splice(@f, 0, 2);
+	    my $id2 = "$id2[0] $id2[1]";
+	    $want{$id2}--;
+	    delete $comps{$id2}{$ref};
+	}
+	print " $ref";
+    }
+    print "\n";
+}
+
+for my $id (sort { $want{$b} <=> $want{$a} } keys %want) {
+    next unless $want{$id};
+    print STDERR "$id";
+    for (&eq($id)) {
+#	next unless $want{$_};
+	die "\n$_ ($want{$_}) vs. $id want ($want{$id})"
+	  unless $want{$_} == $want{$id};
+	print STDERR " $_";
+	$want{$_} = 0;
+    }
+    print STDERR ": want $want{$id}\n";
+    $want{$id} = 0;
+}

Copied: trunk/eda/boom/prettyord (from rev 5820, trunk/gta02-core/bom/prettyord)
===================================================================
--- trunk/eda/boom/prettyord	                        (rev 0)
+++ trunk/eda/boom/prettyord	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,49 @@
+#!/usr/bin/perl
+
+require "parser.pl";
+require "misc.pl";
+
+&parse;
+
+$out[0][0] = "Pos";
+$out[1][0] = "Qty";
+$out[2][0] = "P/N";
+$out[3][0] = "Description";
+$out[4][0] = "Value";
+$out[5][0] = "";
+
+for (sort keys %order) {
+    push(@{ $out[0] }, ++$n);
+    push(@{ $out[1] }, $order{$_}[0]);
+    @f = split(/\s+/, $_);
+    push(@{ $out[2] }, $f[1]);
+    my $dsc = $dsc{$_};
+    if (!defined $dsc) {
+	for (&eq($_)) {
+	    $dsc = $dsc{$_};
+	    last if defined $dsc;
+	}
+    }
+    print STDERR "$_: no description\n" unless defined $dsc;
+    push(@{ $out[3] }, defined $dsc ? $dsc : "???");
+    push(@{ $out[4] }, $order{$_}[1]);
+    push(@{ $out[5] }, sprintf("%.2f", $order{$_}[2]));
+}
+
+for (@out) {
+    push(@max, 0);
+    for (@{ $_ }) {
+	$max[$#max] = length $_ if length $_ > $max[$#max];
+    }
+}
+
+for ($i = 0; $i <= $#{ $out[0] }; $i++) {
+    for ($j = 0; $j != 6; $j++) {
+	my $s = $out[$j][$i];;
+	print $s if $j == 2 || $j == 3 || $j == 4;
+	print " " x ($max[$j]-length $s);
+	print $s if $j == 0 || $j == 1 || $j == 5;
+	print "  " unless $j == 5;
+    }
+    print "\n";
+}

Copied: trunk/eda/boom/test (from rev 5804, trunk/gta02-core/bom/test)

Copied: trunk/eda/boom/workflow.fig (from rev 5816, trunk/gta02-core/bom/workflow.fig)
===================================================================
--- trunk/eda/boom/workflow.fig	                        (rev 0)
+++ trunk/eda/boom/workflow.fig	2010-02-07 14:33:40 UTC (rev 5827)
@@ -0,0 +1,147 @@
+#FIG 3.2  Produced by xfig version 3.2.5a
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+6 450 5850 3150 6525
+4 0 0 50 -1 14 12 0.0000 4 180 2700 450 6030 Source file (in SVN)\001
+4 0 0 50 -1 12 12 0.0000 4 150 1890 450 6255 Generated file\001
+4 0 0 50 -1 18 12 0.0000 4 210 1830 450 6480 Program (in SVN)\001
+-6
+6 450 7875 6480 9000
+4 0 0 50 -1 12 12 0.0000 4 105 540 450 8055 .csv\001
+4 0 0 50 -1 12 12 0.0000 4 150 540 450 8280 .inv\001
+4 0 0 50 -1 12 12 0.0000 4 150 540 450 8505 .equ\001
+4 0 0 50 -1 12 12 0.0000 4 150 540 450 8730 .par\001
+4 0 0 50 -1 12 12 0.0000 4 150 540 450 8955 .ord\001
+4 0 0 50 -1 1 12 0.0000 4 195 2520 1350 8280 inventory with stock and cost\001
+4 0 0 50 -1 1 12 0.0000 4 195 3045 1350 8055 GTA02 EE component stock at FIC\001
+4 0 0 50 -1 1 12 0.0000 4 195 5130 1350 8505 part number equivalences, e.g., manufacturer vs. distributor\001
+4 0 0 50 -1 1 12 0.0000 4 195 2910 1350 8730 component to part number(s) map\001
+4 0 0 50 -1 1 12 0.0000 4 195 5025 1350 8955 list of parts to order, with price and component references\001
+-6
+6 450 7425 3465 7875
+4 0 0 50 -1 12 12 0.0000 4 150 540 450 7605 .chr\001
+4 0 0 50 -1 12 12 0.0000 4 150 540 450 7830 .sub\001
+4 0 0 50 -1 1 12 0.0000 4 195 1695 1350 7605 part characteristics\001
+4 0 0 50 -1 1 12 0.0000 4 195 2085 1350 7830 parameter substitutions\001
+-6
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
+	1 1 2.00 60.00 60.00
+	 7875 2700 7875 2925 6525 2925 6525 4725
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
+	1 1 2.00 60.00 60.00
+	 9000 2700 9000 5850 6525 5850 6525 6075
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
+	1 1 2.00 60.00 60.00
+	 3150 1350 3150 5850 6075 5850 6075 6075
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 6300 2700 6300 4725
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 6300 2025 6300 2475
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 6300 1350 6300 1800
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
+	1 1 2.00 60.00 60.00
+	 4950 1350 4950 4275 6075 4275 6075 4725
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
+	1 1 2.00 60.00 60.00
+	 3150 4500 5850 4500 5850 4725
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
+	1 1 2.00 60.00 60.00
+	 8100 4275 8100 4500 6750 4500 6750 4725
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 11700 1575 11700 3150 10170 3150
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 6300 4950 6300 5400
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 6300 5625 6300 6075
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 6300 6300 6300 6750
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 6300 6975 6300 7425
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 6525 7650 6525 8100
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 9000 1350 9000 1800
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
+	1 1 2.00 60.00 60.00
+	 9000 1575 7875 1575 7875 1800
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
+	1 1 2.00 60.00 60.00
+	 9000 1575 10125 1575 10125 1800
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 7875 2025 7875 2475
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 9000 2025 9000 2475
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 7875 2925 7875 3375
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
+	1 1 2.00 60.00 60.00
+	 8955 3150 8325 3150 8325 3375
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 8100 3600 8100 4050
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 10125 2025 10125 2475
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
+	1 1 2.00 60.00 60.00
+	 10125 2700 10125 7200 6750 7200 6750 7425
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 2.00 60.00 60.00
+	 8955 4500 8100 4500
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 9045 3150 10080 3150
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 11700 4050 11700 4500 10170 4500
+2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 10080 4500 9045 4500
+4 0 0 50 -1 2 16 0.0000 4 255 3345 450 675 BOM Processing - Workflow\001
+4 1 0 50 -1 12 12 0.0000 4 195 1890 6300 2655 gta02-core.lst\001
+4 1 0 50 -1 18 12 0.0000 4 165 1035 6300 1980 eeschema\001
+4 1 0 50 -1 14 12 0.0000 4 150 675 6300 1305 *.sch\001
+4 1 0 50 -1 18 12 0.0000 4 210 990 6300 4905 bom2part\001
+4 1 0 50 -1 18 12 0.0000 4 210 1110 6300 6255 part2order\001
+4 1 0 50 -1 12 12 0.0000 4 195 1890 6300 6930 gta02-core.ord\001
+4 1 0 50 -1 12 12 0.0000 4 195 1890 6300 5580 gta02-core.par\001
+4 1 0 50 -1 14 12 0.0000 4 195 1890 4950 1305 gta02-core.sub\001
+4 1 0 50 -1 14 12 0.0000 4 195 1890 3105 1305 gta02-core.inv\001
+4 1 0 50 -1 18 12 0.0000 4 210 975 6525 7605 prettyord\001
+4 1 0 50 -1 18 12 0.0000 4 165 1095 7875 1980 fic2vendor\001
+4 1 0 50 -1 18 12 0.0000 4 165 675 9000 1980 fic2inv\001
+4 1 0 50 -1 18 12 0.0000 4 210 855 8100 3555 gen2chr\001
+4 1 0 50 -1 12 12 0.0000 4 150 675 8100 4230 *.chr\001
+4 1 0 50 -1 12 12 0.0000 4 195 945 7875 2655 fic.equ\001
+4 1 0 50 -1 12 12 0.0000 4 150 945 9000 2655 fic.inv\001
+4 1 0 50 -1 18 12 0.0000 4 165 735 10125 1980 fic2dsc\001
+4 1 0 50 -1 14 12 0.0000 4 195 2700 9000 1305 inventory-fic-ee.csv\001
+4 0 0 50 -1 12 12 0.0000 4 150 540 450 7155 .lst\001
+4 0 0 50 -1 1 12 0.0000 4 195 2220 1350 7155 BOM generated by KiCad\001
+4 0 0 50 -1 12 12 0.0000 4 150 540 450 6930 .sch\001
+4 0 0 50 -1 1 12 0.0000 4 195 2010 1350 6930 schematics (for KiCad)\001
+4 0 0 50 -1 12 12 0.0000 4 150 540 450 7380 .gen\001
+4 0 0 50 -1 1 12 0.0000 4 195 2745 1350 7380 characteristics generation rules\001
+4 0 0 50 -1 12 12 0.0000 4 150 540 450 9180 .dsc\001
+4 0 0 50 -1 1 12 0.0000 4 195 2685 1350 9180 Textual component description\001
+4 1 0 50 -1 14 12 0.0000 4 180 675 11700 1305 *.gen\001
+4 1 0 50 -1 14 12 0.0000 4 150 675 11700 3780 *.chr\001
+4 1 0 50 -1 14 9 0.0000 4 135 1440 11700 4005 (acx, misc, ...)\001
+4 1 0 50 -1 14 9 0.0000 4 135 1800 11700 1530 (darfon, ralec, ...)\001
+4 1 0 50 -1 12 12 0.0000 4 150 945 10125 2655 fic.dsc\001

Deleted: trunk/gta02-core/bom/CHARACTERISTICS
===================================================================
--- trunk/gta02-core/bom/CHARACTERISTICS	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/CHARACTERISTICS	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,140 +0,0 @@
-*** This is an older draft of the concept - differs sometimes from the way
-    things are done in gta02-core. ***
-
-
-BOM matching
-============
-
-BOMs are matched with inventories in the following way:
-
-- a .lst file with the BOM is generated by KiCad
-
-- using a ruleset, component characteristics are translated to a
-  canonical format and default values may be used for unspecified
-  characteristics
-
-- part catalogs are searched for matches with the canonical component
-  descriptions. This yields a list of supplier-specific part numbers
-  for each component.
-
-  Parts can be characterized by either specifying their properties or
-  by equating them to another part. E.g., a Digi-Key part may be
-  defined as an NXP part which in turn is equivalent to a TI part.
-
-- this list is then matched against inventories, using a suitable
-  optimization strategy (e.g., prioritize inventories and try to
-  pick as many suitable components as possible higher priority ones
-  before moving to lower priority ones)
-
-  E.g., local stock could be the first-level inventory, followed by
-  more distant warehouses, followed by distributors, followed by
-  manufacturers.
-
-  Inventories could also include pricing information.
-
-- TBD: it would be good if parameters gathered in the matching process
-  could be fed back into KiCad (as some sort of annotations, similar
-  to the expanded view of schematic symbols), such that under-specifed
-  parts yielding mismatches can be spotted by manual review.
-
-
-Catalog
-=======
-
-A catalog contains part characteristics and the reference number(s)
-assigned to them.
-
-
-Basic syntax
-------------
-
-Catalog entries consist of "words" in the sense that each word does
-not contain any whitespace and words are separated from each other by
-whitespace. Whitespace can be included in a word if it is enclosed in
-double quotes.
-
-Each entry begins in the first column of a line. If an entry needs
-more than one line, the words on the continuation line(s) must be
-indented by whitespace.
-
-Trailing whitespace is ignored, and so are comments beginning with a
-hash mark. Blank lines end any entry and are also ignored.
-
-Each catalog entry begins with the part number followed by a part type
-designator. 
-
-Characteristics have the form <field>=<value>, where
-the fields follow the pattern outlined below. The value is some
-description of the value of that characteristic, typically a number
-and a unit (e.g., 4.7uF) or a name (e.g., X5R).
-
-Numbers use a decimal point where necessary. Mantissas are normalized
-such that they fall into the range 1 <= n < 1000. E.g., instead of
-0.1uF, write 100nF. There is no space between number and unit. The
-Omega of Ohm is written as "R".
-
-
-Fields
-------
-
-Each 
-
-General fields
-- - - - - - -
-
-FP	Footprint
-H	Height (overrides any height implied by footprint)
-TOL	Tolerance, with percent sign. Split tolerances are indicated as n/m%
-DSC	Free-format description
-
-
-Resistors
-- - - - -
-
-RES	Part type designator
-R	Resistance, with unit
-P	Maximum power dissipation
-V	Maximum volatage
-
-
-Capacitors
-- - - - -
-
-CAP	Part type designator
-C	Capacitance, with unit
-M	Material, e.g., TANT, NP0, X5R, etc.
-V	Maximum voltage
-ESR	ESR, with unit
-
-
-Inductors
--- - - -
-
-Diodes
-- - -
-
-DIODE	Regular diode
-STKY	Schottky diode
-
-Vf	Maximum forward voltage
-Vr	Maximum reverse voltage
-If	Maximum forward current
-Ir	Maximum reverse current
-C	Capacitance
-
-LED	Ligh-emitting diode
-
-COL	Color, multiple colors are separated by /, e.g., blue/red
-ARRAY	If multiple diodes form an array, this parameter describes
-	its structure: CA = common anode, CC = common cathode,
-	SEQ = tap-A-C-tap-A-C-tap sequence
-
-ZENER	Zener diode
-
-Vz	Zener voltage
-
-TVS	Transient voltage suppressor
-
-Vac	Working voltage, AC
-Vdc	Working voltage, DC
-E	Energy

Modified: trunk/gta02-core/bom/Makefile
===================================================================
--- trunk/gta02-core/bom/Makefile	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/Makefile	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,37 +1,37 @@
 UPLOAD=werner at sita.openmoko.org:public_html/gta02-core/
 COPY=rsync -e ssh --progress
 
+BOOM=../../eda/boom/boom
+
 KITS=20
 EQU=fic/fic.equ fic/missing.equ digi-key.equ
 INV=fic/fic.inv gta02-core.inv dummy.inv fic/missing.inv digi-key.inv
 CHR=darfon.chr ralec.chr avx.chr acx.chr misc.chr
 SCH=audio bt cpu-power cpu gps io lcm memory modem pmu sd-sim usb wlan
 
-.PHONY:		all spotless upload show-dup show-missing show-dk
+.PHONY:		all spotless show-dup show-missing show-dk
 # mark them as phony since we have incomplete dependencies in this Makefile
 .PHONY:		fic/fic.equ fic/fic.inv fic/missing.equ
-.PHONY:		ann sch gen generate gv xpdf
+.PHONY:		ann sch gen generate gv xpdf upload
 
 all:		gta02-core.ord
 
-gta02-core.ord:	part2order parser.pl \
-		  gta02-core.par $(INV) $(EQU)
-		perl part2order $(KITS) \
+gta02-core.ord:	gta02-core.par $(INV) $(EQU)
+		$(BOOM) part2order $(KITS) \
 		  $(INV) $(EQU) gta02-core.par >$@ || \
 		  { rm -rf $@; exit 1; }
 	
-gta02-core.par:	bom2part parser.pl match.pl \
-		  $(EQU) $(INV) $(CHR) ../gta02-core.lst gta02-core.sub
-		perl bom2part $(EQU) $(INV) $(CHR) \
+gta02-core.par:	$(EQU) $(INV) $(CHR) ../gta02-core.lst gta02-core.sub
+		$(BOOM) bom2part $(EQU) $(INV) $(CHR) \
 		  ../gta02-core.lst gta02-core.sub \
 		    >$@ || { rm -rf $@; exit 1; }
 
-darfon.chr:	gen2chr parser.pl match.pl fic/fic.equ darfon.gen
-		perl gen2chr DARFON fic/fic.equ darfon.gen >$@ || \
+darfon.chr:	fic/fic.equ darfon.gen
+		$(BOOM) gen2chr DARFON fic/fic.equ darfon.gen >$@ || \
 		  { rm -f $@; exit 1; }
 
-ralec.chr:	gen2chr parser.pl match.pl fic/fic.equ ralec.gen
-		perl gen2chr RALEC fic/fic.equ ralec.gen >$@ || \
+ralec.chr:	fic/fic.equ ralec.gen
+		$(BOOM) gen2chr RALEC fic/fic.equ ralec.gen >$@ || \
 		  { rm -f $@; exit 1; }
 
 fic/fic.equ:
@@ -46,43 +46,34 @@
 fic/missing.equ:
 		$(MAKE) -C fic missing.equ
 
-workflow.pdf:	workflow.fig
-		fig2dev -L pdf $< >$@ || { rm -f $@; exit 1; }
-
-xpdf:		workflow.pdf
-		xpdf workflow.pdf
-
-upload:		workflow.pdf
-		$(COPY) workflow.pdf $(UPLOAD)/bom-workflow.pdf
-
 spotless:
 		$(MAKE) -C fic spotless
 		rm -f gta02-core.par gta02-core.ord darfon.chr ralec.chr
 		rm -f gta02-core-bom.pro gta02-core-bom.sch $(SCH:%=%-bom.sch)
 
-show-dup:	pardup.pl gta02-core.par
-		perl ./pardup.pl gta02-core.par
+show-dup:	gta02-core.par
+		$(BOOM) ./pardup.pl gta02-core.par
 
 show-missing:	$(EQU) fic/fic.dsc
 		(echo '#ORD'; grep MISSING gta02-core.ord ; ) | \
-		  ./prettyord - $(EQU) fic/fic.dsc | \
+		  $(BOOM) prettyord - $(EQU) fic/fic.dsc | \
 		  sed 's/^...  \(.\{,78\}\).*/\1/'
 
 show-dk:
 		(echo '#ORD'; grep DIGI-KEY gta02-core.ord ; ) | \
-		  ./prettyord - digi-key.dsc | \
+		  $(BOOM) prettyord - digi-key.dsc | \
 		  sed 's/^...  //'
 
 #ANN = pmu
 ANN = audio
-ann:		annotate ../$(ANN).sch fic/fic.dsc gta02-core.par $(EQU)
-		perl ./annotate ../$(ANN).sch fic/fic.dsc gta02-core.par \
+ann:		../$(ANN).sch fic/fic.dsc gta02-core.par $(EQU)
+		$(BOOM) annotate ../$(ANN).sch fic/fic.dsc gta02-core.par \
 		  $(EQU) >$(ANN)-bom.sch || { rm -f $(ANN)-bom.sch; exit 1; }
 
 $(SCH:%=%-bom.sch): \
-		annotate $(SCH:%=../%.sch) fic/fic.dsc gta02-core.par $(EQU)
+		$(SCH:%=../%.sch) fic/fic.dsc gta02-core.par $(EQU)
 		for n in $(SCH); do \
-		    perl ./annotate ../$$n.sch fic/fic.dsc gta02-core.par \
+		    $(BOOM) annotate ../$$n.sch fic/fic.dsc gta02-core.par \
 		      $(EQU) >$$n-bom.sch || { rm -f $$n-bom.sch; exit 1; }; \
 		done
 

Deleted: trunk/gta02-core/bom/README
===================================================================
--- trunk/gta02-core/bom/README	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/README	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,336 +0,0 @@
-The BOM processing system
-=========================
-
-The BOM processing system takes a bill of material generated by
-KiCad and converts it in various steps into a "shopping list"
-that can be used to order from various providers.
-
-
-Introduction
-============
-
-The following sections describe how to use the basic elements of
-the BOM processing system.
-
-
-A simple BOM translation
-------------------------
-
-KiCad identifies components by a so-called component reference,
-e.g., R1001, U5, etc. In addition to this, each component can have
-various parameters, such as a "value", its footprint, and further
-user-defined items. These parameters can be shown in the schematics
-(e.g., the value usually is) or they can be hidden (e.g., the
-footprint).
-
-At the end of the process, we want a "shopping list" that can be
-used to order items or to find them in an inventory or catalog.
-Components in the shopping list are identified by a part number.
-
-...
-- BOM
-- inventory
-- ID matching
-
-
-Equivalences
-------------
-
-A single component can be associated with multiple part numbers.
-For example, a chip its manufacturer calls "XYZ-R1" may be listed in
-a distributor's catalog with a completely different order number,
-such as "20-1234-8". The BOM processing system therefore
-distinguishes multiple so-called name spaces. A name space is
-identified by a (unique) name and a part number is generally
-qualified by the name of the name space.
-
-E.g., if the manufacturer is called "ACME" and the distributor of
-electronical components calls itself "DIST-EL", the part in our
-example may have the equivalent names "ACME XYZ-R1" and "DIST-EL
-20-1234-8".
-
-...
-- revise .inv
-
-example.equ:
-
-#INV
-DIST-EL 20-1234-8
-#EQU
-ACME XYZ-R1 DIST-EL 20-1234-8
-
-
-Adding stock and cost
----------------------
-
-- .inv, more fields
-- quanta
-
-Substituting component names
-----------------------------
-
-- intro to .sub
-- ad-hoc fixes
-
-
-Selecting characteristics
--------------------------
-
-- .sub
-- .chr
-...
-
-
-Generating characteristics
---------------------------
-
-- .gen
-
-
-Advanced topics
-===============
-
-- generating .inv files
-- different presentations (e.g., CT, TR, ...)
-- component substitution (one-way equivalence)
-- problem reports
-- hiding known problems (while sourcing)
-
-
-File formats
-============
-
-The BOM processing system uses a large number of different files to
-store information retrieved from the BOM, inventories, intermediate
-results, etc. The following sections describe the various formats.
-
-
-Part characteristics (.chr)
----------------------------
-
-A part characteristics file lists the parameters of components.
-This information is then matched with the parameters specified in
-the schematics.
-
-The part characteristics file begins with a line containing only
-#CHR
-
-After this, each line contains the manufacturer (namespace), the
-part number, and a list of parameter=value entries. Fields are
-separated by spaces.
-
-Long lines can be wrapped by indenting the continuation lines.
-
-Blank lines and comments (#) are ignored.
-
-
-Substitutions (.sub)
---------------------
-
-A substitutions file specifies rules for translating component
-parameters in schematics to part characteristics.
-
-A substitution rule consists of zero or more conditions and zero or
-more assignments. The conditions are of the form field=pattern. The
-field can be a per-component fields KiCad provides or any parameter
-set by substitutions.
-
-KiCad fields are named as follows:
-
-KiCad field  Field name
------------  ----------
-Reference    REF (*)
-Value        VAL
-FP           Footprint
-Field1       F1
-...          ...
-
-(*) As a shortcut, REF= can be omitted.
-
-Note that fields with a user-defined name currently still only appear
-as F1, F2, etc.
-
-The special field name FN can be used to look for a match in all of
-F1, F2, ... This way, it's sufficient to use a consistent syntax for
-additional parameters, without having to assign also a fixed location
-for them. If more than one field matches, the first match is taken.
-
-Field names are case-insensitive.
-
-The pattern is uses a notation similar to filename globbing. There
-are the following special constructs:
-
-- * matches a string of any length
-- ? matches a single character
-- (...) matches the pattern between the parentheses and records the
-  string matched
-- $X marks a value in nXn notation, e.g., 4u7 or 100R. Such values
-  are converted to SI-like notation.
-
-A rule is applied when all conditions are fulfilled. In this case,
-assignments of the form field=value are executed. Strings obtained
-in the match can be included in a value as follows:
-
-- $field and ${field} are replaced by the respective field
-- $field:n and ${field:n} are replaced by the n-th (...) pattern in
-  the match of the respective field
-
-If a rule ends with an exclamation mark, the substitution process stops
-after the rule is applied. Otherwise, further rules are processed.
-
-Examples:
-
-R* val=$R -> R=$val
-
-This rule translates the values of all resistors to SI notation.
-
-D* FN=(*)Vdc -> T=TSV Vdc=FN:1
-
-This rule sets the parameters T and Vdc for Zeners acting as TSVs.
-
-If a set of rules has a common set of conditions or assignments, the
-more compact block notation can be used instead of repeating them for
-each rule:
-
-common-conditions -> common-assignments {
-    rule-specific-conditions -> rule-specific-assignments
-    ...
-}
-
-Rules in a block only match if both the common and the rule-specific
-conditions are met. Then the common and the rule-specific assignments
-are performed. If a condition or an assignment appears both in the
-common and the rule-specific part, only the latter is used.
-
-Long lines can be wrapped by indenting the continuation lines. Note
-that { and ! are also considered to be part of the same line as the
-rest of the rule. In particular, the following construct wouldn't
-work:
-
-X=Y
-{
-    ...
-}
-
-With proper indentation, this would:
-
-X=Y
-  {
-    ...
-}
-
-
-Characteristics generation (.gen)
----------------------------------
-
-The substitution mechanism can also be used to automatically generate
-characteristics from part numbers, e.g., for resistors or capacitors.
-
-.gen files are exactly .sub files, with the exception that the only
-field used is the REF field and that it contains the part number.
-
-Once the rule set has been processed, all fields (except REF) whose
-name doesn't begin with an underscore are placed in the characteristics
-entry as parameters.
-
-An entry is only produced if the rule set is explicitly terminated.
-
-
-Parts list (.par)
-------------------
-
-A parts file lists the parts that are suitable for a given BOM item.
-The file begins with a line containing only
-#PAR
-
-After this, each line contains the component reference, a space, and
-then one or more namespace part-number groups, separated by spaces as
-well.
-
-Blank lines and comments (#) are ignored.
-
-
-Order list (.ord)
------------------
-
-An order file lists the quantities to order from inventories, along
-with the cost and the component references the item is used for. The
-file begins with a line containing only
-#ORD
-
-After this, each line contains the supplier (namespace), the reference
-number, the number of items to order, the currency code, the cost,
-and one or more component references.
-
-Blank lines and comments (#) are ignored.
-
-
-Equivalence (.equ)
-------------------
-
-Equivalence files establish equivalences between parts numbers in the
-same or in different name spaces. An equivalence file begins with a
-line containing only
-#EQU
-
-After this, each line consists of the following four space-separated
-fields:
-
-namespace-1 part-number-1 namespace-2 part-number-2
-
-Blank lines and comments (#) are ignored.
-
-
-Inventory (.inv)
-----------------
-
-Inventory files list inventory and component cost. An inventory file
-begins with a line containing only
-#INV
-
-After this, each line contains the namespace and the part number,
-followed by the number of items in stock, the currency code, and one
-or more pricing entries.
-
-Each pricing entry consists of two fields: the number of items in an
-order, and the per item price at that quantity. A sequence of
-increasing order sizes indicates that they are quanta. A sequence of
-decreasing order sizes indicates that smaller quanta are possible
-after a previous larger threshold has been met.
-
-Example:
-
-... USD 1 0.5 10 0.4 100 0.2
-
-Means that an order of at least 170 units would be made either as
-2 * 100 units, costing USD 40, or as 1 * 100 + 7 * 10 units, costing
-USD 20 + USD 28 = USD 48.
-
-If the entry is
-
-... USD 1 0.5 10 0.4 100 0.2 1 0.2
-
-Then the USD 0.2 per unit cost would apply to any any quantity of at
-least 100 units. So a 170 units order would cost USD 34.
-
-Blank lines and comments (#) are ignored.
-
-The number of items in stock and the pricing data can be omitted. We
-call this "virtual inventory". In this case, the numer of items in
-stock and the price default to large numbers (e.g., 999999). Virtual
-inventory is used to suppress warnings for parts that have not been
-sourced yet, but where sourcing is in progress.
-
-
-Description (.dsc)
-------------------
-
-A description file contains plain text descriptions of parts. The file
-begins with a like containing only
-#DSC
-
-Each line contains the name space, a space, the part number, another
-space, and the description. The description can contain any printable
-character and ends with a newline.
-
-Blank lines and comments (#) are ignored.

Deleted: trunk/gta02-core/bom/annotate
===================================================================
--- trunk/gta02-core/bom/annotate	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/annotate	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,188 +0,0 @@
-#!/usr/bin/perl
-
-require "parser.pl";
-require "misc.pl";
-
-
-&parse;
-
-
-$H = 50;	# character height
-$W = $H*0.9;	# character width
-$L = $H+20;	# line skip
-
-
-sub normalize
-{
-    my @t = @_;
-
-    # convert from (x0, y0, w, h) to (x0, y0, x1, y1)
-    $t[2] += $t[0];
-    $t[3] = $t[1]-$t[3];
-    return ($t[0], $t[3], $t[2], $t[1]);
-}
-
-
-#
-# 2x2 matrix inversion
-# http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_2.C3.972_matrices
-#
-
-sub invert
-{
-    my @m = @_;
-    my $f = 1/($m[0]*$m[3]-$m[1]*$m[2]);
-    return ($f*$m[3], -$f*$m[1], -$f*$m[2], $f*$m[0]);
-}
-
-
-sub block
-{
-    my @t = &normalize(@_);
-    push(@block, [ @t ]);
-    $wnl .= "Wire Notes Line\n\t$t[0] $t[1] $t[2] $t[3]\n";
-}
-
-
-sub pass
-{
-    my @t = &normalize(@_);
-
-    for (@block) {
-	my @b = @{ $_ };
-	next if $t[0] > $b[2];
-	next if $t[2] < $b[0];
-	next if $t[1] > $b[3];
-	next if $t[3] < $b[1];
-	return 0;
-    }
-    return 1;
-}
-
-
-sub put
-{
-    local ($x0, $y0, $ref, @s) = @_;
-
-    my $h = @s*$L;
-    my $w = 0;
-    for (@s) {
-	my $t = $W*length $_;
-	$w = $t if $t > $w;
-    }
-    my $a = 270;
-    my $r = 100;
-    my $x, $y;
-    my $ym = $y0-$h+$H/2;
-    for ($i = 0; $i != 128; $i++) {
-	$x = int($x0+$r*cos($a/180*3.14159));
-	$y = int($ym+$r*sin($a/180*3.14159));
-	last if &pass($x, $y, $w, $h);
-	$a += 22.5;
-	$r += $L/8;
-    }
-    warn "no place found for \"$s[0]\"" if $i == 128;
-
-    my @m = &invert( @{ $m{$ref} });
-    &block($x, $y+$H/2, $w, $h);
-    my $n = 10;
-    for my $s (reverse @s) {
-	my $dx = $x-$x0;
-	my $dy = $y-$y0;
-	my $sx = $x0+$dx*$m[0]+$dy*$m[1];
-	my $sy = $y0+$dx*$m[2]+$dy*$m[3];
-	($hv, $hj, $vj) = ("H", "L", "C") if $m[0] == 1;
-	($hv, $hj, $vj) = ("H", "R", "C") if $m[0] == -1;
-	($hv, $hj, $vj) = ("V", "C", "B") if $m[1] == 1;
-	($hv, $hj, $vj) = ("V", "C", "T") if $m[1] == -1;
-	$s =~ s/~/-/g;
-	print "F $n \"$s\" $hv $sx $sy $H  0000 $hj ${vj}NN\n";
-	$y -= $L;
-	$n++;
-    }
-}
-
-
-#
-# pass 1: find the orientation of all parts
-#
-
-for (@eeschema) {
-    $ref = $1 if /^L \S+ (\S+)/;
-    undef $ref if /^\$EndComp/;
-    next unless /^\s+(-?[01])\s+(-?[01])\s+(-?[01])\s+(-?[01])\s*$/;
-    my @m = split(/\s+/);
-    shift @m;
-    $m{$ref} = [ @m ];
-}
-
-
-#
-# pass 2: block the spaces occupied by fields
-#
-
-for (@eeschema) {
-    $ref = $1 if /^L \S+ (\S+)/;
-    if (/^P (\d+) (\d+)/) {
-	$x0 = $1;
-	$y0 = $2;
-    }
-    next unless /^F /;
-    die "$_" unless
-      /^F \d+ "([^"]*)" ([HV]) (\d+) (\d+) (\d+) +(\d+) ([LC]) (C)/;
-    ($s, $hv, $x, $y, $size, $flag, $hj, $vj) =
-      ($1, $2, $3, $4, $5, $6, $7, $8);
-    $dx = $x-$x0;
-    $dy = $y-$y0;
-    $x = $x0+$dx*$m{$ref}[0]+$dy*$m{$ref}[1];
-    $y = $y0+$dx*$m{$ref}[2]+$dy*$m{$ref}[3];
-    next if $flag != 0;
-    $w = $size*0.8*length $s;
-    # we don't need to consider H/V
-    &block($hj eq "L" ? $x : $x-$w/2, $y+$size/2, $w, $size);
-}
-
-#
-# pass 3:
-#
-
-for (@eeschema) {
-    undef @f if /^\$Comp/;
-    if (/^L \S+ (\S+)/) {
-	$ref = $1;
-	my @p = @{ $parts{$ref} };
-	while (@p) {
-	    my @id = splice(@p, 0, 2);
-	    my $id = "$id[0] $id[1]";
-	    for ($id, &eq($id)) {
-		next unless defined $dsc{$_};
-		push(@f, $dsc{$_});
-		last;
-	    }
-	}
-    }
-    if (/^P (\d+) (\d+)/) {
-	$x = $1;
-	$y = $2;
-    }
-    if (/^\s+/) {
-	my %seen;
-	my @u = ();
-	for (@f) {
-	    next if $seen{$_};
-	    push(@u, $_);
-	    $seen{$_} = 1;
-	}
-	undef @f;
-	# $m{$ref}[0] == 1	OK
-	# $m{$ref}[0] == -1	OK
-	# $m{$ref}[1] == 1	OK
-	# $m{$ref}[1] == -1	OK (small deviations found)
-	&put($x, $y, $ref, @u) if 1 || $m{$ref}[1] == -1;
-    }
-    if (/\$EndSCHEMATC/) {
-	# uncomment for debugging
-#	print $wnl;
-    }
-    print "$_\n";
-}

Deleted: trunk/gta02-core/bom/bom2part
===================================================================
--- trunk/gta02-core/bom/bom2part	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/bom2part	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,92 +0,0 @@
-#!/usr/bin/perl
-
-require "parser.pl";
-require "match.pl";
-require "misc.pl";
-
-
-sub issue
-{
-    print shift(@_), " ", join(" ", @_, &eq(@_)), "\n";
-}
-
-
-if ($ARGV[0] eq "-d") {
-    $debug = 1;
-    shift @ARGV;
-}
-&parse;
-
-$total = 0;
-$bad = 0;
-
-print "#PAR\n";
-for $ref (keys %cmp) {
-    @f = @{ $cmp{$ref} };
-    $total++;
-
-    print STDERR "REF $ref\n" if $debug;
-
-    # if we're lucky, we get a direct ID match
-
-    if (defined $id{$f[0]}) {
-	print STDERR "FIRST ID\n" if $debug;
-	&issue($ref, $id{$f[0]});
-	next;
-    }
-
-    # no such luck. Let's roll up our sleeves and to the substitutions.
-
-    undef %field;
-    $field{"REF"} = $ref;
-    $field{"VAL"} = $f[0];
-    if ($f[1] eq "") {
-	print STDERR "warning: $ref ($f[0]) has no footprint\n";
-    } else {
-	$field{"FP"} = $f[1];
-    }
-    for (my $i = 1; $i != 10; $i++) {
-	$field{"F$i"} = $f[$i+1];
-    }
-    &apply_rules();
-
-    # try our luck again
-
-    if (defined $id{$field{"VAL"}}) {
-	print STDERR "SECOND ID\n" if $debug;
-	&issue($ref, $id{$field{"VAL"}});
-	next;
-    }
-
-    # still nothing. Let's match characteristics then.
-
-    my @p = ();
-    COMP: for my $c (keys %chr) {
-	print STDERR "PART $c\n" if $debug;
-	for (keys %field) {
-	    next if $_ eq "REF" || $_ eq "VAL" || $_ =~ /^F\d$/;
-	    print STDERR "  $_=",$field{$_}," " if $debug;
-	    if (!defined $chr{$c}{$_}) {
-		print STDERR "NO FIELD\n" if $debug;
-		next COMP;
-		next;
-	    }
-	    if ($chr{$c}{$_} eq $field{$_}) {
-		print STDERR "== $chr{$c}{$_}\n" if $debug;
-	    } else {
-		print STDERR "!= $chr{$c}{$_}\n" if $debug;
-		next COMP;
-	    }
-	}
-	push(@p, $c);
-    }
-    if (@p) {
-	&issue($ref, @p);
-	next;
-    }
-
-    print STDERR "unmatched: $ref (", join(", ", @f), ")\n";
-    $bad++;
-#    print join("#", ($ref, @f)), " -> $id{$f[0]}\n";
-}
-print STDERR "$bad/$total unmatched\n" if $bad;

Modified: trunk/gta02-core/bom/fic/Makefile
===================================================================
--- trunk/gta02-core/bom/fic/Makefile	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/fic/Makefile	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,3 +1,5 @@
+BOOM=../../../eda/boom/boom
+
 INV = inventory-fic-ee.csv
 
 .PHONY:		all spotless
@@ -17,7 +19,8 @@
 		grep -v ' 0 USD' fic.inv >present.inv || { rm -f $@; exit 1; }
 
 missing.equ:	equ2equ missing.inv fic.equ
-		perl equ2equ missing.inv fic.equ >$@ || { rm -f $@; exit 1; }
+		$(BOOM) ./equ2equ missing.inv fic.equ >$@ || \
+		  { rm -f $@; exit 1; }
 
 spotless:
 		rm -f fic.equ fic.inv fic.dsc present.inv missing.equ

Modified: trunk/gta02-core/bom/fic/equ2equ
===================================================================
--- trunk/gta02-core/bom/fic/equ2equ	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/fic/equ2equ	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 
-require '../parser.pl';
+require 'parser.pl';
 
 &parse;
 

Deleted: trunk/gta02-core/bom/gen2chr
===================================================================
--- trunk/gta02-core/bom/gen2chr	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/gen2chr	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,35 +0,0 @@
-#!/usr/bin/perl
-
-require "parser.pl";
-require "match.pl";
-
-
-if ($ARGV[0] eq "-d") {
-    $debug = 1;
-    shift @ARGV;
-}
-if ($ARGV[0] eq "-n") {
-    $negate = 1;
-    shift @ARGV;
-}
-$key = shift @ARGV;
-&parse;
-
-print "#CHR\n";
-for (keys %id) {
-    next unless $id{$_} eq "$key $_";
-    undef %field;
-    $field{"REF"} = $_;
-    if (!&apply_rules()) {
-	print "$id{$_}\n" if $negate;
-	next;
-    }
-    next if $negate;
-    print $id{$_};
-    for (sort keys %field) {
-	next if $_ =~ /^_/;
-	next if $_ eq "REF";
-	print " $_=$field{$_}";
-    }
-    print "\n";
-}

Deleted: trunk/gta02-core/bom/match.pl
===================================================================
--- trunk/gta02-core/bom/match.pl	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/match.pl	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,200 +0,0 @@
-#!/usr/bin/perl
-
-use re 'eval';
-
-
-#
-# "sub" populates the following global variables:
-#
-# $end[rule-number] = 0 / 1
-# $match[rule-number]{field}[0] = original-pattern
-# $match[rule-number]{field}[1] = RE1 
-# $match[rule-number]{field}[2] = RE2
-# $action[rule-number]{field} = value
-#
-# $match_stack[depth]{field}[0] = original-pattern
-# $match_stack[depth]{field}[1] = RE1
-# $match_stack[depth]{field}[2] = RE2
-# $action_stack[depth]{field} = value
-# $may_cont = 0 / 1
-# $last
-# $last_action
-#
-
-#
-# $cvn_from{internal-handle} = index
-# $cvn_to{internal-handle} = index
-# $cvn_unit{internal-handle} = unit-name
-# $cvn_num = internal-handle
-# $found{field-or-subfield} = string
-
-
-#
-# We convert each input pattern into two regular expressions: the first matches
-# units in the nXn notation, e.g., 4u7 or 100R. The second matches them in SI
-# notation (sans space).
-#
-# When matching (sub_match), we first apply the first expression. Each time we
-# encounter a unit ($R, $F, etc.), __cvn is called. __cvn stores the index of
-# the unit in %cvn_from and %cvn_to.
-#
-# We then pick these substrings from the input string and convert the units to
-# SI notation. At the same time, we normalize the mantissa. Once done, we run
-# the second expression. This one always matches (hopefully :-)
-#
-# All (...) ranges in the original pattern have been replaced with named
-# capture buffers in the second expression, so all these subfields are now
-# gathered in the $+ array. (The same also happened in the first pass, but we
-# ignore it.)
-#
-# Finally, when expanding a value (sub_expand), we look for $field and
-# $field:index, and expand accordingly.
-#
-
-
-sub __cvn
-{
-    local ($num) = @_;
-
-    $cvn_from{$num} = $-[$#-];
-    $cvn_to{$num} = $+[$#+];
-}
-
-
-sub sub_match
-{
-    local ($s, $field, $m1, $m2) = @_;
-
-    #
-    # Perform the first match and record where we saw $<unit> patterns.
-    #
-    undef %cvn_from;
-    undef %cvn_to;
-    return undef unless $s =~ $m1;
-
-    #
-    # Convert the unit patterns to almost-SI notation. (We don't put a space
-    # after the number, but the rest is SI-compliant.)
-    #
-    my $off = 0;
-    for (keys %cvn_from) {
-	my $unit = $cvn_unit{$_};
-	my $from = $cvn_from{$_}+$off;
-	my $len = $cvn_to{$_}-$cvn_from{$_};
-	die unless substr($s, $from, $len) =~
-	    /(\d+)$unit(\d*)|(\d+)([GMkmunpf])(\d*)/;
-
-	#
-	# Normalize to \d+.\d*
-	#
-	my $v = "$1$3.$2$5";
-	my $exp = $4 eq "" ? " " : $4;
-
-	#
-	# Remove leading zeroes.
-	#
-	$v =~ s/^0*(\d+)/\1/;
-
-	#
-	# Mantissa must be < 1000.
-	# Do the math as string operation to avoid rounding errors.
-	#
-	while ($v =~ /(\d+)(\d{3})\./) {
-	    $v = "$1.$2$'";
-	    $exp =~ tr/GMk munpf/TGMk munp/;
-	}
-
-	#
-	# Mantissa must be >= 1.
-	#
-	while ($v =~ /\b0\.(\d+)/) {
-	    if (length $1 < 3) {
-		$v = $1.("0" x (3-length $1)).".";
-	    } else {
-		$v = substr($1, 0, 3).".".substr($1, 3);
-	    }
-	    $exp =~ tr/GMk munpf/Mk munpa/;
-	}
-
-	#
-	# Remove trailing zeroes
-	#
-	$v =~ s/(\.[1-9]*)0*/\1/;
-
-	$exp =~ s/ //;
-	$v =~ s/\.$//;
-	$v = $v.$exp.$unit;
-	$off += length($v)-$len;
-	substr($s, $from, $len, $v);
-    }
-
-    #
-    # Run the second match on the string to process any (...) patterns
-    #
-    $found{$field} = $s;
-    die $m2 unless $s =~ $m2;
-    for (keys %+) {
-	$found{$_} = $+{$_};
-    }
-    return $s;
-}
-
-
-sub sub_expand
-{
-    local ($s) = @_;
-
-    while ($s =~ /^([^\$]*)\$([A-Za-z_]\w*)(:(\d+))?|^([^\$]*)\${([A-Za-z_]\w*)(:(\d+))?}/) {
-	my $name = "$2$6";
-	$name .= "__$4$8" if defined($4) || defined($8);
-	die "don't know \"$name\"" unless defined $found{$name};
-	$s = $1.$5.$found{$name}.$';
-    }
-    return $s;
-}
-
-
-#
-# return 0 if all rules have been exhausted, 1 if there was an explicit halt.
-#
-
-sub apply_rules
-{
-    RULE: for (my $i = 0; $i <= $#match; $i++) {
-	print STDERR "RULE #$i\n" if $debug;
-	%found = %field;
-	FIELD: for my $f (keys %{ $match[$i] }) {
-	    my @f = $f ne "FN" ? ($f) :
-	      ("F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9");
-	    for (@f) {
-		print STDERR "  MATCH $_=$match[$i]{$f}[0] " if $debug;
-		if (!defined $found{$_}) {
-		    print STDERR "NO FIELD\n" if $debug;
-		    next;
-		}
-		print STDERR "FIELD $found{$_} " if $debug;
-		if (!defined &sub_match($found{$_}, $f,
-		  $match[$i]{$f}[1], $match[$i]{$f}[2])) {
-		    print STDERR "MISS\n" if $debug;
-		    next;
-		}
-		print STDERR "MATCH\n" if $debug;
-		next FIELD;
-	    }
-	    next RULE;
-	}
-	for (keys %{ $action[$i] }) {
-	    my $s = &sub_expand($action[$i]{$_});
-	    print STDERR "  SET $_=$action[$i]{$_} => $s\n" if $debug;
-	    $field{$_} = $s;
-	}
-	if ($end[$i]) {
-	    print STDERR "  END\n" if $debug;
-	    return 1;
-	}
-    }
-    return 0;
-}
-
-
-return 1;

Deleted: trunk/gta02-core/bom/misc.pl
===================================================================
--- trunk/gta02-core/bom/misc.pl	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/misc.pl	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,26 +0,0 @@
-#!/usr/bin/perl
-
-
-#
-# determine the equivalent parts, taking into account that %eq is transitive
-#
-
-sub eq
-{
-    my %seen;
-    my @p = @_;	# parts to consider
-    my @r = ();	# new equivalences we've found
-    my $skip = @p;
-
-    while (@p) {
-	my $p = shift @p;
-	next if $seen{$p};
-	$seen{$p} = 1;
-	push(@r, $p) if $skip-- <= 0;
-	push(@p, @{ $eq{$p} });
-    }
-    return @r;
-}
-
-
-return 1;

Deleted: trunk/gta02-core/bom/parser.pl
===================================================================
--- trunk/gta02-core/bom/parser.pl	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/parser.pl	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,392 +0,0 @@
-#!/usr/bin/perl
-
-use re 'eval';
-
-
-sub skip
-{
-    # do nothing
-}
-
-
-#
-# "bom" populates the following global variable:
-#
-# $cmp{component-reference}[0] = value
-# $cmp{component-reference}[1] = footprint
-# $cmp{component-reference}[2] = field1
-# ...
-#
-
-sub bom
-{
-    if (/^#End Cmp/) {
-	$mode = *skip;
-	return;
-    }
-    die unless /^\|\s+(\S+)\s+/;
-    my $ref = $1;
-    my @f = split(/\s*;\s*/, $');
-    next if $f[0] eq "NC";
-    $cmp{$ref} = [ @f ];
-}
-
-
-#
-# "equ" populates the following global variables:
-#
-# $id{item-number} = "namespace item-number"
-#   This is used for heuristics that look up parts commonly referred to by
-#   their part number.
-#
-# $eq{"namespace0 item-number0"}[] = ("namespace1 item-number1", ...)
-#   List of all parts a given part is equivalent to.
-#
-
-sub equ
-{
-    my @f = split(/\s+/);
-    my $a = "$f[0] $f[1]";
-    my $b = "$f[2] $f[3]";
-    $id{$f[1]} = $a;
-    $id{$f[3]} = $b;
-    push @{ $eq{$a} }, $b;
-    push @{ $eq{$b} }, $a;
-}
-
-
-#
-# "inv" populates the following global variables:
-#
-# $id{item-number} = "namespace item-number"
-#   This is used for heuristics that look up parts commonly referred to by
-#   their part number.
-#
-# $inv{"namespace item-number"}[0] = items-in-stock
-# $inv{"namespace item-number"}[1] = currency
-# $inv{"namespace item-number"}[2] = order-quantity
-# $inv{"namespace item-number"}[3] = unit-price
-#   [2] and [3] may repeat.
-#
-
-sub inv
-{
-    my @f = split(/\s+/);
-    my $id = "$f[0] $f[1]";
-    shift @f;
-    my $ref = shift @f;
-    die "duplicate inventory entry for \"$id\"" if defined $inv{$id};
-    $id{$ref} = $id;
-    $inv{$id} = [ @f ];
-    $inv{$id}[0] = 999999 unless defined $inv{$id}[0];
-    $inv{$id}[1] = "N/A" unless defined $inv{$id}[1];
-    $inv{$id}[2] = 1 unless defined $inv{$id}[2];
-    $inv{$id}[3] = 999999 unless defined $inv{$id}[3];
-}
-
-
-#
-# "par" populates the following global variables:
-#
-# $parts{component-ref}[0] = namespace
-# $parts{component-ref}[1] = item-number
-# [0] and [1] may repeat
-#
-# $want{"namespace item"} = number of times we may use the part. If multiple
-#   parts are eligible for a component, each of them is counted as desirable
-#   for each component.
-#
-# $comps{"namespace item"}{component-ref} = 1
-#   Set of components a part may be used for.
-#
-
-sub par
-{
-    my @f = split(/\s+/);
-    my $ref = shift @f;
-    $parts{$ref} = [ @f ];
-    while (@f) {
-	my @id = splice(@f, 0, 2);
-	my $id = "$id[0] $id[1]";
-	$want{$id}++;
-	$comps{$id}{$ref} = 1;
-    }
-}
-
-
-#
-# "chr" populates the following global variable:
-#
-# $chr{"namespace item-number"}{parameter} = value
-#
-# $last is used internally for continuation lines.
-#
-
-sub chr
-{
-    my @f;
-    if (/^\s+/) {
-	@f = split(/\s+/, $');
-    } else {
-	@f = split(/\s+/);
-	my $ref = shift @f;
-	my $num = shift @f;
-	$last = "$ref $num";
-    }
-    for (@f) {
-	die "\"=\" missing in $_" unless /=/;
-	$chr{$last}{uc($`)} = $';
-    }
-}
-
-
-#
-# "sub" populates the following global variables:
-#
-# $end[rule-number] = 0 / 1
-# $match[rule-number]{field}[0] = original-pattern
-# $match[rule-number]{field}[1] = RE1 
-# $match[rule-number]{field}[2] = RE2
-# $action[rule-number]{field} = value
-#
-# $match_stack[depth]{field}[0] = original-pattern
-# $match_stack[depth]{field}[1] = RE1
-# $match_stack[depth]{field}[2] = RE2
-# $action_stack[depth]{field} = value
-# $may_cont = 0 / 1
-# $last
-# $last_action
-#
-
-#
-# $cvn_from{internal-handle} = index
-# $cvn_to{internal-handle} = index
-# $cvn_unit{internal-handle} = unit-name
-# $cvn_num = internal-handle
-# $found{field-or-subfield} = string
-
-
-sub sub_pattern
-{
-    local ($field, $p) = @_;
-    my $n = 0;
-    $p =~ s/\./\\./g;
-    $p =~ s/\+/\\+/g;
-    $p =~ s/\?/./g;
-    $p =~ s/\*/.*/g;
-    my $tmp = "";
-    while ($p =~ /^([^\(]*)\(/) {
-	$n++;
-	$tmp .= "$1(?'${field}__$n'";
-	$p = $';
-    }
-    $p = "^".$tmp.$p."\$";
-    my $q = $p;
-    while ($p =~ /^([^\$]*)\$(.)/) {
-	$p = "$1(\\d+$2\\d*|\\d+[GMkmunpf$2]\\d*)(?{ &__cvn($cvn_num); })$'";
-	$cvn_unit{$cvn_num} = $2;
-	die unless $q =~ /^([^\$]*)\$(.)/;
-	$q = "$1(\\d+(\.\\d+)?[GMkmunpf]?$2)$'";
-	$cvn_num++;
-    }
-    return ($p, $q);
-}
-
-
-sub sub_value
-{
-    return $_[0];
-}
-
-
-sub sub
-{
-    /^(\s*)/;
-    my $indent = $1;
-    my @f = split(/\s+/, $');
-    my $f;
-    my $in = 0;		# indentation level
-    while (length $indent) {
-	my $c = substr($indent, 0, 1, "");
-	if ($c eq " ") {
-	    $in++;
-	} elsif ($c eq "\t") {
-	    $in = ($in+8) & ~7;
-	} else {
-	    die;
-	}
-    }
-    if ($may_cont && $in > $last) {
-	pop(@match);
-	pop(@action);
-	pop(@end);
-    } else {
-	$match_stack[0] = undef;
-	$action_stack[0] = undef;
-	$last_action = 0;
-	$last = $in;
-    }
-    if (!$last_action) {
-	while (@f) {
-	    $f = shift @f;
-	    last if $f eq "->" || $f eq "{" || $f eq "}" || $f eq "!";
-	    if ($f =~ /=/) {
-		$match_stack[0]{uc($`)} = [ $', &sub_pattern(uc($`), $') ];
-	    } else {
-		$match_stack[0]{"REF"} = [ &sub_pattern("REF", $f) ];
-	    }
-	}
-	$last_action = 1 if $f eq "->";
-    }
-    if ($last_action) {
-	while (@f) {
-	    $f = shift @f;
-	    last if $f eq "{" || $f eq "!";
-	    die unless $f =~ /=/;
-	    $action_stack[0]{uc($`)} = &sub_value($');
-	}
-    }
-    $may_cont = 0;
-    if ($f eq "{") {
-	unshift(@match_stack, undef);
-	unshift(@action_stack, undef);
-	die "items following {" if @f;
-    } elsif ($f eq "}") {
-	shift @match_stack;
-	shift @action_stack;
-	die "items following }" if @f;
-    } else {
-	die "items following !" if @f && $f eq "!";
-	push(@end, $f eq "!");
-	$may_cont = $f ne "!";
-	my $n = $#end;
-	push(@match, undef);
-	push(@action, undef);
-	for my $m (reverse @match_stack) {
-	    for (keys %{ $m }) {
-		$match[$n]{$_} = $m->{$_};
-	    }
-	}
-	for my $a (reverse @action_stack) {
-	    for (keys %{ $a }) {
-		$action[$n]{$_} = $a->{$_};
-	    }
-	}
-    }
-}
-
-
-#
-# "ord" populates the following global variables:
-#
-# $order{"namespace item-number"}[0] = quantity to order
-# $order{"namespace item-number"}[1] = currency
-# $order{"namespace item-number"}[2] = total cost in above currency
-# $order{"namespace item-number"}[3] = component reference
-# ...
-#
-
-sub ord
-{
-    my @f = split(/\s+/);
-    my @id = splice(@f, 0, 2);
-    @{ $order{"$id[0] $id[1]"} } = @f;
-}
-
-
-#
-# "dsc" populates the following global variable:
-#
-# $dsc{"namespace item-number"} = description
-#
-
-sub dsc
-{
-    my @f = split(/\s+/);
-    my @id = splice(@f, 0, 2);
-    $dsc{"$id[0] $id[1]"} = join(" ", @f);
-}
-
-
-#
-# "eeschema" populates the following global variable:
-#
-# $eeschema[] = line
-#
-
-
-sub eeschema
-{
-    push(@eeschema, $_[0]);
-    if ($_[0] =~ /^\$EndSCHEMATC/) {
-	$mode = *skip;
-	undef $raw;
-    }
-}
-
-
-sub parse
-{
-    $mode = *skip;
-    while (<>) {
-	chop;
-	if (/^#Cmp.*order = Reference/) {
-	    $mode = *bom;
-	    next;
-	}
-	if (/^#Cmp.*order = Value/) {
-	    $mode = *skip;
-	    next;
-	}
-	if (/^eeschema \(/) {	# hack to allow loading in any order
-	    $mode = *skip;
-	    next;
-	}
-	if (/^EESchema Schematic/) {
-	    $mode = *eeschema;
-	    $raw = 1;
-	    die "only one schematic allowed" if defined @eeschema;
-	    &eeschema($_);
-	    next;
-	}
-	if (/^#EQU\b/) {
-	    $mode = *equ;
-	    next;
-	}
-	if (/^#INV\b/) {
-	    $mode = *inv;
-	    next;
-	}
-	if (/^#PAR\b/) {
-	    $mode = *par;
-	    next;
-	}
-	if (/^#CHR\b/) {
-	    $mode = *chr;
-	    undef $last;
-	    next;
-	}
-	if (/^#(SUB|GEN)\b/) {
-	    $mode = *sub;
-	    undef $last;
-	    undef $last_action;
-	    undef $may_cont;
-	    next;
-	}
-	if (/^#ORD\b/) {
-	    $mode = *ord;
-	    next;
-	}
-	if (/^#DSC\b/) {
-	    $mode = *dsc;
-	    next;
-	}
-	if (!$raw) {
-	    s/#.*//;
-	    next if /^\s*$/;
-	}
-	&$mode($_);
-    }
-}
-
-return 1;

Deleted: trunk/gta02-core/bom/part2order
===================================================================
--- trunk/gta02-core/bom/part2order	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/part2order	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,133 +0,0 @@
-#!/usr/bin/perl
-
-require "parser.pl";
-require "misc.pl";
-
-$mult = shift(@ARGV);
-&parse;
-
-
-sub number
-{
-    local ($id) = @_;
-
-    my $s = $inv{$id}[0];
-    my $n = $want{$id}*$mult;
-    return $n < $s ? $n : $s;
-
-}
-
-
-#
-# The heuristics here aren't very nice. We give zero-cost stock priority over
-# any other stock, when we go by stock size up to the quantity we need. The
-# idea is to exhause local stock (zero-cost) first, then try to obtain the
-# parts with as few orders as possible.
-#
-# It would be better to have some sort of priority, so that we can express a
-# preference among stock we already own. Also, if non-zero-cost stock has widly
-# different prices, the smallest order cost may not be a good indicator of
-# which source we prefer.
-#
-# Furthermore, the algorithm doesn't consider the number of sources we use in
-# total or things like lead time, shipping cost, customs, etc.
-#
-
-sub rank
-{
-    local ($a, $b) = @_;
-
-    my $na = &number($a);	# min(number wanted, available)
-    my $nb = &number($b);
-    my $pa = $inv{$a}[3];	# per unit price for smallest quantum
-    my $pb = $inv{$b}[3];
-
-#print STDERR "a=$a b=$b na=$na nb=$nb pa=$pa pb=$pb\n";
-    return 1 if $na && !$pa && $pb;
-    return -1 if $nb && $pa && !$pb;
-    return $na <=> $nb if $na != $nb;
-    return $pb <=> $pa;
-}
-
-
-for (keys %parts) {
-    $parts++;
-}
-
-print "#ORD\n";
-for (sort { &rank($b, $a) } keys %want) {
-    my $n = &number($_);
-    $n -= $n % $mult;
-    next unless $n;
-    my @f = @{ $inv{$_} };
-    my $max = shift @f;
-    my $currency = shift @f;
-    my @qty;
-    my @price;
-    my %index;
-    my $best_qty;
-    my $best_price = undef;
-    while (@f) {
-	my $q = shift @f;
-	my $p = shift @f;
-	if (defined $index{$q}) {
-	    $price[$index{$q}] = $p;
-	} else {
-	    push(@qty, $q);
-	    push(@price, $p);
-	    $index{$q} = $#qty;
-	    # @@@ this fails if smaller quantities following a large quantity
-	    # differ from the quantities preceding them. E.g., 1 10 100 25
-	    # wouldn't yield correct results.
-	}
-	for (my $i = $#qty; $i >= 0; $i--) {
-	    my $order = 0;
-	    my $price = 0;
-	    my $left = $n;
-	    for (my $j = $#qty; $j >= $i; $j--) {
-		while ($left >= ($j == $i ? 1 : $qty[$j])) {
-		    $left -= $qty[$j];
-		    $order += $qty[$j];
-		    $price += $price[$j]*$qty[$j];
-		}
-	    }
-	    next if $order > $max;
-	    if (!defined $best_price || $price < $best_price) {
-		$best_price = $price;
-		$best_qty = $order;
-	    }
-	}
-    }
-    next if !defined $best_price;
-    print "$_ $best_qty $currency $best_price";
-    my $id = $_;
-    while (keys %{ $comps{$id} }) {
-	last if $best_qty < $mult;
-	$best_qty -= $mult;
-	my $ref = (sort keys %{ $comps{$id} })[0];
-#print STDERR "$id: $ref + ", join("|", keys %{ $comps{$id} }), "\n";
-	my @f = @{ $parts{$ref} };
-	while (@f) {
-	    my @id2 = splice(@f, 0, 2);
-	    my $id2 = "$id2[0] $id2[1]";
-	    $want{$id2}--;
-	    delete $comps{$id2}{$ref};
-	}
-	print " $ref";
-    }
-    print "\n";
-}
-
-for my $id (sort { $want{$b} <=> $want{$a} } keys %want) {
-    next unless $want{$id};
-    print STDERR "$id";
-    for (&eq($id)) {
-#	next unless $want{$_};
-	die "\n$_ ($want{$_}) vs. $id want ($want{$id})"
-	  unless $want{$_} == $want{$id};
-	print STDERR " $_";
-	$want{$_} = 0;
-    }
-    print STDERR ": want $want{$id}\n";
-    $want{$id} = 0;
-}

Deleted: trunk/gta02-core/bom/prettyord
===================================================================
--- trunk/gta02-core/bom/prettyord	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/prettyord	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,49 +0,0 @@
-#!/usr/bin/perl
-
-require "parser.pl";
-require "misc.pl";
-
-&parse;
-
-$out[0][0] = "Pos";
-$out[1][0] = "Qty";
-$out[2][0] = "P/N";
-$out[3][0] = "Description";
-$out[4][0] = "Value";
-$out[5][0] = "";
-
-for (sort keys %order) {
-    push(@{ $out[0] }, ++$n);
-    push(@{ $out[1] }, $order{$_}[0]);
-    @f = split(/\s+/, $_);
-    push(@{ $out[2] }, $f[1]);
-    my $dsc = $dsc{$_};
-    if (!defined $dsc) {
-	for (&eq($_)) {
-	    $dsc = $dsc{$_};
-	    last if defined $dsc;
-	}
-    }
-    print STDERR "$_: no description\n" unless defined $dsc;
-    push(@{ $out[3] }, defined $dsc ? $dsc : "???");
-    push(@{ $out[4] }, $order{$_}[1]);
-    push(@{ $out[5] }, sprintf("%.2f", $order{$_}[2]));
-}
-
-for (@out) {
-    push(@max, 0);
-    for (@{ $_ }) {
-	$max[$#max] = length $_ if length $_ > $max[$#max];
-    }
-}
-
-for ($i = 0; $i <= $#{ $out[0] }; $i++) {
-    for ($j = 0; $j != 6; $j++) {
-	my $s = $out[$j][$i];;
-	print $s if $j == 2 || $j == 3 || $j == 4;
-	print " " x ($max[$j]-length $s);
-	print $s if $j == 0 || $j == 1 || $j == 5;
-	print "  " unless $j == 5;
-    }
-    print "\n";
-}

Deleted: trunk/gta02-core/bom/workflow.fig
===================================================================
--- trunk/gta02-core/bom/workflow.fig	2010-02-07 13:59:06 UTC (rev 5826)
+++ trunk/gta02-core/bom/workflow.fig	2010-02-07 14:33:40 UTC (rev 5827)
@@ -1,147 +0,0 @@
-#FIG 3.2  Produced by xfig version 3.2.5a
-Landscape
-Center
-Metric
-A4      
-100.00
-Single
--2
-1200 2
-6 450 5850 3150 6525
-4 0 0 50 -1 14 12 0.0000 4 180 2700 450 6030 Source file (in SVN)\001
-4 0 0 50 -1 12 12 0.0000 4 150 1890 450 6255 Generated file\001
-4 0 0 50 -1 18 12 0.0000 4 210 1830 450 6480 Program (in SVN)\001
--6
-6 450 7875 6480 9000
-4 0 0 50 -1 12 12 0.0000 4 105 540 450 8055 .csv\001
-4 0 0 50 -1 12 12 0.0000 4 150 540 450 8280 .inv\001
-4 0 0 50 -1 12 12 0.0000 4 150 540 450 8505 .equ\001
-4 0 0 50 -1 12 12 0.0000 4 150 540 450 8730 .par\001
-4 0 0 50 -1 12 12 0.0000 4 150 540 450 8955 .ord\001
-4 0 0 50 -1 1 12 0.0000 4 195 2520 1350 8280 inventory with stock and cost\001
-4 0 0 50 -1 1 12 0.0000 4 195 3045 1350 8055 GTA02 EE component stock at FIC\001
-4 0 0 50 -1 1 12 0.0000 4 195 5130 1350 8505 part number equivalences, e.g., manufacturer vs. distributor\001
-4 0 0 50 -1 1 12 0.0000 4 195 2910 1350 8730 component to part number(s) map\001
-4 0 0 50 -1 1 12 0.0000 4 195 5025 1350 8955 list of parts to order, with price and component references\001
--6
-6 450 7425 3465 7875
-4 0 0 50 -1 12 12 0.0000 4 150 540 450 7605 .chr\001
-4 0 0 50 -1 12 12 0.0000 4 150 540 450 7830 .sub\001
-4 0 0 50 -1 1 12 0.0000 4 195 1695 1350 7605 part characteristics\001
-4 0 0 50 -1 1 12 0.0000 4 195 2085 1350 7830 parameter substitutions\001
--6
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
-	1 1 2.00 60.00 60.00
-	 7875 2700 7875 2925 6525 2925 6525 4725
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
-	1 1 2.00 60.00 60.00
-	 9000 2700 9000 5850 6525 5850 6525 6075
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
-	1 1 2.00 60.00 60.00
-	 3150 1350 3150 5850 6075 5850 6075 6075
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 6300 2700 6300 4725
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 6300 2025 6300 2475
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 6300 1350 6300 1800
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
-	1 1 2.00 60.00 60.00
-	 4950 1350 4950 4275 6075 4275 6075 4725
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
-	1 1 2.00 60.00 60.00
-	 3150 4500 5850 4500 5850 4725
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
-	1 1 2.00 60.00 60.00
-	 8100 4275 8100 4500 6750 4500 6750 4725
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
-	 11700 1575 11700 3150 10170 3150
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 6300 4950 6300 5400
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 6300 5625 6300 6075
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 6300 6300 6300 6750
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 6300 6975 6300 7425
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 6525 7650 6525 8100
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 9000 1350 9000 1800
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
-	1 1 2.00 60.00 60.00
-	 9000 1575 7875 1575 7875 1800
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
-	1 1 2.00 60.00 60.00
-	 9000 1575 10125 1575 10125 1800
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 7875 2025 7875 2475
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 9000 2025 9000 2475
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 7875 2925 7875 3375
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
-	1 1 2.00 60.00 60.00
-	 8955 3150 8325 3150 8325 3375
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 8100 3600 8100 4050
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 10125 2025 10125 2475
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
-	1 1 2.00 60.00 60.00
-	 10125 2700 10125 7200 6750 7200 6750 7425
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
-	1 1 2.00 60.00 60.00
-	 8955 4500 8100 4500
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
-	 9045 3150 10080 3150
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
-	 11700 4050 11700 4500 10170 4500
-2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
-	 10080 4500 9045 4500
-4 0 0 50 -1 2 16 0.0000 4 255 3345 450 675 BOM Processing - Workflow\001
-4 1 0 50 -1 12 12 0.0000 4 195 1890 6300 2655 gta02-core.lst\001
-4 1 0 50 -1 18 12 0.0000 4 165 1035 6300 1980 eeschema\001
-4 1 0 50 -1 14 12 0.0000 4 150 675 6300 1305 *.sch\001
-4 1 0 50 -1 18 12 0.0000 4 210 990 6300 4905 bom2part\001
-4 1 0 50 -1 18 12 0.0000 4 210 1110 6300 6255 part2order\001
-4 1 0 50 -1 12 12 0.0000 4 195 1890 6300 6930 gta02-core.ord\001
-4 1 0 50 -1 12 12 0.0000 4 195 1890 6300 5580 gta02-core.par\001
-4 1 0 50 -1 14 12 0.0000 4 195 1890 4950 1305 gta02-core.sub\001
-4 1 0 50 -1 14 12 0.0000 4 195 1890 3105 1305 gta02-core.inv\001
-4 1 0 50 -1 18 12 0.0000 4 210 975 6525 7605 prettyord\001
-4 1 0 50 -1 18 12 0.0000 4 165 1095 7875 1980 fic2vendor\001
-4 1 0 50 -1 18 12 0.0000 4 165 675 9000 1980 fic2inv\001
-4 1 0 50 -1 18 12 0.0000 4 210 855 8100 3555 gen2chr\001
-4 1 0 50 -1 12 12 0.0000 4 150 675 8100 4230 *.chr\001
-4 1 0 50 -1 12 12 0.0000 4 195 945 7875 2655 fic.equ\001
-4 1 0 50 -1 12 12 0.0000 4 150 945 9000 2655 fic.inv\001
-4 1 0 50 -1 18 12 0.0000 4 165 735 10125 1980 fic2dsc\001
-4 1 0 50 -1 14 12 0.0000 4 195 2700 9000 1305 inventory-fic-ee.csv\001
-4 0 0 50 -1 12 12 0.0000 4 150 540 450 7155 .lst\001
-4 0 0 50 -1 1 12 0.0000 4 195 2220 1350 7155 BOM generated by KiCad\001
-4 0 0 50 -1 12 12 0.0000 4 150 540 450 6930 .sch\001
-4 0 0 50 -1 1 12 0.0000 4 195 2010 1350 6930 schematics (for KiCad)\001
-4 0 0 50 -1 12 12 0.0000 4 150 540 450 7380 .gen\001
-4 0 0 50 -1 1 12 0.0000 4 195 2745 1350 7380 characteristics generation rules\001
-4 0 0 50 -1 12 12 0.0000 4 150 540 450 9180 .dsc\001
-4 0 0 50 -1 1 12 0.0000 4 195 2685 1350 9180 Textual component description\001
-4 1 0 50 -1 14 12 0.0000 4 180 675 11700 1305 *.gen\001
-4 1 0 50 -1 14 12 0.0000 4 150 675 11700 3780 *.chr\001
-4 1 0 50 -1 14 9 0.0000 4 135 1440 11700 4005 (acx, misc, ...)\001
-4 1 0 50 -1 14 9 0.0000 4 135 1800 11700 1530 (darfon, ralec, ...)\001
-4 1 0 50 -1 12 12 0.0000 4 150 945 10125 2655 fic.dsc\001




More information about the commitlog mailing list