Fuelly Forums

Fuelly Forums (https://www.fuelly.com/forums/)
-   Experiments, Modifications and DIY (https://www.fuelly.com/forums/f9/)
-   -   diy mpg gauge (https://www.fuelly.com/forums/f9/diy-mpg-gauge-5319.html)

sonyhome 06-24-2008 10:05 AM

Try to load the applet on it, and just run it. You then also need to connect the car onto the microphone input.

jtiner 07-23-2008 09:22 PM

Quote:

Originally Posted by skewbe (Post 64732)
Tada!!, it's somewhere in the ballpark for a 98'tro!! Now anyone who has a laptop or tablet in their car can monitor MPG

***you need to be logged in to see the pictures,
You can go here to see the pictures without logging in:
https://opengauge.org/diympggauge/
***
https://www.gassavers.org/attachment....1&d=1185090285
*tank will persist when window is closed


Using this circuit, with right and left switched :rolleyes:
https://www.gassavers.org/attachment....1&d=1183986302


Zoom in on recording of line in (44100 8 bit stereo) for anylizing the waveform (vss* on top, injector on bottom) and general futzing in response. I don't think the diodes are doing much on the vss side :eek:
https://www.gassavers.org/attachment....1&d=1185091266
*Yellow line is default threshold for the vss,
*Cyan "..." injector
*I adapted the code to the latest wave form (inj pulse was on other channel and right side up?!?)
*vss is the misnamed Vehicle Speed Sensor. It really only measures distance. The computer watching the vss still has to keep track of time to figure out the velocity.



Speaking of code, here it is in it's entirety, 6 drama filled pages to chew on in one file called Mpg.java :
Code:

import java.awt.Color;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.text.NumberFormat;
import java.util.Properties;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.TargetDataLine;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Mpg extends Thread {
        static String propFile = "./mpg.properties";        // place to persist confguration and trips.
                                                                                                //Will be created on first run if it doesnt exist.

        static int injThreshold = Integer
                        .parseInt(getProperty("injThreshold", "-30")); // value above the noise

        static int vssThreshold = Integer
                        .parseInt(getProperty("vssThreshold", "100")); // value above the noise

        static double distanceFudge = Double.parseDouble(getProperty("distanceFudge", "3200.0"));

        static double fuelFudge = Double.parseDouble(getProperty("fuelFudge",        "8000000.00"));

        static String dummyFile = getProperty("dummyFile", "");

        // low level stats tracked in the "trip" class.
        class Trip {
                String name;

                long sampleCount; // num samples, used to compute elapsed time, good
                                                        // for about 58 billion hours @ 44100hz

                long injHi; // stores number of samples that were "HI" (injector was
                                        // open)

                long vssTot; // how many pulses from the vss, indication of distance
                                                // travelled

                public Trip(String _name) {
                        name = _name;
                }

                // real lightweight update process, gets called at end of audio chunk
                public void Update(long _sampleCount, long _injHi, long _vssTot) {
                        sampleCount += _sampleCount;
                        injHi += _injHi;
                        vssTot += _vssTot;
                }

                public String toString() {
                        return "name=" + name + ";sampleCount=" + sampleCount + ";injHi="
                                        + injHi + ";vssTot=" + vssTot;
                }

                public void reset() {
                        sampleCount = 0;
                        injHi = 0;
                        vssTot = 0;
                }

                public Trip load() {
                        sampleCount = Long
                                        .parseLong(getProperty(name + ".sampleCount", "0"));
                        injHi = Long.parseLong(getProperty(name + ".injHi", "0"));
                        vssTot = Long.parseLong(getProperty(name + ".vssTot", "0"));
                        return this;
                }

                public void save() {
                        properties.put(name + ".sampleCount", "" + sampleCount);
                        properties.put(name + ".injHi", "" + injHi);
                        properties.put(name + ".vssTot", "" + vssTot);
                }

                public double miles() {
                        return (double) vssTot / distanceFudge;
                }

                public double hours() {
                        return ((double) sampleCount) / (44100.0D * 3600.0D);
                }

                public double gallons() {
                        return (double) injHi / fuelFudge;
                }

                public double mpg() {
                        return gallons() > 0.0D ? (miles() / gallons())
                                        : Double.POSITIVE_INFINITY;
                }
        }

        class TripPanel extends JPanel {
                Trip trip = null;

                JLabel name = new JLabel("name");

                JLabel miles = new JLabel("miles");

                JLabel gallons = new JLabel("gallons");

                JLabel mpg = new JLabel("mpg");

                JLabel hours = new JLabel("gallons");

                JLabel mph = new JLabel("mph");

                JButton reset = new JButton("reset");

                /** used for limiting numbers to 4 decimal places*/
                NumberFormat fm = NumberFormat.getNumberInstance();

                public TripPanel(Trip _trip) {
                        trip = _trip;
                        setLayout(new GridLayout(1, 8));
                        setBorder(BorderFactory.createLineBorder(Color.BLACK));
                        add(name);
                        add(miles);
                        add(gallons);
                        add(mpg);
                        add(hours);
                        add(mph);
                        add(reset);
                        reset.addActionListener(new ActionListener() {
                                public void actionPerformed(ActionEvent e) {
                                        trip.reset();
                                }
                        });
                        fm.setMaximumFractionDigits(4);
                }

                public void upDateLabels() {
                        name.setText(" " + trip.name);
                        miles.setText(fm.format(trip.miles()));
                        gallons.setText(fm.format(trip.gallons()));
                        mpg.setText(fm.format(trip.mpg()));
                        hours.setText(fm.format(trip.hours()));
                        mph.setText(fm.format(trip.miles() / trip.hours()));
                }

        }

        private TargetDataLine m_line;

        protected boolean m_bRecording = true;

        Trip instant = new Trip("instant");

        TripPanel instantPanel = new TripPanel(instant);

        Trip current = new Trip("current");

        TripPanel currentPanel = new TripPanel(current);

        Trip tank = new Trip("tank").load();

        TripPanel tankPanel = new TripPanel(tank);

        public Mpg(TargetDataLine line, AudioFileFormat.Type targetType) {
                m_line = line;

                new Thread(new Runnable() {// thread to update the view every second
                                        public void run() {
                                                while (m_bRecording) {
                                                        instantPanel.upDateLabels();
                                                        instant.reset();// reset the instant trip after
                                                                                        // displaying it
                                                        currentPanel.upDateLabels();
                                                        tankPanel.upDateLabels();
                                                        try {
                                                                Thread.sleep(1000);
                                                        } catch (Exception e) {
                                                        }
                                                }
                                        }
                                }).start();

        }

        public void start() {
                m_line.start();
                super.start();
        }

        public void stopRecording() {
                m_line.stop();
                m_line.close();
                m_bRecording = false;
                System.out.println(" inj sampleCount = " + current.sampleCount
                                + " inj hi = " + current.injHi + " inj vssTot = "
                                + current.vssTot);
        }

        boolean ig = true;

        boolean vg = true;

        void processChunk(byte[] b, int c) {
                long ih = 0;
                long vt = 0;

                for (int x = 0; x < c; x += 2) {
                        int val = ((int) b[x] & 255) - 127;
                        if (val > vssThreshold && vg) {
                                vt++;
                                System.out.println(" vss going hi "
                                                + (current.sampleCount + (x / 2)));
                                vg = false;
                        }
                        if (val < 0) {
                                vg = true;
                        }

                        val = ((int) b[x + 1] & 255) - 127;
                        if (val < injThreshold) {
                                ig = true;
                        }
                        if (val > 0)
                                ig = false;
                        if (ig)
                                ih++;
                }
                instant.Update(c / 2, ih, vt);
                current.Update(c / 2, ih, vt);
                tank.Update(c / 2, ih, vt);
        }

        public void realrun() {
                byte[] buffer = new byte[m_line.getBufferSize()];
                while (m_bRecording) {
                        int c = m_line.read(buffer, 0, m_line.available());
                        if (c != 0) {
                                processChunk(buffer, c);
                        }
                }
        }

        public void run() {
                if ("".equals(dummyFile))
                        realrun();
                else
                        dummyrun();
        }

        public void dummyrun() {
                try {
                        String strFilename = dummyFile;
                        File soundFile = new File(strFilename);
                        AudioInputStream audioInputStream = null;
                        audioInputStream = AudioSystem.getAudioInputStream(soundFile);

                        int nBytesRead = 0;
                        byte[] abData = new byte[44100];
                        while (nBytesRead != -1) {
                                nBytesRead = audioInputStream.read(abData, 0, abData.length);
                                if (nBytesRead >= 0) {
                                        processChunk(abData, nBytesRead);
                                }
                                try {
                                        Thread.sleep(1000);
                                } catch (Exception e) {
                                }
                        }
                } catch (Exception e) {
                        e.printStackTrace();
                }
        }

        public static void main(String[] args) throws Exception {

                AudioFormat audioFormat = new AudioFormat(
                                AudioFormat.Encoding.PCM_SIGNED, 44100.0F, 8, 2, 2, 44100.0F,
                                false);
                DataLine.Info info = new DataLine.Info(TargetDataLine.class,
                                audioFormat);
                TargetDataLine targetDataLine = null;
                targetDataLine = (TargetDataLine) AudioSystem.getLine(info);
                targetDataLine.open(audioFormat);
                AudioFileFormat.Type targetType = AudioFileFormat.Type.WAVE;

                final Mpg recorder = new Mpg(targetDataLine, targetType);
                JFrame j = new JFrame("MPG Monitor!!!");
                j.setSize(640, 125);
                Container c = j.getContentPane();
                c.setLayout(new GridLayout(4, 1));

                JPanel hd = new JPanel();
                hd.setLayout(new GridLayout(1, 8));
                hd.add(new JLabel(""));
                hd.add(new JLabel("MILES"));
                hd.add(new JLabel("GAL"));
                hd.add(new JLabel("MPG"));
                hd.add(new JLabel("HRS"));
                hd.add(new JLabel("MPH"));
                hd.add(new JLabel(""));

                c.add(hd);
                c.add(recorder.instantPanel);
                c.add(recorder.currentPanel);
                c.add(recorder.tankPanel);

                j.addWindowListener(new WindowAdapter() {
                        public void windowClosing(WindowEvent e) {
                                recorder.stopRecording();
                                recorder.tank.save();//this just adds the trip fields to the properties object
                                try {
                                        properties.store(new FileOutputStream(new File(propFile)),"");
                                } catch (Exception f) {
                                }
                                System.exit(0);
                        }
                });

                j.setVisible(true);
                recorder.start();

        }

        static Properties properties;

        static String getProperty(String tag, String dflt) {
                String s = "";

                try {
                        if (properties == null) {
                                properties = new Properties();
                                try {
                                        properties.load(new FileInputStream(new File(propFile)));
                                } catch (Exception e) {
                                }
                                ;
                        }
                        s = properties.getProperty(tag);
                        if (s == null) {
                                s = dflt;
                                properties.put(tag, s);// will propogate default values to the
                                                                                // file
                        }
                } catch (Exception e) {
                }
                return s;

        }

}

So, believe it or not, it's all there. I think its a solid proof of concept anyway. Every car will be different, and there's plenty else that can be done to it: RPM column, invert signal/swap channel from mpg.properties file. A a collection of presets for different cars... But I, personally, am gonna try and do other stuff for a while :)

I am new to this forum and I have been reading the thread about diy mpg gauge using a laptop. Quite an ingenious idea! Very clever. I am ready to put this circuit together but I have a few questions: 1. What kind of diodes should be used? 2. The injector wire; is this an injector wire from an individual injector or the wire that controls all of the injectors? 3. Is it possible to hook this up to a palm pilot?

I am planning to set this up on a 1996 Chrysler concorde and if successful I will publish photos and a tutorial for this car.

sonyhome 07-23-2008 10:15 PM

[QUOTE=jtiner;112351]
1. What kind of diodes should be used? 2. The injector wire; is this an injector wire from an individual injector or the wire that controls all of the injectors? 3. Is it possible to hook this up to a palm pilot?
[QUOTE]
So I'm not the expert but I'll pitch in:
1- any diode, for example some from a power supply.
2- I think it's directly from one of the injectors.
3- Try to run the applet first. Load it on palm & I say save an audio file on an mp3 player, and play it back to the palm via a wire conecting both. If it works you should be OK.

jtiner 07-24-2008 08:37 AM

Thanks for the advise.

Are there parameters within the software that I need to program to the specifics of my car? In other words, if I am using an injector wire from one injector do I need to program the software for 6 cylinders (x6)? I am thinking of the calculations it needs to make for the entire engine, not just one cylinder.

sonyhome 07-24-2008 04:23 PM

Don't expect to have it right off the bat: I think there's a small config file to define parameters and tune to your car... Injector size, delay between open and gas flow, # cylinders, tire rotation speed, etc will impact the MPG reading. I think it's all in the original post/thread. You can also contact the guy who wrote this instead of my 2nd hand flaky info.

jim-frank 08-26-2008 08:01 AM

I've recently gotten this to work just fine. Here's my take on it.

1)Any silicon small signal diode will do. Radio Shack or scavenge. (Actually, a germanium diode will work, but you'll get a maximum signal of about 0.3 volts instead of 0.6 volts.)

2) Yes, use the injector wire from just one cylinder, whichever wire is easiest to get to is fine.

3) If your Palm Pilot has a stereo line level sound card input, you can do it. Otherwise, no deal. You'd also have to get the Java app ported to Palm OS. (I don't know how that would be done).

There are only four variables that need to be calibrated to your vehicle. These are called fuelFudge, distanceFudge, injThreshold, and vssThreshold.

They are found in the mpg.properties file that the program creates the first time you run it. You can open this file with any text editor, (with the program NOT running) and then save your changes before running mpg.java again.

I found some details that are scantily covered in the information above, and these are relevant.

Once you get the interface electronics wired up and connected to a soundcard, it's a good idea to run a recording of the signal. I use CoolEdit or Adobe Audition, but anything that will let you record and then zoom in on the waveform will do.

A peek into the java code reveals that the waveform is decoded for the program as numbers from -127 to +127. This number is the number of samples above or below zero that the waveform goes. This number is also what is referered to as the threshold variables in the program.

In my case, the injector waveform makes a pretty clean vertical line at the opening/closing of the injector, (like a square wave), so a threshold of near zero works OK. I adjusted mine to -8, which prevents false injector readings when the engine is off (noise signals will vary around the zero line by some amount.)

The vehicle speed sensor waveform in my car is a steep sided quasi-sinewave. A look at the waveform shows that it varies from -65 to plus 65 or so, and a threshold of about 10 works for me.

After you have set the threshold factors, you can set the fuelFudge and distanceFudge to correspond to your car.

Set the distanceFudge first by running down the road and having a helper (for safety's sake) monitor the speedometer versus the program's calculated speed. The program counts the number of speeds sensor pulses in a one second interval, then divides this by the distanceFudge to get the speed.

So, if the program shows that you are going faster than the speedometer, INCREASE the fudge factor. If it shows a calculated speed lower than the actual, DECREASE the fudge factor.

You can get pretty close with a little math. If the distance Fudge is 3200, (the beginning default value), you're going 60, and the program shows 70, then you're off by (70/60)*3200, or about 373. So try a fudge factor of 3573. When you get within a few percent, it's easier to just tweak the fudge factor by two to five units at a time, say from 3573 to 3575, then 3580, etc.

Over a long trip the TANK total miles (bottom box in the program) will be more accurate to tweak the fudge factor. So if you have 400 miles on the tank, and the program has calculated 375, you will have to decrease the fudge factor a bit.

Now that you have the speed calibrated, it's time to get the fuel factor set up. It works the same way, the program divides the total number of fuel injector pulses by the fuelFudge to get the actual fuel usage. You can get pretty close by topping off the tank, running the program while driving a quarter to half a tank off, then comparing the total gallons calculated with what it actually takes to fill the tank back up.

Same math will get you close, so if the program says it took 10 gallons, and it actually takes 8, you have to increase the fuelFudge by about 1.25 times. (The default is 8 million, so you'd want to try 10 million) It takes a bit of tweaking to get this one set up, but just a tank or so of fuel should get you pretty close indeed.

I hope this helps you all out.

Jim

curjones 09-02-2008 04:46 AM

I'm kinda lost here. May never get what you are doing so be gentle. Are you using the sound card already in the computer. Then do you have to enter some data to make this program work on the computer. Im trying to understand the basic concept. I have seen where you can buy programs that read gauges and sensors on the lap top.

theholycow 09-02-2008 05:12 AM

The idea is pretty cool: You use the existing sound card, since what comes over the injector wire resembles sound enough to be accurately recorded by the sound card. There's a program that analyzes that input, designed for this purpose; you just need to adjust that program.

curjones 09-02-2008 06:10 AM

So you buy the program, name,where and how much. Do you have to have a specific sound card.

theholycow 09-02-2008 07:12 AM

That's a good question about the program. I didn't know it existed until jim-frank posted about it above. Before that, I just knew that others have connected to sound cards, as in this thread:
https://www.gassavers.org/showthread.php?t=7608
(that link is in my thread about using the HF multimeter, too)

To do it that way, no specific sound card is necessary. The same is almost certainly true of the program jim-frank uses, which he says runs on Java; Java is somewhat hardware-agnostic, and uses the OS to deal with stuff like sound cards. I hope he comes back and posts the name of the program and tells us where to find it. A few minutes of googling failed to produce it for me.

I'd recommend trying it on a computer you're not afraid to hurt, first, just in case your fuel injection system uses high voltage or current or could otherwise damage the computer...after all, you are making a connection that no engineer ever expected you to make.


All times are GMT -8. The time now is 02:02 AM.

Powered by vBulletin® Version 3.8.8 Beta 1
Copyright ©2000 - 2024, vBulletin Solutions, Inc.