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)。
