Järjestelmä on jaettu neljän osasysteemiin. Nämä ovat 1. prosessori, 2. anturit ja toimilaitteet, 3. käyttöliittymä
ja 4. fysiikkamalli.
Osien välisistä riippuvuuksista on suunniteltu malli
DFD-muodossa.
Simulaattori toteutetaan pääasiallisesti C++:lla, mahdollisesti osittain C:llä. Säikeitä ei käytetä vaan kontrolli
siirtyy yksinkertaisilla funktionkutsuilla. Käyttöliittymä ohjaa kaikkia muita osia kutsumalla tarvittavia
luokkien julkisia jäsenfunktioita.
Järjestelmän kontrollin siirtyminen on kuvattu Use Case Map notaatiota käyttäen. Use Case Map on korkean tason kuvausmenetelmä, josta voi lukea lisää pian ilmestyvästä kirjasta A Use Case Map Approach to High-Level Design of Object Oriented Systems, R.J.A. Buhr, R.S. Casselman.
Seuraavassa kaikki funktiot joita on tarkoitus kutsua prosessori-osasysteemin ulkopuolelta. Anturit ja toimilaitteet -osasysteemi kutsuu ainoastaan Liitäntäriman funktioita, kaikki muut funktiot on tarkoitettu käyttöliittymä-osasysteemin kutsumiksi.
typedef unsigned char byte; // tavu 8 bittiä
typedef long word; // sana 16 bittiä
class CPU
{
public:
// Konstruktori
CPU();
// Staattinen osoitefunktio, jolta luokan palvelujen käyttäjät voivat
kysyä viimeksi luodun instanssin
// osoitetta. Jos viimeisen luonnin
jälkeen jokin luokan instanssi on tuhottu, palautetaan NULL.
static CPU *Osoite ();
// Funktiot rekisterien arvojen muuttamista varten
void Aseta_a(byte arvo);
void Aseta_b(byte arvo);
void Aseta_d(word arvo);
void Aseta_x(word arvo);
void Aseta_y(word arvo);
void Aseta_sp(word arvo);
void Aseta_pc(word arvo);
// Destruktori
~CPU();
}; // CPU
class ohjaus
{
public:
/* Prosessori suorittaa yhden käskyn.
Palauttaa prosessorin pysähtymisen syyn.
0 = ei ole pysähtynyt
1 = breakpoint
2 = tuntematon käsky
Jos pysähtymisen syy oli breakpoint, palauttaa argumenttina kyseisen
breakpointin. Argumenttina palautetaan lisäksi kulunut aika sekunteina. */
int suorita_kasky(char *breakpoint, double *aika);
/* Kysytään rekisterien ja muistin sisältö.
Palauttaa argumenteissa rekisterit ja osoittimet muistien alkuun.
Muisti on merkkitaulukko. */
void rekisterit_ja_muisti(char *a, char *b, short *d, short *pc, short *sp,
short *x, short *y, char *cc, char *sis_muisti,
char *ulk_muisti);
/* Annetaan ehdot, joilla prosessori pysähtyy.
Palauttaa, oliko ehtolauseke laillinen vai ei.
0 = oikein
1 = virheellinen
Ehdot on merkkitaulukko. */
bool aseta_rekisteri_breakpointit(char *ehdot);
bool aseta_breakpointit(int suoritettuja_kaskyja, short data_alku, short data_loppu);
/* Annetaan tiedot, jotka tulostetaan lokiin.
void lokitulostustiedot(short muistin_alku, short muistin_loppu,
bool rekisterit, bool luku/kirj);
/* Asetetaan breakpointit päälle tai pois.
tila = 1: päälle
tila = 0: pois */
void breakpointit(bool tila);
/* Asetetaan lokitulostukset päälle tai pois.
tila = 1: päälle
tila = 0: pois */
void lokitulostukset(bool tila);
}; // ohjaus
class IO-laitteet
{
public:
// Konstruktori
IO-laitteet();
// Staattinen osoitefunktio, jolta luokan palvelujen käyttäjät voivat
kysyä viimeksi luodun instanssin
// osoitetta. Jos viimeisen luonnin
jälkeen jokin luokan instanssi on tuhottu, palautetaan NULL.
static IO-laitteet *Osoite();
// Ladataan suoritettava tiedosto EEPROMiin.
// Palauttaa, onnistuiko tiedoston avaaminen.
// TRUE = onnistui
// FALSE = ei onnistunut
bool Lataa_koodi(char *tiedostonnimi);
// Destruktori
~IO-laitteet();
}; // IO-laitteet
class Liitantarima
{
public:
// Konstruktori
Liitantarima();
// Riman kautta kulkevat tiedot mikrokontrollerin ja auton välillä
// Rimassa on 64 nastaa
// Staattinen osoitefunktio, jolta luokan palvelujen käyttäjät voivat
kysyä viimeksi luodun instanssin
//osoitetta. Jos viimeisen luonnin
jälkeen jokin luokan instanssi on tuhottu, palautetaan NULL.
static Liitantarima *Osoite ();
// Haetaan tietoa rimasta. Argumenttina annetaan nastan numero (1-64).
// Palauttaa jännitteen voltteina.
double Hae(int nasta);
// Päivitetään liitäntäriman sisältöä. Argumenttina annetaan nastan
// numero (1-64) ja sen jännite.
void Paivita(int nasta, double jannite);
// Destruktori
~Liitantarima();
}; // Liitantarima
Auton tila esitetään kahdessa eri ikkunassa. Toisessa auton sijainti radalla ja toisessa auton parametrit (voimat, suunta, nopeus jne.) Renkaista lähtevät voimat ovat autoon vaikuttavia kitkavoimia. Rengaskulma esitetään eturenkaiden kääntymisellä, samoin kuin ohjauksen kohdekulma (katkoviivalla piirretyt eturenkaat). Moottorin teho visualisoidaan auton rungossa olevalla palkilla, josta näkee kuinka monta prosenttia moottorin tehosta yritetään saada. Anturit ovat värillisiä ympyröitä, joiden sävyt vaihtelevat antureiden kirkkauden mukaan. Nopeusvektori lähtee auton rungon keskipisteestä. Auto myös pyörii keskipisteensä suhteen, riippuen missä suunnassa auto on. Muut arvot, eli kulmanopeus, rengasnopeus ja aika ovat skalaareja ja nämä näytetään numeroarvoina.
Rata näytetään kaksiuloitteisena ja auto liikkuu tätä rataa pitkin.
Muisti esitetään taulukossa heksadesimaalilukuina. Muisti-ikkunoita voi avata tarpeen vaatiessa lisää napista new ja siten voi seurata niitä muistiosoitteita, joista on kiinnostunut. Sisäiselle ja ulkoiselle muistille on omat ikkunansa.
Fysiikkamalliin yhteydessä olevat ohjelmiston osat ovat toimilaitelohko, käyttöliittymä ja ratatiedosto. Fysiikkamalli lukee radan tiedot rataoliolta, kun simulointi alkaa. Tasaisin väliajoin (luokkaa tuhansia kertoja sekunnissa kuvattavan mallin aikaa) käyttöliittymä välittää fysiikkamallille tiedon kuluneesta ajasta. Aikaväli ei ole täysin vakio, vaan riippuu edellisen prosessorin suorittaman käskyn pituudesta. Fysiikkamalli kysyy auton saamat ohjaussignaalit (renkaiden kulma ja moottorin teho) toimilaiteosalta.
Fysiikkamalli määrittelee auton tilan kyseisen ajan kuluttua ja antaa käyttöliittymälle (näytölle) tiedon auton tilasta (sijainti, asento, nopeus, kulmanopeus, rengasnopeus ja voimavektorit) sekä antureiden tarvitsemat tiedot (ledien etäisyydet teipistä ja rengasnopeus) toimilaitelohkolle. Tiedot välitettyään fysiikkamalli jää odottamaan seuraavaa yhteydenottoa.
Seuraavassa fysiikkaosan luokat ja julkiset jäsenfunktiot eli funktiot, joita voidaan kutsua sen ulkopuolelta.
// Fysiikkamalli
// Auto on fysiikkamallin keskeinen luokka, jonka tilan muutoksista
// muut ovat kiinnostuneita.
class FmAuto
{
public:
// Konstruktori, jota käyttöliittymä kutsuu simuloinnin alkaessa.
// Parametrit ilmoittavat auton paikan ja asennon rataolion
// ilmoittamalla radalla.
FmAuto (int alkupiste_x, int alkupiste_y, double alkuasento);
// Destruktori, joka nollaa staattisen yksityisen muuttujan id.
// Toteutettu inline-funktiona header-tiedoston lopussa.
~FmAuto ();
// Staattinen osoitefunktio, jolta luokan palvelujen käyttäjät voivat
// kysyä viimeksi luodun instanssin osoitetta. Jos viimeisen luonnin
// jälkeen jokin luokan instanssi on tuhottu, palautetaan NULL.
static FmAuto *Osoite ();
// Käyttöliittymä käskee fysiikkamallia liikahtamaan tietyn ajan
// eteenpäin. Aika on edellisestä kutsusta kulunut auton maailman aika.
// Paluuarvonaan funktio palauttaa tiedon, onko auto radan päällä.
// Lisäksi funktio palauttaa näytön haluamia tietoja muuttamalla
// funktioparametreja. Paikka on auton sijainti rataolion ilmoittamassa
// koordinaatistossa. Asento on auton kulma myötäpäivään positiiviseen
// y-akseliin nähden, [-pii..pii].
bool Auton_sijainti (double aika,
int *paikkax,
int *paikkay,
double *asento);
// Käyttöliittymä voi kysyä autoon vaikuttavia voimia. Nopeusvektorin
// kulma on rataan nähden ja voimavektoreiden auton runkoon nähden.
void Auton_voimat (Vektori *nopeus,
double *kulmanopeus,
Vektori *kitka_etu,
Vektori *kitka_taka,
double *rengasnopeus);
// Nopeusanturi kutsuu rengasnopeus-funktiota. Rengasnopeus tarkoittaa
// renkaan alapinnan nopeutta auton alustaan nähden. Paluuarvo m/s.
double Rengasnopeus ();
// Fotodiodikampa kutsuu etaisyys teipista -funktiota. Se palauttaa
// anturin numeron ilmoittaman ledi-fotodiodi -parin määräämän
// mittauspisteen pienimmän etäisyyden teipistä metreinä.
double Etaisyys_teipista (char anturin_numero);
private:
static FmAuto *id = NULL;
};
inline FmAuto::~FmAuto ()
{
id = NULL;
}
inline FmAuto
*FmAuto::Osoite ()
{
return id;
}
// Vektori on fysiikkamallin ja näytön käyttämä luokka.
class Vektori
{
public:
// Konstruktori
Vektori ();
// Summafunktio
Vektori operator+ (Vektori ensimmainen, Vektori toinen);
// Komponentit ovat julkisia.
// Suunta on radiaaneja myötäpäivään, [-pii..pii].
double suunta;
double pituus;
};
Toimilaiteosaan yhteydessä olevat ohjelmiston osat ovat prosessori, näyttö ja fysiikkamalli. Toimilaiteosa saa prosessorilta tiedon kuluneesta ajasta ja lukee prosessorin rimaoliolta tiedon toimilaitteille tulevista signaaleista. Toimilaiteosa kerää tiedot ja muokkaa ne fysiikkamallille moottorin tehoksi ja jarruvoimaksi sekä renkaiden asennoksi. Säännöllisin väliajoin fysiikkamalli kysyy tiedot toimilaitteilta. Toimilaitteet kysyvät tarvitessaan fysiikkamallilta rengasnopeutta ja fotodiodien etäisyyksiä ratateipistä. Toimilaiteosa tuottaa prosessorin rimaoliolle signaaleja jatkuvasti, mutta kulmapotentiometriä lukuunottamatta niiden informaatiosisältö voi muuttua vain niin usein kuin fysiikkamallia on kutsuttu. Tämä ei liene ristiriidassa todellisuuden kanssa, sillä auton on ehdittävä liikkua, jotta antureiden annoissa tapahtuisi muutoksia. Kontrollerissa pyörivän ohjausohjelman on todellisuudessakin osattava huomioida vasteen viipyminen.
Seuraavassa toimilaiteosasta ulos näkyvät jäsenfunktiot.
// Toimilaiteosa
// Laitteet on luokka, joka instantioi ja pitää sisällään kaikki
// toimilaitteet ja anturit. Se toimii näiden rajapintana ulkomaailmaan.
class TlLaitteet
{
public:
// Konstruktori, jota käyttöliittymä kutsuu simuloinnin alkaessa.
// Asettaa instanssin osoitteen staattiseen yksityiseen muuttujaan id
// Osoite-funktiota varten.
// TlLaitteet::TlLaitteet () : id(this)
// {
// ...
// }
TlLaitteet ();
// Destruktori, joka nollaa staattisen yksityisen muuttujan id.
// Toteutettu inline-funktiona header-tiedoston lopussa.
~TlLaitteet ();
// Staattinen osoitefunktio, jolta luokan palvelujen käyttäjät voivat
// kysyä viimeksi luodun instanssin osoitetta. Jos viimeisen luonnin
// jälkeen jokin luokan instanssi on tuhottu, palautetaan NULL.
// Toteutettu inline-funktiona header-tiedoston lopussa.
static TlLaitteet *Osoite ();
// Prosessori kutsuu käskyn suoritettuaan.
// Aika on simuloinnin alusta kulunut auton maailman kokonaisaika.
// Funktio lukee liitinluokan arvoja ja muuttaa niitä paluuarvonaan.
void Keraa_tiedot (double aika);
// Näyttö kutsuu saadakseen fotodiodianturin havainnoiman
// kirkkauden. Anturin_numero on välillä 1 - 6.
// Paluuarvo välillä [0..1].
double Heijastunut_kirkkaus (char anturin_numero);
// Näyttö kutsuu saadakseen moottorin prosentuaalisen tehon,
// joka moottorista yritetään saada. Paluuarvo välillä [0..100].
double Tehoprosentti ();
// Auto kutsuu halutessaan moottorin hetkellisen tehon yhteensä
// molemmille akseleille. Auto itse tietää jakautumissuhteen.
// Paluuarvo watteina.
double Moottorin_teho ();
// Auto kutsuu halutessaan tietää molempien akseleiden yhteisen
// jarrutusvoiman. Auto itse tietää jakautumissuhteen.
// Paluuarvo newtoneina.
double Jarruvoima ();
// Näyttö kutsuu halutessaan tietää asennon, johon ohjaus on
// matkalla. Paluuarvo on eturenkaiden kulma radiaaneissa
// myötäpäivään auton runkoon nähden. Arvo on välillä [-pii/2..pii/2].
double Ohjauksen_kohdekulma ();
// Näyttö tai fysiikkamalli kutsuu halutessaan renkaiden kulman
// tietyllä hetkellä. Paluuarvo on eturenkaiden kulma radiaaneissa
// myötäpäivään auton runkoon nähden. Arvo on välillä [-pii/2..pii/2].
double Rengaskulma ();
private:
static TlLaitteet *id = NULL;
};
inline TlLaitteet::~TlLaitteet ()
{
id = NULL;
}
inline TlLaitteet
*TlLaitteet::Osoite ()
{
return id;
}
Rataolion luokkakuvaus:
// Rata
class Rata
{
public:
Rata ();
~Rata ();
// VektoriDeque on kahteensuuntaan linkitetty lista luokka,
// jonka arvot ovat luokan Vektori komponentteja.
VektoriDeque ratavektorit;
// Mittakaava metreissä (kuinka monta metriä on yksikkö).
double mittakaava;
// Teipinleveys metreissä.
double teipinleveys;
// Paluttaa seuraavan ratavektorin.
Vektori anna_seuraava ();
// Paluttaa jono-osoittimen sen hetkisen vektorin.
Vektori anna_tama ();
// Paluttaa edellisen ratavektorin.
Vektori anna_edellinen ();
// Siirtää osoitinta eteenpäin.
void taaksepain ();
// Siirtää osoitinta taaksepain.
private:
// Jonon osoitin.
Vektori *osoitin;
}