The online racing simulator
Searching in All forums
(56 results)
rheiser
S2 licensed
Not JInSim related, but...

I think you're running into a byte ordering problem. The decimal number 536936448 is 0x20010000 in hex. The decimal number 288 is 0x120 in hex. So it looks to me like the least significant bit (LSB) is first. You can try doing this in your code instead of the readInt():


int nodes = di.readByte() + (di.readByte() << 8) +
(di.readByte() << 16) + (di.readByte() << 24);

You'll have to do that with all value types larger that a byte. And you might have to mask the bytes returned from readByte with 0x000000ff (i.e., di.readByte() & 0x000000ff).

If would probably be best to abstract the calculation into a method call for each data type you need, to save typing

Try that and see if it works.
rheiser
S2 licensed
Quote from wheel4hummer :Wouldn't it be better to use a DAC instead?

Actually, a lot of DACs use PWM internally. I just didn't want to add another chip to his circuit when it's easily done with the microocontroller.
rheiser
S2 licensed
OK, I'll give it a try. I'll start at LFS and end at the gauge. The links after each section apply to that section.

1) Get the data from OutGauge. There are a number of libraries that help you do this. I wrote the first version of JInSim, so I'll use that as an example. In fact, there's an example program that uses OutGauge to control a GUI speedometer.

http://jinsim.sf.net

2) Send the data out. You'll use the data in exactly the same way, except instead of using it to update a graphical gauge, you'll send it out a serial (or USB) port.

http://users.frii.com/jarvi/rxtx/

3) Turn the number into a voltage. At the other end of your cable, a microcontroller circuit will be attached. There are a number of microcontrollers with built-in serial or USB support. All the playing around that I've done is will Atmel microcontrollers. You'll need to write another program for the microcontroller. This one will be written in assembler or C. It will need to take the number you sent down the serial cable and vary the voltage coming out of one of the pins on the microcontroller. A lot of people use a simple hack called Pulse Width Modulation (PWM). You'll need to figure out what voltage corresponds to what reading on the gauge to figure out how to turn your number into the voltage.

http://www.atmel.com
http://www.avrfreaks.net
http://www.embedded.com/story/OEG20010821S0096
http://www.youtube.com/watch?v=Q-EK1x_f39s

4) Hook up the gauge. One wire of the gauge will hook up to a circuit that comes from one pin of the microcontroller, and the other wire will go to ground.
rheiser
S2 licensed
kyler,

I think what he was talking about are these gauges: http://www.mikesflightdeck.com/diy_aircore_instruments.htm

What you want to do takes a bit more than programming. I haven't seen any inexpensive kits for hooking up gauges the way you'd like to. OutGauge gives you a bunch of numbers (e.g., 4000 if the engine is revving at 4000 RPM). Most gauges fit to work in a sim cockpit take some kind of variable voltage over range of their movement (e.g., 1.2v = 4000 RPM on the dial). Your exercise is to turn a number coming from OutGauge into voltage going into the tachometer. There are a number of write-ups from people who have done it. Usually it involves writing a program that can grab the number from OutGauge, send it out the serial port on the computer to a microcontroller circuit that takes the number and uses something like PWM to turn it into a variable voltage. The hard part is the electronics, and writing the code for the microcontroller. If you are interested in learning about these things, it's well worth it, especially if imagining the project done keeps you going. This was just a quick overview, but you can see it's not really just a case of connecting the guage and having it work somehow. There are people here who have done it, and hopefully they'll point you to their write-ups, or do a write-up if they haven't yet. Good luck!
rheiser
S2 licensed
Quote from amp88 :

To make the situation a bit clearer, here's the recursive method to retrieve the file details:
.
.
<snip>
.
.
Sample output for a file would be the following:

0 ms get file name
0 ms get absolute file path
3 ms get file size
3 ms get last modified
8 ms add new file object

So, we can see that retrieving the file size and the last modified date are taking the vast majority of the time for each file

The only way I can think of escaping the network overhead you're incurring now is to create a service of one kind or another on the target machine that you can call to get the information you're requesting all in one transfer. I'm not sure what the requirements for the program you're writing are, though. So, that might not even be a consideration.
rheiser
S2 licensed
File.listFiles() is a handy method, but I don't think it's going to solve amp88's problem. The File objects returned are just abstract representations of the files on disk. Each time you call length() or lastModified(), it fetches the data again.

amp88 - have you done any profiling with hprof or something to see where time is being spent at a finer grain?
rheiser
S2 licensed
I took a look at NodeLapInfoResponse, and it looks like we were trying to read in a NodeLaps for the max number of players every time. I changed it so we are now just reading in a NodeLap for each of the existing players. I think this may fix the problem. If you don't want to check out the latest source on SourceForge, just go to NodeLapInfoResponse, and in the construct() method, change "MAX_PLAYERS" to "numberPlayers".

Let us know if this works! Thanks.
rheiser
S2 licensed
I'm probably at the extreme end of this discussion. In my real life job, I worked on a project that generated JUnit tests from submitted code. See <http://www.junitfactory.com>.

This is usually nice to have for legacy code that has little to no tests written for it. The tests are easy and cheap to regenerate, so the coding model looks something like: Generate tests -> Run tests -> Verify and fix failures -> Add features -> <repeat>

The initial version of JInSim I wrote test first. It's a good way to make sure you've thought through the different user scenarios within the program you're writing, and end up with tests at the end. If you have tests you can be more confident that changes you've made in your code don't break something else. With modern IDE's refactoring capabilities, even the test maintenance isn't as much of a hassle as you'd think.

The biggest mistake I made when I submitted JInSim to SourceForge was not checking in the tests along with it. Tests have another nice side effect in that they serve as a type of documentation for the code. In an open source environment, this is invaluable when adding developers. If they add code and break the tests, they have something to point to when asking questions from the seasoned developers on the project.

Having said all of that, it's very hard to convince someone who doesn't believe in the value of unit testing to write unit tests.
rheiser
S2 licensed
James,

Here's a quick and dirty program I threw together that demonstrates what you want, I think. Compile this and run it with the xml filename as the first argument and it will put up a rudimentary JFrame with a tree in it containing nodes populated from the xml file. The key is the Document classes. From there you can get any DOM type information you want from the top level node through it's children.

package sandbox;

import java.io.File;

import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ReadXMLDocument {

private String filename;

public ReadXMLDocument(String xmlFilename) {
filename = xmlFilename;
}

public void run() {
try {
File xmlFile = new File(filename);

DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = builder.parse(xmlFile);

NodeList nodes = document.getChildNodes();

DefaultMutableTreeNode root = new DefaultMutableTreeNode(xmlFile.getName());

populateNodeFromDOM(root, nodes);

JTree tree = new JTree(root);
JFrame frame = new JFrame();
frame.getContentPane().add(tree);
frame.validate();
frame.pack();
frame.setVisible(true);

} catch (Exception e) {
e.printStackTrace();
}
}

private void populateNodeFromDOM(DefaultMutableTreeNode parentNode, NodeList domNodes) {
for(int i = 0; i < domNodes.getLength(); i++) {
Node currNode = domNodes.item(i);
String nodeName = null;
if(currNode.getNodeName().startsWith("#")) {
nodeName = currNode.getNodeName() + ": " + currNode.getTextContent();
} else {
nodeName = currNode.getNodeName();
}
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(nodeName);
populateNodeFromDOM(newNode, currNode.getChildNodes());
parentNode.add(newNode);
}
}

static public void main(String[] args) {
ReadXMLDocument reader = new ReadXMLDocument(args[0]);
reader.run();
}


}

rheiser
S2 licensed
Quote from amp88 :
My current bat file to run the program is "java finances/FinancesStart"

I think there are a couple things that you need to do differently. Firstm I'll assume that "FinancesStart" is your main class, and isn't in a package (i.e., it doesn't have a "package" directive at the top of the .java file). Second, you don't specify the name of the library you want to include on the classpath, so let's pretend it's called "foo.jar" and it's in a folder called "lib" under your current directory. I'll also assume you're using Windows. If you're using a *nix variant, the only thing that changes is the separator character in the classpath arg, which will change to a colon from a semicolon. Here's the command line:

java -cp "lib/foo.jar;finances" FinancesStart

The "-cp" arg tells the java VM to look in "lib/foo.jar" and the directory "finances" for the classes it wants to use. If FinancesStart is in a package called "finances" (i.e., there is a line that says "package finances;" at the top of FinancesStart.java), then the command line would look like this:

java -cp "lib/foo.jar;." finances.FinancesStart

*Note the "." in the classpath arg
Last edited by rheiser, . Reason : Clarifying OS
rheiser
S2 licensed
Great post, Todd (post #30). I was using "dynamic camber" because I couldn't remember what it was called on the setup screen. The term "live camber" is what I meant. I also just learned you can watch the dynamic camber through the Shift-L or F9(?) layovers during a replay. I was watching the tire temps and trying to watch dynamic camber in the F9 screen the old fashioned way -- while driving
rheiser
S2 licensed
Quote from Forbin :It might simply be that LFS's tire model is more-camber dependent than you might expect. You get better results from simply keeping the tire level mid-corner than you do by keeping the tire temps relatively even.

I was under the possible misapprehension that the two went hand-in-hand. It seems like every setup guide begins with instructions to drive laps and adjust camber until the tire temps are even across each tire.

When I got to a point where this was the case, the dynamic camber numbers seemed appropriate. On the one I was working on last night, my lap times came down pretty consistently. At least until I started mucking with settings that are less measurable (at least at my experience level) -- like track bar settings.

So I started looking at the dynamic camber settings for a "reality check", instead of the static numbers on the left. What are the differences between these two values in the Garage?
rheiser
S2 licensed
What Tristan and rjm said.

It's funny, because I just noticed this recently. I've been away from LFS for about a year, and just started getting some seat time in again. Obviously, the stock setups that I had made me a moving chicane, so I procured some "better" setups from kindly fellow racers. Immediately, my lap times came down significantly.

I still wasn't keeping up with the faster folks, so I started twiddling copies of the setups I'd just received. The first thing I noticed was how extreme the camber settings were. The inconsistent heat across the tire after ten laps was pretty incredible. The outsides of the tires never got anywhere near optimal operating temperature, whereas the insides came up quickly and never really got so hot that they became greasy.

I spent a couple of hours making more reasonable setups, but as others have said, there is rarely a race over 5 laps, let alone ten. My inclination is to say that there's an error somewhere in the tire model, but I have no way to defend that position
rheiser
S2 licensed
I was talking about a general XML schema for race results -- like maybe one created by the SCCA or some tech savvy race series. I didn't find one for auto racing, but I did find one for regattas
rheiser
S2 licensed
Quote from filur :It would probably be quite simple to convert an LFS-specific format into what you have in mind.

No doubt.

I just went back and read the first message, and realized that the point is to make a common format for multiple add-ons. I'm not sure what the actual use case is.

I wonder if there is a generalized schema for race results. I'll have to do some investigation.
rheiser
S2 licensed
I'm coming to this late, so I apologize for the disruption

I don't think I understand the goals behind this stats format? Multiple LFS add-ons and utilities are going to save in this format? To what end? Maybe I'm missing an angle, but the thrust of the discussion seems to be around displaying results on a web page. That seems like an "end result", rather than a "midpoint for exchange".

Now if the idea was to make an XML schema that held generic race data, that gets more interesting. If I could take the XML file generated from any sim (or any auto race, for that matter), and transform it in the same way as any other, that seems useful. I could write a single XSLT stylesheet for all of the results from the different sims my large league or team uses. I could easily compare laps on the same track run on different sims. You get the picture.

A schema that just mirrors the results of the LFS network packets doesn't seem as useful. Make the information race-centric, instead.

quick example:
<event>
<track name="Monza" configuration="2005 F1"/>
<qualify startTime="1176910077">
<drivers>
<driver name="Rob Heiser" car="BMW Sauber">
<lap number=1 time="80345"/>
<lap number=2 time="101988">
<pit time="10656"/>
</lap>
</driver>
<driver name="John Doe" car="BMW Sauber">
<lap number=1 time="0">
<dnf/>
</driver>
</drivers>
</qualify>
<race>
.
. Similar to qualifying
.
</race>
</event>

Obviously that's not fleshed out enough. Whether the race and qualify elements are lists of drivers or laps is beside the point, also. What's interesting is information that's shared between all forms of auto racing, rather than information that's specific to a particular sim.
Last edited by rheiser, . Reason : Forgot &quot;code&quot; tags
rheiser
S2 licensed
I agree with Karl, if a buffer is getting re-used make sure it's cleared to all zeroes before using it again. Depending on what programming language it's written in, you may have to zero it out right from the start.
rheiser
S2 licensed
Quote from Stuff :ISM is for identifying the server, not anyone on it. If you get guest and especially no name field, that says single player to me.

So packets from a single player instance of LFS are being sent to the relay at isrelay.liveforspeed.net and it's in turn sending that to the connected relay clients? Or these packets are being sent when a new relay client connects? I guess I'm not sure where single player instances of LFS relate to the relay.
ISM packets from InSim Relay
rheiser
S2 licensed
I'm receiving ISM ("InSimMulti") packets from the relay that identify as "guest" (for the "Host" field) and contain an empty "Name" field. Reading the InSim.txt file, this seems to indicate someone is joining a game that's not in multiplayer mode. Is this a correct reading? Or did I miss something somewhere?
rheiser
S2 licensed
Nice summary. I could've used it while I was first developing JInSim
rheiser
S2 licensed
Yep, the new features of Java 6 (Java SE 6? Java 1.6? What's with Sun and their versions?) Anyway...the new features are nice. I'm trying to stay 1.4 compliant for right now. You'll notice I don't use things like parameterized types in JInSim. I'm not doing anything fancy with locks either, so the cool new Lock objects wouldn't make a difference, I don't think. Thanks for pointing them out, though. It made me sit down and check out the feature more deeply.
JInSim v0.3 released
rheiser
S2 licensed
I'll announce new releases on this thread from now on instead of spamming the forum with new threads. The new release includes:

- An experimental client using the selectable sockets from Java's nio package.
- Fixes for thread synchronization (thanks Brilwing!)
- Fixes and additions to the request and response classes.
- Additions to InSimHelper
- Fixes to the packaging so that the speedometer example can find its images
- Fixes to some of the math on the RaceEventClient
- More documentation.
rheiser
S2 licensed
Thanks for the feedback, Brilwing -- I really appreciate it. I've been working on the thread synchronization and added your suggestions. The bug in ResultResponse was a cut and paste error :guilty:

I plan to make a new release today, so these should be fixed in that release as well as in CVS.
rheiser
S2 licensed
Quote from Victor :Maybe you need to convert your distance values first?

No, I'm doing that correctly. Here's the pertinent code snippet:


// private float ANGLE_CONSTANT = 65536.0f/360.0f;
CompCar challenger = event.getChallenger();

SetDirectCameraRequest request = new SetDirectCameraRequest();
request.setCameraType(StateResponse.CUSTOM_VIEW);
request.setFlags((short)(StateResponse.ISS_SHIFTU | StateResponse.ISS_VIEW_OVERRIDE | StateResponse.ISS_SHIFTU_NO_OPT));

InSimVector pos2 = new InSimVector(0, 65536 * 10, 65536 * 10);
InSimVector cameraPosition = challenger.getPosition().add(pos2);
request.setPosition(cameraPosition);

double heading = Math.toDegrees(Math.atan2((cameraPosition.getY()- challenger.getPosition().getY()),(cameraPosition.getX()- challenger.getPosition().getX())));
double pitch = Math.toDegrees(Math.atan2((cameraPosition.getZ()- challenger.getPosition().getZ()), (cameraPosition.getY()- challenger.getPosition().getY())));

request.setHeading((short)(heading * ANGLE_CONSTANT));
request.setPitch((short)(pitch * ANGLE_CONSTANT));

send(request);

The parts I'm unsure of are:

the call to setCameraType()
the call to setFlags()
the correct calculations of theta and phi with respect to the coordinate system
Camera positioning with InSim
rheiser
S2 licensed
I'm trying to position a camera using CPP packets, but need help because I'm obviously doing something wrong. If I want to position the camera absolutely somewhere in the world, how do I do it? What flags do I set in the CPP packet? What does "Follower could not find position" mean when it prints out in red on the screen (during a multiplayer replay if it makes a difference)? Here's what I'm doing in general:

* Picking a world position to point the camera at.
* Setting the position in the CPP packet to +10 on the y-axis and +10 on the z-axis away from the position.
* Taking the arctangent of the delta between the camera and world positions y-axis divided by the difference between the x-axis (arc2(dy/dx)) for the heading of the camera.
* Taking the arctangent of the delta between the camera and world positions z-axis divided by the difference between the y-axis (arc2(dz/dy)) for the pitch of the camera.

The camera seems like it ends up really close to the track. I can't tell if it's pointed in the correct direction or not. Help!
Last edited by rheiser, . Reason : had &quot;arc2(dz/dxy)&quot; instead of &quot;arc2(dz/dy)&quot;
FGED GREDG RDFGDR GSFDG