Bu yazımızda GPS nedir? GPS ile nasıl konum belirlenir? sorularına cevap vermeye çalışacak, Stellaris üzerinde GPS modülümüz üzerinden verileri okuyacak ve yorumlayacağız. Anlık konum bilgilerimizi hesaplayacağız. Konum bilgilerimizi UART haberleşmesi yardımıyla alıp(UART0), yine aynı haberleştirme protokolü ile ekrana verilerimizi basacağız.(UART1)

 

 

 

 

 

GPS İle Konum Belirleme

GPS modülü, uydular ile arasındaki mesafeyi ölçerek anlık  konumumuzu belirlemeye çalışan bir mekanizmadır. Anlık konum bilgilerimizi alabilmek için bu modülü kullanacağız. Uygulamamızı SIRF III adlı modül ile yapacağız. Bu modülü seçmemizdeki faktörler yüksek duyarlılık(-159dBm) ve yer istasyonu olmadan 5 metreye kadar doğruluk verebilmesi olmuştur. Modül hakkında detaylı bilgi için GPS cihazı seçimi için yazmış olduğumuz yazı incelenebilir.

GPS İle Konum Nasıl Belirlenir?

GPS modülünün uydularla arasındaki mesafeyi ölçerek konumunu belirlemeye çalıştığından bahsetmiştik. Bu kısımda bu konuyu biraz daha detaylandırmaya çalışacağız. GPS temel olarak uydularla arasında üçgenleştirme metodunu kullanır. Fakat üçgenleştirmeden bahsederken yanılgıya düşülmemesi gereken kısım GPS ile uydular arasında her hangi bir açı yoktur. Böyle bir bilgi alamaz.  Aslında 24 uydu verisi alınır fakat 3 uydu yardımıyla var olan konumumuz bulunur diyebiliriz.

Mesela diyelim ki ilk uydudan aldığımız verilere göre 30 000km uzaklık bilgisini aldık. Bu uzaklık bilgisi, dünya üzerinde o uyduyu merkez kabul edip etrafına yarı çapı 30 000km olan bir alan çizmemiz olarak yorumlanabilir. Daha sonra ikinci uydudan aynı şekilde 22 000 km uzaklık değeri aldığımızı düşünün ve bu uydunun etrafına da aynı şekilde bir alan oluşturulduğunu var sayalım. Bu uydu bilgilerinin 3 tanesinin oluşturduğu alanlar bir yerde kesişecek ve bize dünya üzerindeki yerimizi verecektir.

 

İlk resimde kesim noktasının bulunması, ikinci resimde geçerli alanların sınırlandırılması ve son resimde ise GPS alıcısının oluşan alanları değiştirerek kesişimi netleştirmesi resmedilmiştir. GPS alıcının duyarlılığı ve doğruluğu burada işimize yaramaktadır. Konumu belirlemek için uzaklıkları kullanıyoruz dedik. Peki bu uzaklıkları nasıl hesaplıyoruz gibi bir soru gelebilir.

Uzaklık hesabını aslında çok temel bir eşitliğe göre yapıyoruz. Yol = Hız x Zaman formülü yadımıyla uydu ile aramızdaki mesafenin hesabını yapacağız. Zaman olarak kullanacağımız kısım sinyalin seyahat zamanı, hız olarak kullanacağımız kısım ise sinyalin hızı olacaktır. Uydu bize o anki saat bilgisini göndermektedir. Bizde o anki alıcının zamanından uydunun zaman bilgisini çıkararak seyahat zamanını bulacağız. Fakat burada düşünülmesi gereken kısım her uydudan aynı zamanda veri alındığı baz alınmaktadır ve aslında aynı zamanda veri alınmamaktadır. Mesela 22000 km uzaklıktaki bir uydu ile 33000 km uzaklıktaki bir uydu aynı zamanda cevap vermemektedir. Fakat böyle bir var sayıma baş vurulmaktadır.

GPS hatalarının nedenlerinden bir tanesi yukarıda bahsettiğimiz zamanlamadır. Genellikle bu hata uydulardan kaynaklanmaz. Çünkü üzerilerinde atomik saatler vardır ve bunlar sürekli doğru veri gönderirler. Mükemmel zamanlama istiyorsak GPS modülümüz ile birlikte atomik saat kullanabiliriz.

Bahsettiğimiz uzaklık hesabı üzerinde tabiki hatalar mevcuttur. Zaten bu hataları telafi edebildiğimiz derecede hassas ölçümler yapabiliriz. Bu hatalar bulutlardan ya da havadaki partiküllerden kaynaklanabilir. Aynı zamanda atmosfer tabakasına girdikçe sapmalar yapacaktır. Işınımız atmosfer tabakalarından geçerken mesela iyonosfer üzerindeki partiküllerden etkilenebilir ya da trofosfer üzerindeki su buharı nedeniyle yavaşlayabilir. Ve bu bize zamanlama üzerinde hatalar verebilir. Bu sorunları çözmek için ise  çift yönlü frekans diye adlandırılan bir hesaplamaya baş vurulmaktadır.

Çift yönlü frekans hesabı, GPS modüle gönderilen iki taşıyıcı sinyal arasındaki gecikme hesaplanarak yapılmaktadır. (L1 ve L2)  L1 sinyali herkese açık olsa da L2 sinyali sadece askeri alan için kullanıma açıktır ve bu taşıyıcı sinyal üzerindeki veriyi biz alamayız. Bu yüzden bu yöntem bizim için geçerli bir yöntem olmayacaktır.

GPS Modül Çıktılarının Yorumlanması

GPS modül bize bir çok veri göndermektedir. Bu verilerin farklı farklı anlamları olmaktadır. Bu bölümde modülümüzün verdiği çıktıların görevlerini detaylandırmaya çalışacağız. İlk olarak bize enlem ve boylam bilgisi gerektiği için cihazımız üzerinde bu bilgiyi alabileceğimiz komut dizisini bulduk. GGA isimli komut dizisi yardımıyla bu bilgileri alabileceğiz.

Tablodan da görülebileceği üzere ilk virgülden sonraki veri bize UTC zamanını, ikinci virgülden sonra enlem bilgisi, üçüncü virgülden sonra kuzey-güney bilgisi, dördüncü virgülden sonra ise boylam bilgisi verilmektedir. Bu bilgileri tanımlamalarında da görüldüğü gibi tanımlayarak gerekli hesaplamalarımızı yapacağız. GGA bilgisini gelen verilerden yakalayarak bu seriye ulaşabileceğiz.

Kullanacağımız bir diğer kısım ise GPS üzerine göndereceğimiz giriş komutları olacaktır.

İlk olarak mesajın ne tür bir mesaj olduğunu söyleyecek tanımlama kodunu gönderiyoruz, ardından hangi protokolde çalışmak istediğimizi data gönderim hızını vs. ayrları yapıyoruz. Bu şekilde GPS içerisinde ayarlamaları yapabiliyoruz.

MCU Üzerindeki İşlemler

Stellaris üzerinde GPS modülünü interrupt üzerinden kontrol etmeye karar verdik. Uygulamamızda magnetometer için yazmış olduğumuz kaynak kodları güncelleyeceğiz ve iki uygulamayı  birleştireceğiz. Öncelikle interrupt ayarlarını yapmamız gerekiyor.

ConfigInt   
void
ConfigInt()
{
	 SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
	 SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
	 IntMasterEnable();
	 GPIOPinTypeUART(GPIO_PORTD_BASE, GPIO_PIN_2 | GPIO_PIN_3);
 
	 UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 4800,
                        (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                         UART_CONFIG_PAR_NONE));
	 IntEnable(INT_UART1);
	 UARTIntEnable(UART1_BASE, UART_INT_RX | UART_INT_RT);
 
}

GPS ile UART1 üzerinden haberleşeceğimiz için UART1 ve onun ait olduğu port olan GPIOD portunu aktif hale getiriyoruz.  Daha sonra genel interrupt bayrağını açıyoruz. Ardından UART haberleşme protokolü ayarlarını tanımlıyor ve ardından pinler üzerindeki interruptı aktifleştiriyoruz.

İnterrupt fonksiyonumuz içerisinde ise NMEA protokolünden gelen verileri ayrıştırıp istediğimiz değerleri alacak bir yapı kurmamız gerekiyordu. Bunun için öncelikle istediğimiz değişkeni yakalayana kadar bekleyip, yakalama işlemi gerçekleştikten sonra ise bu verileri bir yere kaydedip ayrıştırarak ekrana anlaşılır bir şekilde basmaya karar verdik.

void
UARTIntHandler(void)
{
	 static int i=0;
	 static int j=0;
 
	 ulStatus = UARTIntStatus(UART1_BASE, true); 
 
	  while(UARTCharsAvail(UART1_BASE))
	  {
        temp[i] =  UARTCharGetNonBlocking(UART1_BASE); 
 
		if(i>=5 && temp[i]=='A' && temp[i-1]=='G' && temp[i-2]=='G' && temp[i-3]=='P'&& temp[i-4]=='G'&& temp[i-5]=='$')
		{
 
			for(j=0;j<6;j++)
				data[j] = temp[i+j-5];
 
			valid = 1 ;
			GGA=1;
		}
 
	    if(valid == 1 )
	    {
 
			data[j] = temp[i];
 
			if(temp[i]=='*')
	 		{
			  valid=0;
			  UARTprintf("%s\n",data);
			  getLocation(data);
 
			}
		} 
 
	j=(j+1)%2000;
	i=(i+1)%2000;
 
	}
	UARTIntClear(UART1_BASE, ulStatus);		// seri port interrupt bayrağını temizler
}

UARTCharsAvail(UART1_BASE) fonksiyonu yardımıyla buffer üzerindeki değerler bitene kadar UART üzerinden data alış verişini gerçekleştiriyoruz. Burada aldığımız değerleri temp değişkenine alarak istediğimiz protokolün gelip gelmediğini kontrol ediyoruz. Eğer istediğimiz komut gelmişse(GGA) bu komut bilgilerini data değişkenine kaydediyoruz. Ardından gelen komut üzerinden bilgilerimizi ayırmamız gerekiyor. Bunun için stringh.h kütüphanesinde yer alan strtok fonksiyonunu kullanacağız.

void
getLocation(char * pch)
{
	 pch = strtok (data,",");
 
	 while (pch != NULL)
  	 {
 
		if(GGA==6)
		{
		   if(*pch=='E')
				UARTprintf(" Dogu\n");
		   else if(*pch=='W')
				UARTprintf(" Bati\n");
 
		   GGA=0;
		   UARTprintf("\n\n");
		}
		else if(GGA==5)
		{
		   UARTprintf("Boylam= %s",pch);
		   GGA=6;
		}
		else if(GGA==4)
		{
			GGA=5;
			if(*pch=='N')
				UARTprintf(" Kuzey\n");
			else if(*pch=='S')
				UARTprintf(" Guney\n");
		}
		else if(GGA==3)
		{
			UARTprintf("Enlem= %s",pch);
			GGA=4;
		}
		else if(GGA==2)
		{
			GGA=3;
		   	UARTprintf("UTC zamani= %s\n",pch);
 
		}
		else if(GGA==1)
		{
			GGA=2;
		}
		pch = strtok (NULL,",");
	 }
}

Bu fonksiyonda diziyi virgülden ayırarak istediğimiz enlem ve boylam değerlerini alıp ekrana yazdıracağız. GGA değişkeni, seri sonu gelene kadar her seferinde bir virgül bulduğu için böyle bir numara yapmaya ihtiyaç duyduk.

Değişiklik yaptığımız bir diğer konu ise vektör tablosu oldu. Çünkü interrupt fonksiyonunu geçerli vektör tablosunda belirtmemiz gerekiyordu. Bunun için proje üzerindeki Startup.s dosyasına girerek gerekli değişiklikleri yapacağız.

;******************************************************************************
;
; External declaration for the interrupt handler used by the application.
;
;******************************************************************************
        EXTERN  UARTIntHandler
 
;******************************************************************************
;******************************************************************************
;
; The vector table.
;
;******************************************************************************
        EXPORT  __Vectors
__Vectors
        DCD     StackMem + Stack            ; Top of Stack
        DCD     Reset_Handler               ; Reset Handler
        DCD     NmiSR                       ; NMI Handler
        DCD     FaultISR                    ; Hard Fault Handler
        DCD     IntDefaultHandler           ; The MPU fault handler
        DCD     IntDefaultHandler           ; The bus fault handler
        DCD     IntDefaultHandler           ; The usage fault handler
        DCD     0                           ; Reserved
        DCD     0                           ; Reserved
        DCD     0                           ; Reserved
        DCD     0                           ; Reserved
        DCD     IntDefaultHandler           ; SVCall handler
        DCD     IntDefaultHandler           ; Debug monitor handler
        DCD     0                           ; Reserved
        DCD     IntDefaultHandler           ; The PendSV handler
        DCD     IntDefaultHandler           ; The SysTick handler
        DCD     IntDefaultHandler           ; GPIO Port A
        DCD     IntDefaultHandler           ; GPIO Port B
        DCD     IntDefaultHandler           ; GPIO Port C
        DCD     IntDefaultHandler           ; GPIO Port D
        DCD     IntDefaultHandler           ; GPIO Port E
        DCD     IntDefaultHandler              ; UART0 Rx and Tx
        DCD     UARTIntHandler           ; UART1 Rx and Tx
        DCD     IntDefaultHandler           ; SSI0 Rx and Tx
        DCD     IntDefaultHandler           ; I2C0 Master and Slave
        DCD     IntDefaultHandler           ; PWM Fault
        DCD     IntDefaultHandler           ; PWM Generator 0
        DCD     IntDefaultHandler           ; PWM Generator 1
        DCD     IntDefaultHandler           ; PWM Generator 2
        DCD     IntDefaultHandler           ; Quadrature Encoder 0
        DCD     IntDefaultHandler           ; ADC Sequence 0
        DCD     IntDefaultHandler           ; ADC Sequence 1
        DCD     IntDefaultHandler           ; ADC Sequence 2
        DCD     IntDefaultHandler           ; ADC Sequence 3
        DCD     IntDefaultHandler           ; Watchdog timer
        DCD     IntDefaultHandler           ; Timer 0 subtimer A
        DCD     IntDefaultHandler           ; Timer 0 subtimer B
        DCD     IntDefaultHandler           ; Timer 1 subtimer A
        DCD     IntDefaultHandler           ; Timer 1 subtimer B
        DCD     IntDefaultHandler           ; Timer 2 subtimer A
        DCD     IntDefaultHandler           ; Timer 2 subtimer B
        DCD     IntDefaultHandler           ; Analog Comparator 0
        DCD     IntDefaultHandler           ; Analog Comparator 1
        DCD     IntDefaultHandler           ; Analog Comparator 2
        DCD     IntDefaultHandler           ; System Control (PLL, OSC, BO)
        DCD     IntDefaultHandler           ; FLASH Control

Vektör tablosu üzerinde interrupt fonksiyonumuz için değişiklikleri yukarıdaki gibi yapıyoruz. Ardından programımızı derleyip çalıştırabiliriz.

Sadece GPS çıktıları;

 Stellaris ile entegre edildikten sonra;

GPS üzerinden aldığımız verileri yorumlayarak konumumuzu hesaplamak mümkün oluyor. Öncelikle aldığımız enlem ve boylam verilerini derece;dakika;saniye olacak şekilde güncelliyoruz. Örnek olarak aldığımız verilerden bir tanesi ;

Enlem : 3947.0490 Kuzey Boylam: 03030.6671 doğu olduğu bilgisini aldık. Yukarıdaki tablolardan faydalanarak verileri güncellediğimizde;

Enlem: 39 derece : 47 dakika : 2.94saniye Boylam: 30 derece : 30dakika : 40.026 saniye olarak yorumlanabilir. Kordinatları yerine koyduğumuzda konumumuzu vermesini bekliyoruz.

10 metre kadar hata payı var. Böylelikle GPS ile konum belirlemeyi de tamamlamış bulunuyoruz.