/* * Celestial Fusion * Software for digitized engine data acquisition and processing * * Author: James McCrae */ #include #include #include #include #include #include #define outport 0x3BC #define inport 0x3BD #define TACHBUSADDR 0 #define SPEEDBUSADDR 1 #define NUMINPUTSENSORS 2 #define GAUGERESOLUTIONX 480 #define GAUGERESOLUTIONY 480 //Vehicle specific constants #define MAXTACHOMETERRANGE 8000.0 #define TACHOMETERREDLINE 5500.0 //Global variables (for screen size, etc) int NUMINPUTSAMPLES=20; int RESOLUTIONX=640; int RESOLUTIONY=480; float PULSETIMECONSIDERED=1.0; float POLLDELAYSEC=0.05; float UPDATESPERSEC=10.0; /* Class declarations */ class Poll { public: Poll(); void setTimestamp(double timestamp); void setReading(unsigned char reading); double getTimestamp(); unsigned char getReading(); private: double timestamp; unsigned char reading; }; class ParallelPortIO { public: ParallelPortIO(); void setSimulated(bool simulated); bool getSimulated(); Poll *obtainPolls(int busaddress); void addPoll(int whichsensor, double timestamp, unsigned char reading); int pollSensor(int whichsensor); private: //number of samples held for each sensor dynamically determined Poll *inputsequence[NUMINPUTSENSORS]; int beginindex[NUMINPUTSENSORS]; bool simulated; }; class Simulator { public: Simulator(); void update(ParallelPortIO *ppio); private: int state; float rpm; float speed; int gear; int curpulses; double eventtimestamp; float missedlastpoll; }; class GUI { public: GUI(); void update(ParallelPortIO *ppio); void incrementPoll(); private: double lastupdate; int currentscreen; int lastpolls; int numpolls; double lastsecond; BITMAP *doublebuffer; BITMAP *logo; BITMAP *optionmenu; BITMAP *tachometer; BITMAP *numbers[10]; BITMAP *needle; BITMAP *whiteline; BITMAP *smallredline; BITMAP *bigredline; BITMAP *rpmx1000; void drawTachometer(ParallelPortIO *ppio); }; /* Function declarations */ void alleg_init(); void cmdLineError(); /* Main Method */ int main(int argc, char ** argv) { bool dosimulate=false; //parse command line arguments for (int i=1;isimulated=simulated; } bool ParallelPortIO::getSimulated() { return simulated; } /* * Note: To be used by Simulator only */ void ParallelPortIO::addPoll(int whichsensor, double timestamp, unsigned char reading) { //set reading and timestamp for poll inputsequence[whichsensor][beginindex[whichsensor]].setReading(reading); inputsequence[whichsensor][beginindex[whichsensor]].setTimestamp(timestamp); //iterate to next poll element in array beginindex[whichsensor]++; beginindex[whichsensor]=beginindex[whichsensor]%NUMINPUTSAMPLES; } int ParallelPortIO::pollSensor(int whichsensor) { int realdata=0; outp(outport, 1); int busdata=(inp(inport)&0xF0); outp(outport, 2); outp(outport, 0); realdata=(busdata/16)^8; return realdata; } Poll *ParallelPortIO::obtainPolls(int busaddress) { return inputsequence[busaddress]; } GUI::GUI() { float numx; float numy; int count=0; int eachnum=0; lastpolls=0; numpolls=0; lastsecond=0.0; doublebuffer=create_bitmap(RESOLUTIONX,RESOLUTIONY); clear_bitmap(doublebuffer); logo=load_pcx("images/uprlogo.pcx",NULL); optionmenu=load_pcx("images/optmenu.pcx",NULL); numbers[0]=load_pcx("images/0.pcx",NULL); numbers[1]=load_pcx("images/1.pcx",NULL); numbers[2]=load_pcx("images/2.pcx",NULL); numbers[3]=load_pcx("images/3.pcx",NULL); numbers[4]=load_pcx("images/4.pcx",NULL); numbers[5]=load_pcx("images/5.pcx",NULL); numbers[6]=load_pcx("images/6.pcx",NULL); numbers[7]=load_pcx("images/7.pcx",NULL); numbers[8]=load_pcx("images/8.pcx",NULL); numbers[9]=load_pcx("images/9.pcx",NULL); needle=load_pcx("images/needle.pcx",NULL); whiteline=load_pcx("images/whiteln.pcx",NULL); smallredline=load_pcx("images/smredln.pcx",NULL); bigredline=load_pcx("images/bigredln.pcx",NULL); rpmx1000=load_pcx("images/rpmx1000.pcx",NULL); //prepare image of tachometer //create it at one resolution (scale it as necessary for others) BITMAP *tachometerbig=create_bitmap(GAUGERESOLUTIONX,GAUGERESOLUTIONY); tachometer=create_bitmap(RESOLUTIONX*3/4,RESOLUTIONY*7/8); clear_bitmap(tachometerbig); clear_bitmap(tachometer); for (float i=-45.0;i<=225.0;i+=270.0*(200.0/MAXTACHOMETERRANGE)) { count=count%5; if (count==0) { numx=cos((i*256.0/360.0)+128.0)*170.0; numy=sin((i*256.0/360.0)+128.0)*170.0; draw_sprite(tachometerbig,numbers[eachnum],GAUGERESOLUTIONX/2+(int)numx-12,GAUGERESOLUTIONY/2+(int)numy-12); eachnum++; } if ((i+45.0)/270.0>TACHOMETERREDLINE/MAXTACHOMETERRANGE) pivot_sprite(tachometerbig,bigredline,GAUGERESOLUTIONX/2,GAUGERESOLUTIONY/2,150,5,ftofix(i*256.0/360.0)); else { if (count==0) { pivot_sprite(tachometerbig,whiteline,GAUGERESOLUTIONX/2,GAUGERESOLUTIONY/2,150,5,ftofix(i*256.0/360.0)); } else pivot_sprite(tachometerbig,smallredline,GAUGERESOLUTIONX/2,GAUGERESOLUTIONY/2,150,5,ftofix(i*256.0/360.0)); } count++; } draw_sprite(tachometerbig,rpmx1000,GAUGERESOLUTIONX/2-35,GAUGERESOLUTIONY/2+100); //scale tach to proper resolution stretch_sprite(tachometer,tachometerbig,0,0,RESOLUTIONX*3/4,RESOLUTIONY); lastupdate=double(clock()); currentscreen=0; } void GUI::update(ParallelPortIO *ppio) { int keyread; //update only 5 times per second if ((double(clock())-lastupdate)/CLOCKS_PER_SEC<=1.0/UPDATESPERSEC) return; lastupdate=double(clock()); clear_to_color(doublebuffer,16); draw_sprite(doublebuffer,logo,0,0); //main menu if (currentscreen==0) { draw_sprite(doublebuffer,optionmenu,50,100); } //tachometer else if (currentscreen==1) { drawTachometer(ppio); } draw_sprite(screen,doublebuffer,0,0); //handle keypresses if (keypressed()) { keyread=readkey(); if ((keyread&0xff)=='q') exit(0); if (currentscreen==0) { //main menu if ((keyread&0xff)=='1') currentscreen=1; } else currentscreen=0; } clear_keybuf(); } void GUI::incrementPoll() { numpolls++; } void GUI::drawTachometer(ParallelPortIO *ppio) { //trace out tachometer by rotating along an origin //"fixed" is an allegro-specific unit of measure for an angle //a 32-bit data structure that represents a fixed point number //(a complete circle in "fixed" is represented by 256) Poll *currentpolls; int count; float calculatedrpm; float needleangle; //draw tachometer sprite at correct scale draw_sprite(doublebuffer,tachometer,RESOLUTIONX/8,RESOLUTIONY/8); //process polls and calculate the rpm //calculated by considering the number of pulses to occur in a second //also note that 2 pulses occur for each revolution //preliminary formula: // E(pollreadingsinlastsecond)*60(sec/min)/2(pulse/revolution) calculatedrpm=0.0; count=0; currentpolls=ppio->obtainPolls(TACHBUSADDR); for (int i=0;ilastsecond+1.0) { lastpolls=numpolls; numpolls=0; lastsecond=(double)clock()/CLOCKS_PER_SEC; } } Poll::Poll() { timestamp=0; reading=0; } void Poll::setReading(unsigned char reading) { this->reading=reading; } void Poll::setTimestamp(double timestamp) { this->timestamp=timestamp; } double Poll::getTimestamp() { return timestamp; } unsigned char Poll::getReading() { return reading; } Simulator::Simulator() { state=0; rpm=800; speed=0; gear=1; curpulses=0; eventtimestamp=double(clock()); missedlastpoll=0.0; } void Simulator::update(ParallelPortIO *ppio) { double deltatime=((double)clock())/CLOCKS_PER_SEC-eventtimestamp; float deltarpm; float missedthispoll; int newreading; if (state==0) { deltarpm=-1000; } else if (state==1) { deltarpm=1000; } else if (state==2) { deltarpm=800; } else if (state==3) { deltarpm=600; } else if (state==4) { deltarpm=400; } else if (state==5) { deltarpm=200; } rpm+=deltarpm*deltatime; eventtimestamp=((double)clock())/CLOCKS_PER_SEC; //create new poll with eventtimestamp and approximate reading missedthispoll=((rpm*2/60.0)*deltatime+missedlastpoll)-(int)((rpm*2/60.0)*deltatime+missedlastpoll); newreading=(int)((rpm*2/60.0)*deltatime+missedlastpoll); ppio->addPoll(TACHBUSADDR,eventtimestamp,newreading); //state change if (state!=0) { if (rpm>3500) { rpm=1600; state++; gear=state; } if (state>5) state=0; } else if (state==0) { if (rpm<=800) state=1; } //make round off count in next poll missedlastpoll=missedthispoll; }