vi er her !!!

02120

DifferentielGPS
home

RTCM konvertering

Da vi skulle vælge en datastruktur til dekodning af RTCM signalet var der to hovedspørgsmål:
  • Hvordan laver vi repræsentationen af de binære strenge
  • Hvordan skal data for signalet opbevares

Valget af data struktur til bits

En meget væsentlig datastruktur i vores program er valget af bitsrepræsentationen. Der er ingen decideret bittype i java og boolean typen fylder 8 bits, hvilket giver 7 overflødige bits per bit. Dataene modtages, som bytes repræsenteret ved integers. Men de skal rulles og de to første overflødige "indpakningsbits" skal smides væk. Rulning af byten vil sige, at man spejlvender den, så den bit, der er først før, efter rulningen er den sidste bit og så videre. Herefter skal de samles til 30 bits ord og paritettjekkes. Efter dette tjek, skal den data, der er gemt i ordet kunne trækkes ud. Et ord indeholder flere forskellige variable, som hver har sin faste plads i ordet. Det vil sige, at første bit måske er én selvstændig variabel, mens de 5 næste bit til sammen danner en anden variabel. Derfor er det vigtigt at vælge en repræsentation af bits, som er dynamisk nok til nemt at blive konverteret.
Der er to faktorer vi har overvejet i vores valg af repræsentation af bitstrenge. Man kan have en bitstreng som en ny klasse eller som et indbygget type. Og man kan vælge imellem, om den indbyggede type skal være et boolean array eller en integer.
Da ordene er af størrelsen 30 bits kan de gemmes i én integer på 32 bits. Dette mindsker spildet af bits til 2. Hvis man vælger at bruge et boolean array, kommer man op på 30 booleans á 8 bits lig med 240 bits mod de 32 bits på en integer. Til gengæld er booleanværdierne nemmere at regne med. Hvis man skal bruge integer, skal man ved hver brug med en udregning udtage den bit, man skal se på. Dette giver flere udregninger per sammenligning af bits og ville give paritettjekket væsentligt flere udregninger og gøre det mindre overskueligt. Man ville for eksempel ikke kunne skrive
bitstreng_1[23] == bitstreng_2[23]
I stedet skulle man lave noget som:
bitstreng_1.getValue(23) == bitstreng_2.getValue(23)
hvor getValue så skulle returnerer indholdet af bit 23 som boolean. Det ville kræve en udregning af 2 i 23 potens, som skulle bitvist sammenlignes med den integer variabel, som indeholder bitstrengen.
Således er valget af boolean det nemmeste at programmere. Det er ikke den hukommelsesmæssigt mest optimale løsning, men beregningsmæssigt er den bedre.
Når man vælger at repræsentere bits med boolean, synes en særskilt klasse til repræsentation overflødigt. Dette ville gøre koden længere, da man skulle til at lave funktionskald til at få de enkelte bits. Derfor vil en let løsning være at lade bits repræsenteres ved boolean arrays og lave statiske metoder, som kan konvertere mellem boolean arrays og integers.
En smart måde at kombinere fordelene fra de to opbevaringsmuligheder er, at gemme dataene som heltal, mens de blot opbevares, og konverterer dem til boolean bitstrenge, nær der skal laves udregninger med dem. Det vil ikke kræve de store udregninger, når der skal regnes med bittene, og der vil ikke være en masse spildplads ved opbevaring. Denne metode har vi valgt at bruge.

Datastruktur til opbevaring

Som beskrevet i Teoretisk baggrund består RTCM signalet af en række binære ord, som er samlet i beskeder (frames). Ud fra disse fås informationer om de enkelte satellitter. Vi har derfor fundet det naturligt, at have en tredeling af datastrukturen til RTCM signalet i ord, besked og satellit.

Et ord indeholder de 30 bits data og kan paritetstjekke sig selv. Oprindeligt havde vi tænkt, at ordet skulle have en information om dets nummer i rækken, så man havde en metode, som kunne hente data fra ordene. Dette viste sig dog at være et mindre egnet system, da man i så fald skulle til at overveje hvad der skulle ske, når der blev bedt om data, som ordet ikke indeholder. Vi lod derfor beskeden håndtere data. Således kan man kun bede om alt data fra ordet, eller en delmængde af den.
Da vi under dataopsamling ikke laver en hel besked, før alt data er samlet, er det nødvendigt, at ordklassen kan genkende første ord, og udtage beskedlængden af andet ord.

Beskedsklassen skal konvertere det binære data fra ordene i en fuld besked til satellitinformationer. Da vi lavede klassen til beskeder, var det oprindeligt tanken, at beskeden skulle laves af de klasser, som står for dataopsamlingen. Senere blev vi enige om, at det ikke skulle være tilfældet. Men som følge af det, har vi lavet beskedklassen, så den kan opbevare data. Hvis man kun var interesseret i, at den skulle kunne konvertere det binære data til satellit informationer, kunne man lave en klasse, som blot har én statisk offentlig tilgængelig metode til dette. Men da opbevaring var tanken, lavede vi metoder til at hente ord ud fra beskeden med mere. Da disse metoder bruges internt i klassen har vi valgt at lade dem blive.
Som nævnt under TEORI REFERENCE, må man kende den rækkefølge ordene blev modtaget i. Ud fra dette kan man finde de korrekte satellitdata. Hvis data opbevares i et array, kan beskednummeret findes ud fra indekseringen af dette. Dette har vi derfor valgt, så der ikke er behov for en eksplicit nummerering af ordene.

Satellitklassen er blot en opbevaringsklasse. Vi skal ikke i stor stil regne på dataene, men blot gemme dem og hente dem frem.

Buffere

Da det RTCM signal vi får fra gpsmodtagerne og det internetbaserede GPSNet og er det samme, er det logisk at lave en fælles læsemetode, som de forskellige enheder benytter sig af. Dette har vi valgt. Vi startede som ovennævnt med at samle dataene i ordklassen. Men da vi skulle vælge, hvordan dataene blev sendt videre i systemet, blev vi enige om at lave en buffer til den.
Vi har kaldt bufferen for en CRW Buffer, for "Cyklic Read Write Buffer". Idéen med den er, at der skal kunne læses og skrives fra den næsten simultant. Buffersystemet bruges både til at opbevare de bytes, som man endnu ikke har konverteret til ord, samt at opbevarede færdige ord i (dog ikke i én og samme buffer).
En CRW Buffer består af et array med et skrive- og et læseindeks. Når bufferen er tom er de ens, og når der tilføjes data til bufferen, skrives til den plads som skriveindekset angiver og dette forøges med 1. Man kan læse data fra læseindeksets plads og forøge læseindekset med 1. Når de to indeks atter er ens, er bufferen igen tom. Da arrays er endelige, er bufferen laves cyklisk. Når enden af arrayet nås, skrives og læses der fra starten af det igen, så fremt i fald, at bufferen ikke er fuld.
Med dette buffersystem regnede vi med, at kunne have flere beskeder liggende i samme array. Da beskedlængden kan læses fra 2. ord, behøver man ikke at have nogen variable, som angiver hvor beskedsnittet ligger. Fordelen med sådan et buffersystem er, at man kan læse og skrive fra det samtidigt. Så længde man holder øje med hvor mange beskeder der er i systemet, kan en tråd skrive til bufferen samtidig med, at en anden tråd læser. Holder man ikke øje med en hvor mange beskeder der er modtaget, vil man skulle til at læse hvert enkelt ord fra beskeden frem for en besked på en gang, hvilken så ville kræve endnu en buffer.
Det viste sig desværre, at det ikke var let at implementere systemet ordentligt. Så derfor gik vi væk fra at opbevare flere beskeder i bufferen.

Dataopsamling

Både for GPSNet og de serielle enheder gælder, at data modtages fra en InputStream.
For GPSNet kommer der kontinuerligt data ind. Det vil sige, at man kan forvente at læse fra inputsstrømmen og få svar meget kort efter.
Men for de serielle enheder gælder det, at data kommer i klumper. Den klasse, som skal læse fra en seriel enhed, skal implementere SerialPortEventListener interfacet. Hver gang der er en dataklump parat, bliver der genereret en SerialPortEvent, som man ved at implementere metoden serialEvent kan opsamle. Herefter kan man læse en begrænset mængde data fra inputstrømmen.
Det er derfor ikke hensigtsmæssigt at lave al dataopsamlingen i samme tråd. Dette ville man kunne, ved at læse én byte fra en inputsstrøm, og så, når dette er gjort, læse fra den næste inputsstrøm og så fremdeles. Dette ville dog betyde, at når blot én datastrøm er gået i stå, vil alle skulle vente på timeout fra den, hver gang der bliver forsøgt læst fra den. Desuden vil der muligvis opstå unødige ventetider, hvis en enhed sender med meget større tidsrum imellem hver dataklump end andre.
Derfor har vi valgt at delegere arbejdet ud i tråde. Alle GPSNet enheder modtager i en tråd for sig, så de ikke kommer i vejen for hinanden eller andre enheder. Serielenhederne derimod kræver kun at blive starten. Javas såkaldte Event dispatching thread tager sig af at lave en tråd og sende serial events, som modtagelsen kører i.
Ulempen ved at have flere tråde er, at man ikke ved hvornår de forskellige tråde kører. Man skal derfor undgå, at det er muligt, at to tråde skriver og læser fra samme variabel samtidigt. Dette kan ende i en såkaldt dead-lock situation, hvor begge tråde venter på at komme til at bruge samme variabel, men aldrig kommer til det. En anden ting er, at vi skal have en løkke til at samle data op, som ikke kun kører, når der er kommet nyt data. Til at starte med, havde vi lavet denne sådan, at den kørte uden pause. Men vi fandt ud af, at dette resulterede i, at den kørte mange tusinde gange uden at få noget nyt data. Den tog simpelthen alt tilgængeligt processorkraft, hvilket gjorde det umuligt at køre andre processer samtidigt med den og gjorde det svært at stoppe den igen. Derfor har vi sat denne tråd til at vente et kort tidsrum efter hver gang, den har søgt efter ny data for hver enhed.

Kort forklaring af tråde

Tråde gør, at man kan have flere processer kørende samtidigt. Det vil i vores tilfælde sige, at vi kan modtage data og skrive det til databasen uafhængigt af hinanden samtidigt og brugeren stadigvæk kan bruge GUIen mens der samles data. I Java har tråde en run metode, som indeholder den kode, som bliver udført. Når koden i run metoden er udført, stopper tråden med at køre og kan ikke genstartes. Derfor skal man sørge for, at der er en (næsten) uendelig løkke i tråden, hvis den skal køre uafbrudt. Men da man ikke kan stoppe tråden udefra, bliver man nødt til at lave en betinget uendelig løkke, som:
while(go == true)
hvor go er en boolesk variabel, som kan sætter til falsk af programmet uden for tråden.
Java og det bagvedliggende styresystem sørger for at styre hvornår trådene kører, så det ikke er programmøren, som skal side og styre det.. I princippet kan man have uendeligt mange tråde kørende samtidigt, men i praksis er det selvfølgeligt ikke realistisk på grund af begrænset computerkraft.

home