gps.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. package gps
  2. import (
  3. "fmt"
  4. "io"
  5. "time"
  6. "github.com/bettercap/bettercap/session"
  7. "github.com/adrianmo/go-nmea"
  8. "github.com/stratoberry/go-gpsd"
  9. "github.com/tarm/serial"
  10. "github.com/evilsocket/islazy/str"
  11. )
  12. type GPS struct {
  13. session.SessionModule
  14. serialPort string
  15. baudRate int
  16. serial *serial.Port
  17. gpsd *gpsd.Session
  18. }
  19. var ModeInfo = [4]string{
  20. "NoValueSeen",
  21. "NoFix",
  22. "Mode2D",
  23. "Mode3D",
  24. }
  25. func NewGPS(s *session.Session) *GPS {
  26. mod := &GPS{
  27. SessionModule: session.NewSessionModule("gps", s),
  28. serialPort: "/dev/ttyUSB0",
  29. baudRate: 4800,
  30. }
  31. mod.AddParam(session.NewStringParameter("gps.device",
  32. mod.serialPort,
  33. "",
  34. "Serial device of the GPS hardware or hostname:port for a GPSD instance."))
  35. mod.AddParam(session.NewIntParameter("gps.baudrate",
  36. fmt.Sprintf("%d", mod.baudRate),
  37. "Baud rate of the GPS serial device."))
  38. mod.AddHandler(session.NewModuleHandler("gps on", "",
  39. "Start acquiring from the GPS hardware.",
  40. func(args []string) error {
  41. return mod.Start()
  42. }))
  43. mod.AddHandler(session.NewModuleHandler("gps off", "",
  44. "Stop acquiring from the GPS hardware.",
  45. func(args []string) error {
  46. return mod.Stop()
  47. }))
  48. mod.AddHandler(session.NewModuleHandler("gps.show", "",
  49. "Show the last coordinates returned by the GPS hardware.",
  50. func(args []string) error {
  51. return mod.Show()
  52. }))
  53. return mod
  54. }
  55. func (mod *GPS) Name() string {
  56. return "gps"
  57. }
  58. func (mod *GPS) Description() string {
  59. return "A module talking with GPS hardware on a serial interface or via GPSD."
  60. }
  61. func (mod *GPS) Author() string {
  62. return "Simone Margaritelli <evilsocket@gmail.com>"
  63. }
  64. func (mod *GPS) Configure() (err error) {
  65. if mod.Running() {
  66. return session.ErrAlreadyStarted(mod.Name())
  67. } else if err, mod.serialPort = mod.StringParam("gps.device"); err != nil {
  68. return err
  69. } else if err, mod.baudRate = mod.IntParam("gps.baudrate"); err != nil {
  70. return err
  71. }
  72. if mod.serialPort[0] == '/' || mod.serialPort[0] == '.' {
  73. mod.Debug("connecting to serial port %s", mod.serialPort)
  74. mod.serial, err = serial.OpenPort(&serial.Config{
  75. Name: mod.serialPort,
  76. Baud: mod.baudRate,
  77. ReadTimeout: time.Second * 1,
  78. })
  79. } else {
  80. mod.Debug("connecting to gpsd at %s", mod.serialPort)
  81. mod.gpsd, err = gpsd.Dial(mod.serialPort)
  82. }
  83. return
  84. }
  85. func (mod *GPS) readLine() (line string, err error) {
  86. var n int
  87. b := make([]byte, 1)
  88. for {
  89. if n, err = mod.serial.Read(b); err != nil {
  90. return
  91. } else if n == 1 {
  92. if b[0] == '\n' {
  93. return str.Trim(line), nil
  94. } else {
  95. line += string(b[0])
  96. }
  97. }
  98. }
  99. }
  100. func (mod *GPS) Show() error {
  101. mod.Printf("latitude:%f longitude:%f quality:%s satellites:%d altitude:%f\n",
  102. mod.Session.GPS.Latitude,
  103. mod.Session.GPS.Longitude,
  104. mod.Session.GPS.FixQuality,
  105. mod.Session.GPS.NumSatellites,
  106. mod.Session.GPS.Altitude)
  107. mod.Session.Refresh()
  108. return nil
  109. }
  110. func (mod *GPS) readFromSerial() {
  111. if line, err := mod.readLine(); err == nil {
  112. if s, err := nmea.Parse(line); err == nil {
  113. // http://aprs.gids.nl/nmea/#gga
  114. if m, ok := s.(nmea.GGA); ok {
  115. mod.Session.GPS.Updated = time.Now()
  116. mod.Session.GPS.Latitude = m.Latitude
  117. mod.Session.GPS.Longitude = m.Longitude
  118. mod.Session.GPS.FixQuality = m.FixQuality
  119. mod.Session.GPS.NumSatellites = m.NumSatellites
  120. mod.Session.GPS.HDOP = m.HDOP
  121. mod.Session.GPS.Altitude = m.Altitude
  122. mod.Session.GPS.Separation = m.Separation
  123. mod.Session.Events.Add("gps.new", mod.Session.GPS)
  124. }
  125. } else {
  126. mod.Debug("error parsing line '%s': %s", line, err)
  127. }
  128. } else if err != io.EOF {
  129. mod.Warning("error while reading serial port: %s", err)
  130. }
  131. }
  132. func (mod *GPS) runFromGPSD() {
  133. mod.gpsd.AddFilter("TPV", func(r interface{}) {
  134. report := r.(*gpsd.TPVReport)
  135. mod.Session.GPS.Updated = report.Time
  136. mod.Session.GPS.Latitude = report.Lat
  137. mod.Session.GPS.Longitude = report.Lon
  138. mod.Session.GPS.FixQuality = ModeInfo[report.Mode]
  139. mod.Session.GPS.Altitude = report.Alt
  140. mod.Session.Events.Add("gps.new", mod.Session.GPS)
  141. })
  142. mod.gpsd.AddFilter("SKY", func(r interface{}) {
  143. report := r.(*gpsd.SKYReport)
  144. mod.Session.GPS.NumSatellites = int64(len(report.Satellites))
  145. mod.Session.GPS.HDOP = report.Hdop
  146. //mod.Session.GPS.Separation = 0
  147. })
  148. done := mod.gpsd.Watch()
  149. <-done
  150. }
  151. func (mod *GPS) Start() error {
  152. if err := mod.Configure(); err != nil {
  153. return err
  154. }
  155. return mod.SetRunning(true, func() {
  156. mod.Info("started on port %s ...", mod.serialPort)
  157. if mod.serial != nil {
  158. defer mod.serial.Close()
  159. for mod.Running() {
  160. mod.readFromSerial()
  161. }
  162. } else {
  163. mod.runFromGPSD()
  164. }
  165. })
  166. }
  167. func (mod *GPS) Stop() error {
  168. return mod.SetRunning(false, func() {
  169. if mod.serial != nil {
  170. // let the read fail and exit
  171. mod.serial.Close()
  172. } /*
  173. FIXME: no Close or Stop method in github.com/stratoberry/go-gpsd
  174. else {
  175. if err := mod.gpsd.Close(); err != nil {
  176. mod.Error("failed closing the connection to GPSD: %s", err)
  177. }
  178. } */
  179. })
  180. }