Month: September 2010

การเขียนโปรแกรมต่อ GPS ด้วยคอมโพเน็นต์ OpenGPSX (ภาค 5)

ทดสอบ OpenGPSX บน Ubuntu

  • มาลองทดสอบกันบน Ubuntu Lucid ก็เริ่มจากดาวน์โหลดคอมโพเน็นต์ มาก่อน ทำการติดตั้งเหมือนในวินโดส์ ที่ผมกล่าวไปแล้ว เนื่องจากโค้ดของ Lazarus เป็น cross-platform โค้ดที่ใช้ใน Windows ก็สามารถนำมาใช้ได้เลยใน Linux แต่ติดขัดตรงโปรแกรมที่จะใช้ทดสอบ ผมจะแยกเวอร์ชั่นวินโดส์และเวอร์ชั่น Linux เพราะฟอร์มที่สร้างในวินโดส์ ถ้านำมาใช้ใน Linux ตัวหนังสือ object ที่เราแปะลงฟอร์มเช่น Label, Edit จะใหญ่มากเพราะปัญหาเรื่อง Font ที่ไม่ตรงกันของสอง OS

ดาวน์โหลดและติดตั้ง

  • สนใจโปรแกรมทดสอบดาวน์โหลดได้ตามลิงค์นี้ OpenGPSProject.zip
  • เปิด Lazarus ติดตั้งคอมโพเน็นต์ OpenGPSX ก่อน แล้วค่อย unzip ไฟล์ทดสอบ OpenGPSProject.zip ของผมเองเก็บไว้ที่ /home/priabroy/Lazarus/OpenGPSProject
หน้าตาของ Lazarus ที่ติดตั้ง component OpenGPSX แล้ว
  • เปิดโปรแกรมทดสอบ OpenGPSProject เปิด project ไฟล์ opengpstest.lpi แล้วกด F9 เพื่อรันได้เลย สังเกตว่าการ compile & build ใน Linux จะเร็วมากกว่าบนวินโดส์เทียบกันไม่ติด แต่ตอนรันโปรแกรมดูเหมือนในวินโดส์ทำงานได้เร็วกว่า
  • เปิดไฟล์ NMEA ที่อยู่ในโฟลเดอร์ Data ชื่อไฟล์ gpslog-mod.txt แล้วคลิกปุ่ม “Play NMEA >>”
เปิดไฟล์ NMEA คือไฟล์ gpslog-mod.txt
  • ลองทดสอบกันอีกไฟล์คือไฟล์ griffith-park.nmea จะเห็นหน้าตาตอนรันโปรแกรมดังนี้
ทดสอบด้วยไฟล์ griffith-park.nmea

ปัญหาของโค้ด Cross-platform

  • ผมมีเกร็ดจะเล่าคือเรื่องปํญหาโค้ด cross-platform จากสโลแกนของ Lazarus ที่ว่า Write Once Compile Anywhere ผมเห็นว่าไม่ร้อยเปอร์เซ็นต์ อย่างเช่น Class TGPSSkyplot โค้ดที่ใช้ได้ในวินโดส์กลับไม่ work ใน Linux  TGPSSkyplot เป็น class ที่ผมใช้เขียนสัญลักษณ์ GPS บนท้องฟ้า ในวินโดส์ใช้การเขียน Sprite ได้ แต่กลับใช้ไม่ได้ใน Linux ต้องหันมาใช้วิธีอื่น ลองดูโค้ดกันครับ
      </ul>
      </ul>
      &nbsp;
      <ul>
      <ul>procedure TGPSSkyPlot.DrawGPSSkyPlot(ASatInfos : TGSVInfos);</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>var</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>rSat, rFull : TRect;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>r : TRect;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>sk1, img1 : TPoint;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>x, y : integer;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>i : integer;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>dDist : double;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>Sat : TGSVInfo;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>ts : TSize;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>begin</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>if Assigned(ASatInfos) then</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>begin</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>FGSVInfos := ASatInfos;</ul>
      </ul>
      rSat := Rect(0, 0, FSatSize, FSatSize);
      rFull := Rect(0, 0, FBmpBackground.Width, FBmpBackground.Height);
      //First Sat, Copy FBufferBackgroundImg to FBmpBuffer.
      if (TGSVInfo(FGSVInfos.items[0]).FirstSat) then
      FBmpBuffer.Canvas.CopyRect(rFull, FBmpBackground.Canvas, rFull);
      
      for i := 0 to FGSVInfos.Count - 1 do
      begin
      Sat := TGSVInfo(FGSVInfos.Items[i]);
      dDist := (90 - Sat.Elevation) * FSkyHalfW / 90;
      sk1 := Polar2Cartesian(trunc(dDist),Sat.Azimuth);
      img1 := Cartesian2Pixel(sk1, FTransfrm);
      //Keep image coordinate system into TSatInfo.
      Sat.X := img1.X;
      Sat.Y := img1.Y;
      {Draw Sprites works very well in WINDOWS, Not in Linux}
      {$IFDEF WINDOWS}
      BitBlt(FBmpSatDest[i].Canvas.Handle, 0, 0, FBmpSatDest[i].Width, FBmpSatDest[i].Height,
      FBmpBuffer.Canvas.Handle, img1.X - FImgShift, img1.Y - FImgShift, SRCCOPY);
      BitBlt(FBmpSatDest[i].Canvas.Handle, 0, 0, FBmpSatDest[i].Width, FBmpSatDest[i].Height,
      FBmpMaskSat.Canvas.Handle, 0, 0, SRCAND);
      if (Sat.SNR = 0) then
      BitBlt(FBmpSatDest[i].Canvas.Handle, 0, 0, FBmpSatDest[i].Width, FBmpSatDest[i].Height,
      FBmpRedSpriteSat.Canvas.Handle, 0, 0, SRCINVERT)
      else
      BitBlt(FBmpSatDest[i].Canvas.Handle, 0, 0, FBmpSatDest[i].Width, FBmpSatDest[i].Height,
      FBmpGreenSpriteSat.Canvas.Handle, 0, 0, SRCINVERT);
      {$ENDIF}
      {$IFDEF LINUX}
      BitBlt(FBmpSatDest[i].Canvas.Handle, 0, 0, FBmpSatDest[i].Width, FBmpSatDest[i].Height,
      FBmpBuffer.Canvas.Handle, img1.X - FImgShift, img1.Y - FImgShift, SRCCOPY);
      if (Sat.SNR = 0) then
      FBmpSatDest[i].Canvas.Draw(0, 0, FRedSatImg)
      else
      FBmpSatDest[i].Canvas.Draw(0, 0, FGreenSatImg);
      {$ENDIF}
      BitBlt(FBmpBuffer.Canvas.Handle, img1.X - FImgShift, img1.Y - FImgShift,
      FBmpSatDest[i].Width, FBmpSatDest[i].Height, FBmpSatDest[i].Canvas.Handle, 0, 0, SRCCOPY);
      
      //************* Draw PRN ********************
      with FBmpTxtDest[i] do
      begin
      //Assumed image size to valid for Canvas.Font.
      Width := 10;
      Height := 10;
      Canvas.Font.Color := clBlack;
      Canvas.Font.Style := [fsBold];
      Canvas.Font.Name := 'Sans';
      Canvas.Font.Size := trunc(8/20 * FSatSize);
      ts := Canvas.TextExtent( Sat.Prn);
      //The real size.
      Width := ts.cx;
      Height := ts.cy;
      Canvas.Brush.Style := bsClear;
      Canvas.Brush.Color := self.Color;
      x := img1.X - Width div 2 - 2;
      y := img1.Y + FSatSize div 2 - 2;
      r := Rect(0 , 0, Width, Height);
      BitBlt(Canvas.Handle, 0, 0, Width, Height,
      FBmpBuffer.Canvas.Handle, x, y, SRCCOPY);
      Canvas.TextOut(0, 0, Sat.PRN);
      BitBlt(FBmpBuffer.Canvas.Handle, x, y, Width, Height, Canvas.Handle, 0, 0, SRCCOPY);
      //Last Sat, Copy FBmpBuffer to TGPSSkyplot Canvas.
      if (Sat.LastSat) then
      BitBlt(self.Canvas.Handle, 0, 0, FBmpBuffer.Width, FBmpBuffer.Height, FBmpBuffer.Canvas.Handle, 0, 0, SRCCOPY);
      end;
      end; //for
      end;
      end;
      
      
      • จะเห็นว่าผมต้องแยกโค้ดในวินโดส์และ Linux ด้วยการใช้ directive {$IFDEF} ที่ต้องแยกโค้ดแบบนี้ก็ไม่มากมายหรอกครับ บางครั้งก็สถานการณ์ก็บังคับเช่น hardware อย่าง serial port ในวินโดส์จะเริ่มตั้งแต Com1, Com2, Com3,… ส่วนในลินุกส์เริ่มจาก /dev/ttys0, /dev/ttys1, /dev/ttys2,…
      • ก็ขอจบตอนนี้ไว้ก่อน ตอนหน้ามาดูว่าถ้าต่อ GPS ผ่าน serial port ในวินโดส์และลินุกส์ บนวินโดส์ผมทดสอบเรียบร้อยแล้วใช้งานได้ดี แต่ในลินุกส์ยังไม่ได้ทดสอบ ก็มารอดูกันครับ ติดตามตอนหน้าซึ่งจะเป็นตอนสุดท้าย

การเขียนโปรแกรมต่อ GPS ด้วยคอมโพเน็นต์ OpenGPSX (ภาค 4)

OpenGPSX component on SourceForge

  • ผมได้นำ component ตัวนี้ไปใส่ใน SourceForge.net เป็นที่เรียบร้อยแล้ว ถ้าต้องการตัวที่ update ก็ไปดาวน์โหลดกันได้เลยครับ หน้าตาของ OpenGPSX บน sourceforge ก็ประมาณดังรูปด้านล่าง

OpenGPSX component บน SourceForge.net

  • จากตอนที่แล้วเราทำการแปะคอมโพเน็นต์ลงบนฟอร์ม เพื่อทดสอบอย่างง่าย ต่อไปจะเปิด project ของ Lazarus ที่ผมเขียนขึ้นเพื่อนำคอมโพเน็นต์มาใช้งาน สามารถดาวน์โหลดตามลิงค์นี้ opengpsproject.zip ก่อนจะเปิด project นี้ต้องติดตั้งตัวคอมโพเน็นต์ ที่ผมกล่าวไปแล้วในภาคที่ 3 ก่อนนะครับ ไม่งั้นจะ Error เมื่อดาวน์โหลดมาแล้วก็ unzip ไปไว้ตรงไหนก็ได้

เปิดโปรแกรมทดสอบ

  • รัน Lazarus คลิกที่เมนู Project > Open Project…

เปิดไฟล์ opengpstest.lpi

  • เมื่อเปิดไฟล์ opengpstest.lpi แล้ว ที่ source editor ของ Lazarus คลิกไปที่ main กด F12 บนคีย์บอร์ดดูเพื่อดูฟอร์ม

OpenGPSX Test Project

ตั้ง property ให้ object

  • เมื่อเราแปะ object ลงบนฟอร์ม Lazarus จะตั้งชื่อให้ NMEADecode1, GPSSkyplot1, GPSTarget1, GPSPortConnected1 และ GPSSignalplot1 เพื่อให้ object แต่ละตัวรู้จักกันจะต้องเพิ่มโค้ดประมาณ 3 บรรทัดที่ FormCreate ดังที่ผมคอมเม็นต์ไว้ครับ
      </ul>
      </ul>
      &nbsp;
      <ul>
      <ul>procedure TForm1.FormCreate(Sender: TObject);</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>begin</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>FNMEAs := TStringList.Create;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>InitControls;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>FStop := false;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>cmdStopPlay.Enabled := false;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>{Don't forget to assign for the follow