|
|
07-21-2007, 01:12 PM
|
#31
|
Registered Member
Join Date: Jul 2007
Posts: 150
Country: United States
|
Pretty cool ... but I don't quite understand your diode clamp interface you used.
Could you explain how you make it work?
You connected directly to the injector via a 10K resistor?
And then you grounded the signal via a diode bridge? Is that to avoid having signals above 0.6V? Don't understand...
And what's VSS for?
I though you'd use a coil to detect the pulses so the laptop would be totally independant from the car's electrical circuit...
All you have to do now to compute duty cycle is count the number of samples above a threshold vs all the samples.
You may need to adjust with a constant, or adjusting the threshold since the ramp up slope is not vertical, but seems constant.
__________________
__________________
|
|
|
07-22-2007, 01:40 AM
|
#32
|
Registered Member
Join Date: Jan 2007
Posts: 771
Country: United States
|
We have gui
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:
http://opengauge.org/diympggauge/
***
*tank will persist when window is closed
Using this circuit, with right and left switched
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
*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
__________________
|
|
|
07-22-2007, 08:26 AM
|
#33
|
Registered Member
Join Date: Jan 2007
Posts: 771
Country: United States
|
Quote:
Originally Posted by sonyhome
Pretty cool ... but I don't quite understand your diode clamp interface you used.
Could you explain how you make it work?
You connected directly to the injector via a 10K resistor?
And then you grounded the signal via a diode bridge? Is that to avoid having signals above 0.6V? Don't understand...
|
the diodes are supposed to limit the max voltage going into the sound card, yes. The resistor is supposed to prevent the diodes from trying to clamp the vss and injector to .6 volts. The VSS side could use some work in my case.
Quote:
Originally Posted by sonyhome
And what's VSS for?
I though you'd use a coil to detect the pulses so the laptop would be totally independant from the car's electrical circuit...
|
To do Miles per Gallon, you need to know how many miles you have gone. A direct signal is much easier to parse than an induced signal in a coil. The resistor provides enough independance.
Quote:
Originally Posted by sonyhome
All you have to do now to compute duty cycle is count the number of samples above a threshold vs all the samples.
You may need to adjust with a constant, or adjusting the threshold since the ramp up slope is not vertical, but seems constant.
|
Yah, I just have a fudge number, there probably is a constant component to the injector pulse that could be factored in for greater accuracy.
*Note, if one were to add a "injectorPulseCount" field to the Trip class, and detect it like the vss pulses, then RPM and a constant value adjustment per pulse could be accomplished.
*also note that it is trivial to add more trips, like "trip 1", "trip 2", etc, and have them save to disk when the window is closed.
Here is the sample a mpg.properties file it creates in the current directory, you can tweak the thresholds and fudge factors (and even pipe in a .wav file of a recording of your probes):
#
#Sun Jul 22 11:13:47 CDT 2007
dummyFile=
tank.injHi=3984869
tank.vssTot=1076
fuelFudge=8000000.00
injThreshold=-30
vssThreshold=100
tank.sampleCount=3984876
distanceFudge=3200.0
|
|
|
07-22-2007, 02:55 PM
|
#34
|
|V3|2D
Join Date: Mar 2006
Posts: 1,186
Country: United States
|
thanks for the open source project
__________________
don't waste your time or time will waste you
|
|
|
07-22-2007, 06:37 PM
|
#35
|
Registered Member
Join Date: Dec 2005
Posts: 4,223
Country: United States
|
I am duly, duly impressed! (And in over my head.) Congratulations are in order!
|
|
|
07-23-2007, 06:29 PM
|
#36
|
Registered Member
Join Date: Jan 2007
Posts: 771
Country: United States
|
MPG cruise control
Thanks yall, I wanted to get this idea down somewhere after looking up DWL again.
Ok, so now the computer knows the mpg, big whup. BUT, that old parallel port... It is great for driving, say, a stepper motor, which could, say, be chained up to, say, the throttle to...
Yah, you got it...
Maintain a pre-determined MPG, exactly
|
|
|
07-23-2007, 06:40 PM
|
#37
|
Registered Member
Join Date: Dec 2005
Posts: 4,223
Country: United States
|
Inspired. Creativity is cool.
|
|
|
07-23-2007, 08:59 PM
|
#38
|
Registered Member
Join Date: Jan 2007
Posts: 771
Country: United States
|
Heh, a couple more goodies that could be added by providing things like car weight:
1. monitor acceleration (based on change to vss pulses) and other dynomometer/ET type stuff.
2. provide indication of acceleration/fuel consumption (find the peak when accelerating?) Might be good to have an audio signal for this so you can watch the road while accelerating. Make small adjustments in the gas pedal till the pitch frequency tops out.
3. Compute CDA, accelerate to 60 on a flat road and coast to a stop, let it figure out the rest.
|
|
|
07-24-2007, 06:04 AM
|
#39
|
Registered Member
Join Date: Dec 2005
Posts: 4,223
Country: United States
|
Ooo! I like the audio signal idea. Sort of like version 1.0 of the DWL cruise control.
An audio tone to help you stay near your target FE, rather than spending a lot of time glancing at a screen.
|
|
|
07-24-2007, 06:18 AM
|
#40
|
Registered Member
Join Date: May 2006
Posts: 443
Country: United States
|
Boy there are some smart folks in this old world. WOW
__________________
__________________
09 HCHII, w/Navi
07 Mazda3 S Touring, 5MT
Mild Hypermiler or Mad Man?
|
|
|
|
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
|
» Car Talk & Chit Chat |
|
|
|
|
|
» Fuelly iOS Apps |
|
» Fuelly Android Apps |
|
|
|