gpsd v3.20 源代碼分析

1 gps_device_t

gps_device_t定義gps設(shè)備。

  • 成員device_type的類型是gps_type_t,保存設(shè)備類型信息。
  • 成員lexer的類型是gps_lexer_t,保存解析設(shè)備消息得到的數(shù)據(jù)。
  • 成員context保存上下文,它是gps_context_t類型。

全局數(shù)組devices[] 保存所有的設(shè)備。

struct gps_device_t devices[MAX_DEVICES];

2 gps_type_t

gps_type_t定義gps消息解析器。

  • type_name是類型名,packet_type是類型枚舉值。
  • 虛擬函數(shù)get_packet()讀packet
  • 虛擬函數(shù)parse_packet()解析packet
  • driver_nmea0183解析符合NMEA-0183協(xié)議的消息。

全局數(shù)組gpsd_drivers[]保存了所有支持的gpsd消息解析器。

const struct gps_type_t *gpsd_driver_array[] = {
&driver_unknown,
&driver_nmea0183,
};

const struct gps_type_t **gpsd_drivers = &gpsd_driver_array[0];

3 gps_context_t

gps_context_t保存上下文。

  • 成員shmTime[]是一個數(shù)組,每個元素保存一個shmTime結(jié)構(gòu)。成員shmTimeInUse[]標識shmTime[]中的元素是否已經(jīng)使用。shmTime[]大小為NTPSHMSEGS
#define MAX_DEVICES 4
#define NTPSHMSEGS  (MAX_DEVICES * 2)

3.1 ntpshm_context_init()

ntpshm_context_init()初始化gps_context_t的成員shmTime[]。shmTime[]的內(nèi)存來自共享內(nèi)存,共享內(nèi)存的key是NTP0+n,n是shmTime[]中元素的索引。

#define NTPD_BASE 0x4e545030  // "NTP0"

4 gps_data_t與gps_fix_t

gps_data_t和gps_fix_t保存解析消息得到的數(shù)據(jù)。

5 pps_thread_t與inner_context_t

pps_thread_t保存pps線程需要的數(shù)據(jù)。

  • devicename是設(shè)備名字
  • devicefs是pps設(shè)備的fd。
  • fix_in:pps會將讀到的時間戳數(shù)據(jù)寫入這個成員。這是真實時間的源頭。
  • pps_out: 線程得到的時間戳寫入這個成員。

inner_context_t包裝pps_thread_t,作為創(chuàng)建pps線程的參數(shù)。除了pps_thread是pps_thread_t外,

  • pps_canwait標識pps設(shè)備是否支持wait操作
  • pps_caps指pps設(shè)備能力
  • kernelpps_handle是kernel PPS設(shè)備句柄。

6 gpsd_poll()

6.1 gpsd_poll()

gpsd_poll() 處理gps消息。

  • 調(diào)用generic_get()從gps_device_t::gps_fd讀入消息并解析,解析結(jié)果寫入gps_lexer_t實例。成員gps_lexer_t::outbuffer保存,成員type保存消息類型。
  • 如果解析到的這個消息類型(gps_lexer_t::type)與gps_device_t當(dāng)前的消息類型(gps_device_t::gps_type_t::packet_type)不同,則在gpsd_drivers[]中重新找到匹配的gps_type_t實例,并調(diào)用gspd_switch_driver(),將它綁定到gps_device_t的成員gps_device_t::device_type。
  • 這里找到的gps_type_t是driver_nmea0183
  • 調(diào)用gps_type_t::parse_type()解析數(shù)據(jù)包。對于driver_nmea0183,解析函數(shù)是generic_parse_input()。
  • 調(diào)用gps_merge_fix(),根據(jù)gps_data_t::set的值,用成員newdata給成員gpsdata.fix賦值。

6.2 generic_get()

generic_get()從gps設(shè)備中讀消息并解析。它將工作委托給packet_get()。

  • 調(diào)用read(),從gps_device_t::gpsdata(gps_data_t)::gps_fd讀入消息,保存到gps_device_t::lexer(gps_lexer_t)::inbuffer中。
  • 調(diào)用packet_parse()作詞法分析。分析結(jié)果寫入gps_device_t::gps_lexer_t/lexer::outbuffer。在packet_parse()中,
  • nextstate()逐個解析字符,并設(shè)置此時的狀態(tài)機。如果解析到一個消息,則指定消息類型調(diào)用packet_accept()。如NMEA_PACKET、BAD_PACKET等。
  • packet_accept()將解析的結(jié)果從gps_lexer_t的inbuffer復(fù)制到outbuffer,并設(shè)置gps_lexer_type::type。

6.3 generic_parse_input()

generic_parse_input()解析消息字符串,它的主要工作都是委托nmea_parse()完成的。
在nmea_parse()中,

  • 調(diào)用gpsd_utc_resolve(),從消息中解析時間戳,保存到gps_device_t::newdata::time。
  • gps_device_t::gps_lexer_t/lexer::outbuffer中保存了收到的消息。解析消息的關(guān)鍵字,如“RMC’、“GGA”。
  • 遍歷數(shù)組nmea_phrase[],找到關(guān)鍵字對應(yīng)的處理函數(shù),并調(diào)用它。如RMC,則調(diào)用processRMC();GGA,則調(diào)用processGGA()。
  • processRMC()或processGGA()解析消息字符串,得到其中包含的時間戳,并設(shè)置mask的TIME_SET標志位。
  • 最后檢查這次處理的結(jié)果是否可以作為一個reporting cycle的開始/結(jié)束消息。如果一個cycle完成,則設(shè)置mask的REPORT_IS位。

7 gpsd_multipoll()

7.1 gpsd_multipoll()

gpsd_multipoll()解析gpsd消息得到時間,然后寫入共享內(nèi)存。

  • 調(diào)用gpsd_poll()接收、解析gpsd消息,將時間戳保存在gps_device_t::newdata中。newdata的類型是gps_fix_t。
  • gpsd_multipoll()的參數(shù)之一是一個函數(shù)指針,這里調(diào)用這個函數(shù),它實際上是all_reports()。

7.2 all_reports()

  • 調(diào)用ntp_latch()從gps_device_t::newdata中讀出時間戳,保存到timedelta_t結(jié)構(gòu)中。
  • 調(diào)用pps_thread_fix()通知pps處理線程,這個線程由pps_thread_t管理。pps_thread_fix()實際上是將時間戳保存到pps_thread_t的成員fix_in中。
  • 調(diào)用ntpshm_put(),它又調(diào)用ntp_write(),將時間戳寫入gps_device_t::shm_clock指定的共享內(nèi)存中。

8 ntpshm_link_activate()

nptshm_link_activate()激活 pps線程。它的參數(shù)gps_device_t。

  • 調(diào)用ntpshm_alloc()從gps_context_t::shmTim[]數(shù)組找到一個存放shmTime的位置。
  • gps_device_t::pps_thread_t的成員report_hook是一個函數(shù)指針,后面pps線程會調(diào)用它。這里將它設(shè)置為report_hook()。
  • 調(diào)用pps_thread_activate()。其中,
    • 調(diào)用init_kernel_pps()打開pps設(shè)備。其中調(diào)用open()和time_pps_create(),后者創(chuàng)建用于讀pps脈沖的設(shè)備句柄。
    • 調(diào)用pthread_create()創(chuàng)建pps線程,線程的處理函數(shù)是gpsd_ppsmonitor()。

9 gpsd_ppsmonitor()

gpsd_pps_monitor()是線程的處理函數(shù)。

  • 有兩種等待pps脈沖的方式。如果pps設(shè)備支持TIOCMIWAIT模式,則調(diào)用get_edge_tiocmiwait(),否則調(diào)用get_edge_rfc2783()。這里以后者為例說明。
    • get_edge_rfc2783()調(diào)用time_pps_fetch()等待pps脈沖,等到就返回,同時得到這時的系統(tǒng)時間。
    • 然后它將thread_context的成員fix_in保存到參數(shù)last_fixtime。fix_in的值是在all_reports()函數(shù)中保存的時間戳,這個時間戳是從gps設(shè)備中讀到的;將系統(tǒng)時間保存到參數(shù)clock_ts。
  • 各種pps設(shè)備的脈沖模式可能有較大差別。計算這次的clock_ts與前一次的clock_ts的差值duration,判斷這個pps設(shè)備的脈沖是哪種模式,如果哪種都不是,則中止處理。
  • 將系統(tǒng)時間和來自gps設(shè)備的時間保存到本地變量ppstimes中,然后以它為參數(shù)調(diào)用
    pps_thread_t的成員函數(shù)指針report_hook,也就是report_hook()

10 report_hook()

report_hook()通知感興趣的client程序有新的時間戳到達。

  • 調(diào)用chrony_send()發(fā)送通知消息。
  • 調(diào)用ntpshm_put()寫gps_device_t的成員共享內(nèi)存shm_pps。

11 main()

main()初始化設(shè)備,然后在循環(huán)中讀取設(shè)備。

  • 調(diào)用passivesocks()初始化udp服務(wù)器,以便與感興趣的客戶端程序通信。
  • 調(diào)用ntpshm_context_init()初始化共享內(nèi)存,以便寫時間戳給客戶端程序讀取。
  • 調(diào)用gpsd_add_device()初始化gps設(shè)備。
  • 在循環(huán)中,調(diào)用gpsd_multipoll(),讀取設(shè)備。這里指定作為參數(shù)的函數(shù)指針為all_reports()。

12 gpsd_add_device()

gpsd_add_device()初始化gps設(shè)備和pps設(shè)備。

  • 調(diào)用gpsd_activate()初始化gps設(shè)備。
  • 調(diào)用ntpshm_link_activate()初始化pps設(shè)備。

13 gpsd_activate()

gpsd_activate()初始化gps設(shè)備。

  • 調(diào)用gpsd_open()打開設(shè)備
  • 調(diào)用gpsd_clear()重置狀態(tài)。

相關(guān)鏈接

gpsd v3.20 源代碼分析
chrony v3.5 源代碼分析
ptp4l v4.2源代碼分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容