Jak to się robi dziś – wiadomo, nic wielkiego. Dedykowany hardware, biblioteka z netu i ciach -mamy pozycję z nieba. Albo -jak we wpisach obok – Android, geolokalizacja przeglądarki…
A kiedyś? Mamy -powiedzmy odebrany sygnał GPSa, nadawany w postaci NMEA 0182/183 z prędkością 9600Bd i 8 bitowy procesor bez peryferiów. Jak odebrać, zrozumieć etc?
Trąci myszką? Wiem, ale napiszę.
Procesor to Z80A. Zegar 4MHz, pamięci RAM i EPROM w osobnych kościach, podobnie port szeregowy 8251A (jeden z kilku), o są jeszcze do transmisji z komputerem RS232 oraz do transmisji z NOKIA5110 via mBus – nadawanie i odbiór SMS. Mamy rok ~2002 ?
Nie mam emulatora ani debugera ani asemblera Z80. A – przepraszam, mam asembler -sam napisałem w Pascalu. Zatem mogę programować mnemonikami i z komentarzem nawet, nie muszę w HEX…
Sygnał przychodzi 9600 bodów, a więc tyle bitów w czasie sekundy to jego max prędkość. Pamiętać należy o bicie startu, bicie parzystości i bicie stopu. Zatem jeden nadany bajt zajmuje 11 bitów –> w sekundzie 9600/11 = ok. 873 bajty, odbierane przez 8251A bajt po bajcie i konieczne do odczytania przez procesor, zanim nadejdzie kolejny bajt.
NMEA wygląda podobnie do:
$GPRMC,144651.271,A,5354.2019,N,01415.1032,E,2.01,175.95,011206,,,A*6D
$GPVTG,175.95,T,,M,2.01,N,3.7,K,A*05
$GPGGA,144652.271,5354.2018,N,01415.1036,E,1,10,1.1,57.8,M,,,,0000*39
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPRMC,144652.271,A,5354.2018,N,01415.1036,E,1.97,178.01,011206,,,A*67
$GPVTG,178.01,T,,M,1.97,N,3.6,K,A*08
$GPGGA,144653.271,5354.2016,N,01415.1042,E,1,10,1.1,58.3,M,,,,0000*31
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPRMC,144653.271,A,5354.2016,N,01415.1042,E,2.06,176.34,011206,,,A*68
$GPVTG,176.34,T,,M,2.06,N,3.8,K,A*05
$GPGGA,144654.271,5354.2012,N,01415.1051,E,1,10,1.1,58.8,M,,,,0000*3B
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPRMC,144654.271,A,5354.2012,N,01415.1051,E,2.11,177.37,011206,,,A*6D
$GPVTG,177.37,T,,M,2.11,N,3.9,K,A*00
$GPGGA,144655.271,5354.2006,N,01415.1053,E,1,10,1.1,59.3,M,,,,0000*37
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPRMC,144655.271,A,5354.2006,N,01415.1053,E,2.13,179.38,011206,,,A*68
$GPVTG,179.38,T,,M,2.13,N,4.0,K,A*0D
$GPGGA,144656.271,5354.2000,N,01415.1054,E,1,10,1.1,59.6,M,,,,0000*30
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPGSV,3,1,11,21,59,073,46,16,58,218,45,07,50,114,46,03,47,285,46*7B
$GPGSV,3,2,11,18,43,127,45,22,23,164,,19,19,284,41,27,13,327,36*7E
$GPGSV,3,3,11,06,12,109,33,29,11,047,41,26,09,060,39*48
$GPRMC,144656.271,A,5354.2000,N,01415.1054,E,2.18,180.20,011206,,,A*6E
$GPVTG,180.20,T,,M,2.18,N,4.0,K,A*09
$GPGGA,144657.270,5354.1993,N,01415.1051,E,1,10,1.1,60.0,M,,,,0000*39
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPRMC,144657.270,A,5354.1993,N,01415.1051,E,2.24,180.72,011206,,,A*63
$GPVTG,180.72,T,,M,2.24,N,4.2,K,A*03
$GPGGA,144658.270,5354.1988,N,01415.1047,E,1,10,1.1,60.5,M,,,,0000*3E
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPRMC,144658.270,A,5354.1988,N,01415.1047,E,2.29,181.90,011206,,,A*61
$GPVTG,181.90,T,,M,2.29,N,4.2,K,A*03
$GPGGA,144659.270,5354.1983,N,01415.1047,E,1,10,1.1,60.2,M,,,,0000*33
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPRMC,144659.270,A,5354.1983,N,01415.1047,E,2.33,182.08,011206,,,A*62
$GPVTG,182.08,T,,M,2.33,N,4.3,K,A*0B
$GPGGA,144700.270,5354.1978,N,01415.1041,E,1,10,1.1,60.7,M,,,,0000*39
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPRMC,144700.270,A,5354.1978,N,01415.1041,E,2.38,182.82,011206,,,A*64
$GPVTG,182.82,T,,M,2.38,N,4.4,K,A*05
$GPGGA,144701.270,5354.1972,N,01415.1038,E,1,10,1.1,59.6,M,,,,0000*37
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPGSV,3,1,11,21,59,073,45,16,58,218,45,07,50,114,44,03,47,285,45*79
$GPGSV,3,2,11,18,43,127,45,22,23,164,,19,19,284,43,27,13,327,32*78
$GPGSV,3,3,11,06,12,109,32,29,11,047,40,26,09,060,38*49
$GPRMC,144701.270,A,5354.1972,N,01415.1038,E,2.43,183.10,011206,,,A*67
$GPVTG,183.10,T,,M,2.43,N,4.5,K,A*02
$GPGGA,144702.270,5354.1969,N,01415.1031,E,1,10,1.1,59.4,M,,,,0000*35
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPRMC,144702.270,A,5354.1969,N,01415.1031,E,2.49,182.29,011206,,,A*66
$GPVTG,182.29,T,,M,2.49,N,4.6,K,A*00
$GPGGA,144703.270,5354.1965,N,01415.1024,E,1,10,1.1,58.3,M,,,,0000*3A
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPRMC,144703.270,A,5354.1965,N,01415.1024,E,2.42,185.01,011206,,,A*69
$GPVTG,185.01,T,,M,2.42,N,4.5,K,A*05
$GPGGA,144704.270,5354.1960,N,01415.1022,E,1,10,1.1,57.4,M,,,,0000*36
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPRMC,144704.270,A,5354.1960,N,01415.1022,E,2.36,183.57,011206,,,A*6B
$GPVTG,183.57,T,,M,2.36,N,4.4,K,A*02
$GPGGA,144705.270,5354.1953,N,01415.1024,E,1,10,1.1,56.5,M,,,,0000*31
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPRMC,144705.270,A,5354.1953,N,01415.1024,E,2.47,182.80,011206,,,A*61
$GPVTG,182.80,T,,M,2.47,N,4.6,K,A*0D
$GPGGA,144706.270,5354.1948,N,01415.1012,E,1,10,1.1,56.9,M,,,,0000*31
$GPGSA,A,3,27,06,19,18,16,21,18,29,03,07,,,1.9,1.1,1.5*3E
$GPGSV,3,1,11,21,59,073,50,16,58,218,47,07,50,114,43,03,47,285,45*78
$GPGSV,3,2,11,18,43,127,44,22,23,164,,19,19,284,42,27,13,327,39*73
$GPGSV,3,3,11,06,12,109,27,29,11,047,42,26,09,060,37*40
$GPRMC,144706.270,A,5354.1948,N,01415.1012,E,2.38,179.45,011206,,,A*68
$GPVTG,179.45,T,,M,2.38,N,4.4,K,A*0A
Gdzie np. dla pierwsze frazy:
$GPRMC, - rodzaj frazy
144651.271,A, - godzina oraz rodzaj pozycji (A=active)
5354.2019,N,01415.1032,E, - pozycja
2.01,175.95,011206,,,A
*6D - autosuma kontrolna całości
Czyli odbiór rozpoczynam znakiem $ następnie odczytuje rodzaj frazy, jeśli mnie interesuje to jej treść i na końcu sprawdzam czy zgadza się autosuma kontrolna.
Przerwanie odbierające bajt NMEA, nie może przekraczać max czasu wykonania, zatem kroki muszą być wyliczone precyzyjnie w taktach procesora (to nie RISC – tutaj czasy wykonania rozkazów są różne). Odebrany bajt z portu 8251A ląduje w kolejce o określonej długości, która jest cyklicznie nadpisywana i ma dwa indexy – zapisanego ostatniego bajtu oraz odczytanego ostatniego bajtu. Zwiększa to nieco elastyczność czasową.
`----------------------------------
`
`analizuj NMEA 0182/0183
`trwa: +106tkt + to co w procedurce
`wcześniej od wywołania @RxGPS minęło max ok. +318tkt
`tak więc na procedurkę mamy około 1505-106= ok. 1399tkt
LD HL,(#871A) @GPana `wykonaj analizę NMEA 0182/183
LD A,(HL) `
AND A `
RET Z `JP Z,@sprGP `porównaj z poszczególnymi pozycjami GPSa /mamy ok. +1468tkt/
DEC A `
LD D,#00 `
LD E,A `
LD HL,@GPint `
ADD HL,DE `
ADD HL,DE `po tym w HL adres adresu
LD A,(HL) `
INC HL `
LD H,(HL) `
LD L,A `
JP (HL) `wykonaj procedurkę
`----------------------------------
…adresy kroków pobieram z:
`----------------------------------
`
DEFETYK @GPint `tabela interpretacji kroków rozpoznania
` na każdą z tych procedur mamy max. ok. 1399tkt
DEFTABE GRMC1 ` +1046tkt rozpoznano nagłówek RMC, rozpoznaj czas i czy pozycja aktywna,
DEFTABE GPaer `DEFTABE GGGA2 `rozpoznano nagłówek GGA, +1227tkt omija 3 przecinki,
DEFTABE GPaer `DEFTABE GVTG3 `rozpoznano nagłówek VTG, +1227tkt omija 3 przecinki,
`DEFTABE GPaer `GPkxY `rozpoznano nagłówek GSA
`DEFTABE GPaer `GPkxX `rozpoznano nagłówek GSV
`---
DEFTABE GRMC4 `kolejny krok dla $GPRMC, +806tkt analizuj fi stopnie i minuty
DEFTABE GRMC5 `kolejny krok dla $GPRMC, +1236tkt analizuj fi ułamki minut, półkula
DEFTABE GRMC6 `kolejny krok dla $GPRMC, +899tkt analizuj lambda stopnie i minuty
DEFTABE GRMC7 `kolejny krok dla $GPRMC, +1236tkt analizuj lambda ułamki minut, półkula
DEFTABE GRMC8 `kolejny krok dla $GPRMC, +464tkt olej prędkość w węzłach
DEFTABE GRMC9 `kolejny krok dla $GPRMC, +1201tkt analizuj kurs
DEFTABE GRMCa `kolejny krok dla $GPRMC, +773tkt odczytaj i sprawdź datę
DEFTABE GRMCb `kolejny krok dla $GPRMC, +862tkt określ strefę czasową, popraw godzinę i dzień-mca
DEFTABE GRMCc `kolejny krok dla $GPRMC, +1148tkt sprawdź datę, oblicz dzień tygodnia,
` sprawdź StatusTime ustaw zegar
DEFTABE GRMCd `ostatni krok dla $GPRMC, +1125tkt tworzy text pozycji z NMEA i przesyła
` bajty pozycji i czasu jej określenia, startuje stoper określenia pozycji
DEFTABE G3pre `kolejny krok dla $GPGGA, +1220tkt omija 3 przecinki,
DEFTABE GGGAf `ostatni krok dla $GPGGA, +280tkt odczytuje liczbę satelitów
DEFTABE G3pre `kolejny krok dla $GPVTG, +1220tkt omija 3 przecinki,
DEFTABE GVTGh `kolejny krok dla $GPVTG, +785tkt odczytuje cz. całkowitą prędkości km/h
DEFTABE GVTGi `ostatni krok dla $GPVTG, +1170tkt odczytuje ułamek prędkości km/h
`...
`----------------------------------
Np pierwszy krok dla GPGGA wygląda tak:
` rozpoznaj/analizuj wstępnie czas i czy aktywna/niaktywna pozycja
`np. GPRMC,144651.271,A,5354.2019,N,01415.1032,E,2.01,175.95,011206,,,A*6D
` gdzie: GPRMC =konieczne określenie rodzaju komunikatu,
` 144651.271 => godzina, minuta, sekunda UTC, trzeba uwzględnić strefę czasową ;o)
` A => A oznacza że mamy pozycję, inny znak lub brak -> olewamy całość
` 5354.2019 => fi
` N => znak fi
` 01415.1032 => lambda
` E => znak lambda
` 2.01 => prędkość w węzłach
` 175.95 => kurs
` 011206 => dzień, miesiąc, rok
` resztę olewamy
` pusty => olewamy
` pusty => olewamy
` A => ? nie opisane!
` trwa +1046tkt max
LD DE,#8708 @GRMC1 `rozpoznaj czas i czy aktywna pozycja, masz 3400tkt
LD HL,(#871C) `
CALL @rdLi2 `odczytaj liczbę 2-cyfrową
JR C,@GPaer `error
CP #18 `= &24
JR NC,@GPaer `error
LD (DE),A `zapisz godzinę
DEC DE
CALL @rdLi2 `odczytaj liczbę 2-cyfrową
JR C,@GPaer `error
CP #3C `= &60
JR NC,@GPaer `error
LD (DE),A `zapisz minutę
DEC DE
CALL @rdLi2 `odczytaj liczbę 2-cyfrową
JR C,@GPaer `error
CP #3C `= &60
JR NC,@GPaer `error
LD (DE),A `zapisz sekundę
LD B,#05 `dla szukania przecinka 5 znaków
CALL @GPpr_ `szukaj przecinka
JR NZ,@GPaer `hop -> nie znaleziono przecinka
` tutaj adres HL jest za przecinkiem
LD A,(HL) `
CP #41 `'A' =poprawna, odebrana pozycja GPSa
JR NZ,@GPaer `hop -> inne olewamy
INC HL `next znak
LD A,(HL) `powinien tu być przecinek
CP #2C `przecinek =poprawnie
JR NZ,@GPaer `hop -> inaczej error
INC HL `next znak
LD A,#04 `naxt krok dla $GPRMC to #04
LD (#871C),HL @GoKro `+50tkt zapisz dynamiczny adres znaku
LD HL,(#871A) `adres bajtu kroku analizy tego wiersza
LD (HL),A `
RET `
`----------------------------------
Np rozkodowanie szerokości geograficznej to:
`----------------------------------
`
`
` 5354.2019 => fi
`trwa: +806tkt max.
LD HL,(#871C) @GRMC4 `dla $GPRMC rozpoznaj fi stopnie i minuty
LD (#870D),HL `zapisz adres tmp textowej postaci pozycji GPSa
` #870D - 2b to tmp adres tekstowej postaci określonej pozycji GPSa
LD DE,#0000 `poprawka na 0 przed dziesiątkami stopni
CALL @rdSMU `odczytaj 'SSMM'
JP C,@GPaer `hop -> error
` wynik w DE, adres w HL
LD A,E `
LD E,D `
LD D,A `
LD (#8700),DE `zapisz tmp fi stopnie i minuty
JP @InKro `zapisz adres w linijce i zmień krok
`-------------------------------------
`
`
` odczytaj 'SSMM' od (DE)
` w HL jest adres pierwszego odczytywanego znaku a po wyjściu,
` adres pierwszego znaku za ostatnią odczytaną cyfrą
` w DE musi być #0000 lyb 100stopni *60 gdy to lambda i była jedynka
` NC =ok
` C =error
`trwa +645tkt
CALL @rdLi2 @rdSMU `odczytaj liczbę 2-cyfrową /stopnie fi/lambda/
RET C `error
CP #5A `= &90
RET NC `error
PUSH HL `
LD L,A `stopnie fi/lambda
LD H,#00 `
LD C,L `
LD B,H `
ADD HL,HL `*2
ADD HL,HL `*4
ADD HL,BC `*5
ADD HL,HL `*&10
ADD HL,HL `*&20
LD C,L `
LD B,H `
ADD HL,BC `*&40
ADD HL,BC `*&60
ADD HL,DE `dodaj ew. cyfrę 1 przed lambda
LD C,L `
LD B,H `
POP HL `
PUSH BC `
CALL @rdLi2 `odczytaj liczbę 2-cyfrową /minuty fi/
POP BC `
RET C `error
CP #3C `= &60
RET NC `error
PUSH HL `
LD L,A `minuty fi
LD H,#00 `
ADD HL,BC `*&60
EX DE,HL `
` teraz stopnie i minuty są w DE
POP HL `adres znaku '.' w komunikacie NMEA
RET `tutaj adres HL jest za ostatnią cyfrą, na kropce
`---------------------------------------
`
`
`pomocnicza, odczytuje z (HL) liczbę 2-cyfrową do Acc
`NC -ok i HL za odczytaną drugą cyfrą
`CY-error
` nie zmienia BDE
` trwa +177tkt
CALL @rdCyf @rdLi2 `
RET C `
LD C,A `
ADD A,A `
ADD A,A `
ADD A,C `
ADD A,A `starsza cyfra * &10
LD C,A `
CALL @rdCyf `
RET C `
ADD A,C `
RET `
`----------------------------------
`
`
`pomocnicza odczytuje cyfrę ASCII z (HL) w HL adres ZA odczytanym znakiem (zawsze)
`NC -ok
`CY-error
` trwa +47tkt,
LD A,(HL) @rdCyf `
INC HL `
SUB #30 `
RET C `error
CP #0A `
CCF `
RET `C to error
`---------------------------------------
Odczyt zaś części ułamkowej to:
`----------------------------------
`
`
` 5354.2019 => fi
` N => znak fi
`trwa: +1236tkt max.
LD HL,(#871C) @GRMC5 `dla $GPRMC rozpoznaj fi stopnie i minuty
CALL @rd.3c `odczytaj kropkę i 3 z 5-ciu cyfr ułamka do C
JP C,@GPaer `hop -> error
LD A,(HL) `
INC HL `
INC HL `
CP #4E `'N' =NORD
JR Z,@_go07 `
CP #53 `'S' =SOUTH
JP NZ,@GPaer `hop -> inny znak to error
` zmień znak
LD A,(#8700) `
OR #80 `odwróć bo znak ujemny dla 'South'
LD (#8700),A `
PUSH HL @_go07 `zapamiętaj adres w komunikacie
LD HL,#8702 `adres dla fi ułamka
LD (HL),C `
POP HL `
JP @InKro `zapisz adres w linijce i zmień krok
`---------------------------------------
`
`
`wynik w Crej, format odczytywanych danych: '.XXX??,'
`pomocnicza odczytaj ułamek liczby począwszy od kropki
`wyjście NC =oki, HL za następnym przecinkiem!
` CY=error /pokaże też error gdy odczytamy ułamek ostatniej danej w linijce NMEA
` bo nie kończy się przecinkiem tylko * (a u nas chyba #00)/
`maksymalny błąd w odczycie dla 3 cyfr to wartość 1,072 bita lub 4,1875 najmniejznaczącej cyfry
` czyli wzdłuż południka to 7,76[m]
`trwa: max +1011tkt
LD A,(HL) @rd.3c `
INC HL `
CP #2E `'.' =kropka dziesiętna
SCF `
`RET NZ `hop -> inny znak to error
CALL Z,@rdCyf `tylko gdy była kropka -pierwsza cyfra
RET C `error -brak kropki lub cyfry
` mnożenie cyfry przez 25,6 (pierwsza cyfra po kropce dziesiętnej):
LD B,A `ta cyfra
ADD A,A `*2
ADD A,A `*4
ADD A,A `*8
LD D,A `=cyfra *8
ADD A,A `*16
ADD A,D `=*24
ADD A,B `=*25
LD D,A `zapamiętaj wynik pośredni
LD A,B `ta cyfra
ADD A,A `*2
ADD A,B `*3
ADD A,A `*6
` zakres Acc &54
CALL @Adv10 `wykonaj dzielenie Acc przez &10, wynik w Acc, reszta w Brej
ADD A,D `wynik *25,6
LD C,A `zapamiętaj w Crej
CALL @rdCyf `druga cyfra
JR NC,@_dv04 `
`ADD A,#30 `
`CP #2C `
CP #FC `czy to przecinek?
RET Z `wynik jest już w Crej, HL za przecinkiem
SCF `
RET C `error
` mnożenie drugiej cyfry przez 2,56:
LD E,A @_dv04 `ta liczba
CP #01 `wyjątek
JR Z,@__dv5 `
CP #03 `wyjątek
JR Z,@__dv5 `
INC A `
AND A @__dv5 `
RR A `
LD D,#00 `
JR C,@_divi `
LD D,#05 `
` liczba oczek.wynik.reszty incA div2
` 0 0.0 1 0
` 1 0.6 wyjątek 1 0
` 2 1.2 2 1
` 3 1.8 wyjątek 3 1
` 4 2.4 5 2
` 5 3.0 6 3
` 6 3.6 7 3
` 7 4.2 8 4
` 8 4.8 9 4
` 9 5.4 A 5
ADD A,B @_divi `dodaj resztę z poprzedniego
ADD A,D `dodaj resztę z mnożenia cyfra * 2,5
LD B,A `
LD A,E `ta cyfra (druga)
ADD A,A `*2
ADD A,A `*4
ADD A,E `*5
RRA `/2 bo CY był zero!
ADD A,C `dodaj wynik mnożenia 1szej liczby
LD C,A `zapamiętaj w Crej
CALL @rdCyf `trzecia cyfra
JR NC,@_dv05 ` /jest NC!/
`ADD A,#30 `
`CP #2C `
DEC HL `bo i tak będzie INC HL gdy skoczy bo nie cyfra
CP #FC `czy to przecinek?
JR Z,@__dv8 `wynik jest już w Crej, HL na przecinku
INC HL `
SCF `
RET C `error
` mnożenie trzeciej cyfry przez 0,256:
LD E,A @_dv05 `przechowaj 3cią cyfrę
` zaczynamy od pomnożenia przez 0,25:
RRA `/2 bo CY był zero! jest cykliczne ale wychodzi NC!
AND A `NC
RRA `/4 bo CY był zero! jest cykliczne ale wychodzi NC!
ADD A,C `dodaj wynik z poprzednich cyfr
LD C,A `zapamiętaj w Crej
` teraz jeszcze pomnożymy przez 0,006 (i razy 10):???
LD A,E `jeszcze sprawdzamy resztę
` liczba oczek.reszta obliczona reszta
` 0 0 0
` 1 2 2
` 2 5 5
` 3 7 7
` 4 0 0
` 5 2 2
` 6 5 5
` 7 7 7
` 8 0 0
` 9 3 2
AND #03 `
RLA `*2 i wchodzi NC
CP #03 `
JR C,@__dv7 `
INC A `
ADD A,B @__dv7 `dodaj poprzednie reszty
LD B,A `zapisz w Brej bo z Brej zaraz czytamy
LD A,B @__dv8 `+255tkt poprzednie reszty uwzględniamy teraz
` wynik dotychczasowy jest w Crej
` zakres reszty uzależniony od reszt z 3 cyfr: dla 1 cyfry max 8
` dla 2 cyfry max 5
` dla 3 cyfry max 7, razem zakres 0-&20
CALL @Adv10 `podziel Acc przez &10, wynik w Acc, reszta w B
ADD A,C `dodaj resztę do wyników
LD C,A `zapamiętaj w Crej
CALL @GPprz `szukaj przecinka
RET Z `
SCF `
RET `
`---------------------------------------
Podobnie z długością / lambda. Prędkość i kierunek/kurs to już łatwiej. Podobnie data i czas.
Mam odczytany tekst i mam dane, kończy się fraza NMEA, sprawdzam autosumę, gdy poprawna – mogę uznać dane za prawidłowe i przepisać je w miejsce docelowe:
`------------------------------------------
`
`
`kolejny krok, przeładowuje dane uzyskane z RMC do bufora GPSa
`trwa: +1125tkt
LD HL,#8706 @GRMCd `dla $GPRMC rozpoznaj
DEC HL `
LD (HL),#00 `zapisz liczbę znaków (bez kończącego zera!)
PUSH HL `
INC HL `
`#8706 - 7b to tmp czas odczytany z GPSa z $GPRMC, kolejno:
` sek, min, godz, dn-mca, miesiąc, rok, dzień tygodnia
LD DE,#86F0 `bufor strefowego czasu i daty
`#86F0 - 7b to czas określenia ostatniej pozycji GPS z $GPRMC, (czas naszej strefy) kolejno:
` sek, min, godz, dn-mca, miesiąc, rok, dzień tygodnia
LD BC,#0007 `długość
LDIR `prześlij do bufora GPSu
`6*21+16=142
LD HL,#8700 `
`#8700 - 3b to tmp fi w postaci stałoprzecinkowej 2b to stopnie*60+minuty, 1b to ułamki minut
`#8703 - 3b to tmp lambda w postaci stałoprzecinkowej 2b to stopnie*60+minuty, 1b to ułamki minut
LD DE,#86F7 `
`#86F7 - 3b to fi w postaci stałoprzecinkowej 2b to stopnie*60+minuty, 1b to ułamki minut
`#86FA - 3b to lambda w postaci stałoprzecinkowej 2b to stopnie*60+minuty, 1b to ułamki minut
LD BC,#0006 `długość
LDIR `prześlij do bufora GPSu
`5*21+16=121
LD HL,#0001 `czas, jaki upłynął od określenia
` ostatniej pozycji GPSa i czasu/daty
LD (#86E8),HL `
`#86E8 - 2b to zegar cykający sekundy od określenia ostatniej pozycji z GPSa
`
`już 349tkt
`
` jeśli jest tutaj, to znaczy że fi i lambda mają oczekiwaną postać tekstową
` z więc nie trzeba sprawdzać ile znaków do przecinka, tylko przesłać fi, potem N/S,
` potem lambda i E/W
`
LD HL,(#870D) `adres tmp textowej postaci pozycji GPSa
` #870D - 2b to tmp adres tekstowej postaci określonej pozycji GPSa
` '5354.2019,N,01415.1032,E,'
LD DE,#86D3 `
`#86D2 - &22b to fi, lambda w formie textu, gdzie:
` #86D2 - 1b liczba znaków ASCII (bez zliczenia znaku końca #00)m
` gdy =0 to brak zapisu
` #86D3 - do &21b ASCIIZ tekstu zakończonego #00 opisującego ostatnią odczytaną pozycję
` w formacie: ` '5354.201N 01415.103E'
PUSH DE `
LD BC,#0008 `8 znaków
LDIR `mamy: '5354.201'
`7*21+16=163
`już +512tkt
LD B,#03 `
CALL @GPpr_ `szuka przecinka (kod #2C)
`szuka przecinka od adresu (HL) włącznie do Brej znaków
` nie zmienia DEC
`wyjście NZ B=#00 to nie znaleziono,
` Z B<>#00 to znaleziono, HL za przecinkiem,
` +(38+n*31)tkt gdy znaleziono w (HL+n)
` max trwa ? 131tkt ?
JR NZ,@GPc__ `hop -> error
LD A,(HL) `
LD (DE),A `Nord/South
INC HL `
INC DE `
LD A,#20 `kod spacji
LD (DE),A `
INC DE `
INC HL `HL na pierwszej cyfrze lambda
LD BC,#0009 `9 znaków
LDIR `mamy: '5354.201N 01415.103'
`8*21+16=184
`już +896tkt
LD B,#03 `
CALL @GPpr_ `szuka przecinka (kod #2C)
`szuka przecinka od adresu (HL) włącznie do Brej znaków
` nie zmienia DEC
`wyjście NZ B=#00 to nie znaleziono,
` Z B<>#00 to znaleziono, HL za przecinkiem,
` +(38+n*31)tkt gdy znaleziono w (HL+n)
` max trwa ? 131tkt ?
JR NZ,@GPc__ `hop -> error
LD A,(HL) `
LD (DE),A `East/West
INC DE `
SUB A `kod końca textu ASCIIZ
LD (DE),A `
POP HL @GPc__ `odtwórz startowy adres docelowy
DEC HL `
LD (HL),#00 `zapisz liczbę znaków (bez kończącego zera!)
JP @GPaer `hop -> zakończ analizę $GPRMC...
`---------------------------------------
Co dalej?
Zerknijmy na potrzeby. Jeśli chcemy odrobinkę analizować te dane, a nie jedynie je odczytywać … np chciałbym sprawdzić, jak dana pozycja jest daleko od innej – założonej (co mogłoby dać odpowiedź, że obiekt monitorowany przemieścił się od pozycji 'zakotwiczenia’ – np ktoś nam ukradł samochód, wywożąc na lawecie).
Do takiej analizy potrzeba odrobinkę wiedzy z nawigacji.
Czym zatem jest pozycjia GPS – fi/lambda (szerokość/długość geograficzna)? Fi/lambda określa nam miejsce -przez analigię jak X i Y na wykresie 2D. Problem w tym, że jesteśmy na powierzchni kuli a nie na płaszczyźnie.
O ile fi (szerokość geograficzna) jest łatwa do przeliczenia na odległości, bo 1 stopień szerokości to 60 minut a 1 minuta to 1 Mila Morska -czyli 1852,5m – i jest to stałe(*) w każdym miejscu globu. To z lambda (długość geograficzna), sprawa nie jest już tak łatwa. Oczywiście 1 stopień to 60 minut a 1 minuta to 1852,5m – ale TYLKO na równiku. Im dalej od równika tym 1 minuta jest krótsza od 1852,5m.
Aby dowiedzieć się, jak uzyskać odległość w metrach, odpowiadającą 1 minucie długości geograficznaj na danej szerokości geograficznaj, należy przemnożyć ją przez cosinus tej szerokości. Proste prawda?
…ale czekaj, mamy tylko 8 bitowy Z80 z operacjami dodawania i odejmowania wyrazów 16 bitowych… Nie mam mnożenia ani -tym bardzie funkcji trygonometrycznych!
Z mnożeniem poradzę sobie tak:
`------------------------------------
`
`mnożenie dwóch dodatnich liczb 3 bajtowych z wynikiem 3 bajtowym
`wejście: adres w HL tablicy 3 2bajtowych adresów kolejno: mnożnikA, mnożnejB i wyniku
` procedura zmienia mnożnąB
` wynik jest zerowany przez tą procedurę na początku
`trwa:
PUSH HL @multi `
INC HL `zaczynamy od obliczania adresu wyniku w tabeli
INC HL `
INC HL `
INC HL `
LD A,(HL) `odczytujemy adres wyniku
INC HL `
LD H,(HL) `
LD L,A `
SUB A `
LD (HL),A `zerujemy wynik
INC HL `
LD (HL),A `
INC HL `
LD (HL),A `
POP HL `
LD E,(HL) `
INC HL `
LD D,(HL) `DE =adres mnożnejA
INC HL `
LD B,#03 `3 bajty mnożnikA
PUSH BC @_dod1 `
` DE=adres danej A
LD A,(DE) `odczytaj bajt mnożnikA
LD B,#08 `8 bitów bajtu mnożnikA
RLCA @_dod2 `do CY bit mnożnika, kolejno od najstarszego...
PUSH HL `
PUSH DE `
PUSH BC `
PUSH AF `
` HL=adres adresu mnożnejB
LD E,(HL) `odczytujemy adres mnożnejB
INC HL `
LD D,(HL) `
INC HL `
LD A,(HL) `odczytujemy adres wyniku
INC HL `
LD H,(HL) `
LD L,A `
` HL=adres wyniku
` DE=adres mnożnejB
JR NC,@_dod5 `hop -> nie dodawaj bo bit mnożnikA jest =0
INC HL `
INC HL `lsb wyniku
PUSH DE `
INC DE `
INC DE `lsb mnożnejB
` DE=adres danej B +2
` HL=adres wyniku +2
AND A `CY=0
LD B,#03 `3 bajty do zsumowania
LD A,(DE) @_dod3 `bajt mnożnejB
ADC A,(HL) `bajt sumowanego wyniku
LD (HL),A `zapisz do wyniku
` w CY bit przeniesienia
DEC HL `
DEC DE `
DJNZ @_dod3 `pętla -> kolejny bajt
POP DE `
AND A @_dod5 `odtwórz adres mnożnejB, CY = 0
LD B,#03 `3 bajty do przesunięcia
LD A,(DE) @_dod4 `bajt mnożnejB
RR A `lsbit do CY
LD (DE),A `
INC DE `
DJNZ @_dod4 `pętla -> kolejny bajt
POP AF `
POP BC `
POP DE `
POP HL `
DJNZ @_dod2 `
POP BC `
INC DE `
DJNZ @_dod1 `
RET `
`-----------------------------------
Co zaś z trygomometrią? Co z cosinusem?
Z matematyki pamiętamy, że… cosinus możemy uzyskać poprzez obliczenie ciągu o następującym wzorze, gdy jako x podstawimy kąt w radianach:
A kąt w radianach uzyskamy dzieląc kąt w stopniach przez 2 * pi
`-----------------------------------
`
`LD HL,#0000 @aryt+ `
`LD (#8700),HL `
`LD A,#00 ` 00 stopni 00 minut
`LD (#8702),A `
`
`1 znormalizowana wartość bezwzględna fi w stopniach
LD HL,#8702 @aryt+ `wartość fi tmp
LD DE,#87C5 `
PUSH DE `
AND A `
LD B,#03 `
LD A,(HL) @_ary1 `
RL A `
LD (DE),A `
DEC HL `
DEC DE `
DJNZ @_ary1 `
POP HL `
AND A `
LD B,#03 `
LD A,(HL) @_ary2 `
RL A `
LD (HL),A `
DEC HL `
DJNZ @_ary2 `
`2 wpisz wartość mnożnika radianowego
LD HL,#8298 `\
LD A,#5A `\mnożnik radianowy
LD (#87BA),HL `/
LD (#87BC),A `/
`3 mnożenie fi przez mnożnik radianowy
LD HL,#87C3 `
LD (#87C6),HL `
LD HL,#87BA `
LD (#87C8),HL `
LD HL,#87BD `
LD (#87CA),HL `
LD HL,#87C6 `
CALL @multi `
`4 kopiowanie fi_rad
LD HL,(#87BD) `
LD A,(#87BF) `
LD (#87BA),HL `
LD (#87BC),A `
`5 mnożenie fi_rad ^ 2
LD HL,#87BA `
LD (#87C6),HL `
LD HL,#87BD `
LD (#87C8),HL `
LD HL,#87C0 `
LD (#87CA),HL `
LD HL,#87C6 `
CALL @multi `
`6 kopiujemy fi_rad ^ 2
LD HL,(#87C0) `
LD A,(#87C2) `
LD (#87BD),HL `
LD (#87BF),A `
`7 mnożenie (fi_rad^2) ^ 2
LD HL,#87BD `
LD (#87C6),HL `
LD HL,#87C0 `
LD (#87C8),HL `
LD HL,#87BA `
LD (#87CA),HL `
LD HL,#87C6 `
CALL @multi `
`8 wpis 4 / 24
LD HL,#AA2A `\
LD A,#AA `\mnożnik 8 / 24
LD (#87C0),HL `/
LD (#87C2),A `/
`9 mnożenie (fi_rad^4) * 8/24
LD HL,#87BA `
LD (#87C6),HL `
LD HL,#87C0 `
LD (#87C8),HL `
LD HL,#87C3 `
LD (#87CA),HL `
LD HL,#87C6 `
CALL @multi `
`10 dodajemy 1
LD A,(#87C3) `
OR #40 `
LD (#87C3),A `
`11 odejmowanie 1+(fi^4/4!)-(fi^2/2!)
LD DE,#87C3 `
LD HL,#87BD `
CALL @subst `
`12 normalizujemy
LD HL,#87BF `
AND A `
LD B,#03 `
LD A,(HL) @_mul2 `
RL A `
LD (HL),A `
DEC HL `
DJNZ @_mul2 `
`----
`[...]
W praktyce, dla naszych wartości fi/lambda, wystarczą trzy pierwsze elementy tego ciągu:
1-(fi^2/2!)+(fi^4/4!)
Jak z dokładnością? 3 bajty dla lambda to dwa bajty dla minut(i stopni przeliczonych na minuty) oraz trzeci bajt dla ułamka minut. najmniejsza określona przez nas część ułamka minut to 1/256 minuty, czyli 1852,5m / 256 = 7m23cm . Takiej dokładności nie uzyskamy z komercyjnego GPSa, więc jest ona tutaj wystarczająca.
Teraz, mając gwie pozycje, aby obliczyć odległość pomiędzy nimi, z tw. Pitagorasa:
odległość = pierwiastek_kwadratowy( delta_fi ^2 + delta_L ^2 ) [gdzie delta_L = delta_lambda * cosinus(fi) ]
No i na koniec – porównujemy uzyskaną warość odległości z progiem wywłującym alarm, jeśli zostanie przekroczona. Alarmem może być SMS wysłany z Nokii5110 (wraz z pozycją GPS) lub uruchomienie syreny samochodu. Ale to temat na odrębny wpis.
Wzmiankowany tutaj alarm został zaprojektowany i wykonany od pomysłu do działającego prototypu przez autora.