A PX4 based camera pointer

pipoint.go 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. // Copyright 2017 Google Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. package pipoint
  16. import (
  17. "log"
  18. "time"
  19. "juju.net.nz/x/pipoint/param"
  20. "juju.net.nz/x/pipoint/util"
  21. common "gobot.io/x/gobot/platforms/mavlink/common"
  22. "gobot.io/x/gobot/platforms/mqtt"
  23. )
  24. const (
  25. dt = time.Millisecond * 20
  26. )
  27. var (
  28. // Version is the overall binary version. Set from the
  29. // build.
  30. Version = "dev"
  31. )
  32. // State is a handler for the current state of the system.
  33. type State interface {
  34. Name() string
  35. Update(param *param.Param)
  36. }
  37. // PiPoint is an automatic, GPS based system that points a camera at the rover.
  38. type PiPoint struct {
  39. Params *param.Params
  40. state *param.Param
  41. version *param.Param
  42. tick *param.Param
  43. seconds *param.Param
  44. messages *param.Param
  45. heartbeats *param.Param
  46. heartbeat *param.Param
  47. attitude *param.Param
  48. gps *param.Param
  49. gpsFix *param.Param
  50. neu *param.Param
  51. baseOffset *param.Param
  52. pred *param.Param
  53. rover *param.Param
  54. base *param.Param
  55. sysStatus *param.Param
  56. link *param.Param
  57. linkLast int
  58. remote *param.Param
  59. command *param.Param
  60. mark *param.Param
  61. vel *param.Param
  62. sp *param.Param
  63. offset *param.Param
  64. pan *Servo
  65. tilt *Servo
  66. latPred *LinPred
  67. lonPred *LinPred
  68. altPred *LinPred
  69. states []State
  70. elog *EventLogger
  71. log *log.Logger
  72. param param.ParamChannel
  73. audio *AudioOut
  74. }
  75. // NewPiPoint creates a new camera pointer.
  76. func NewPiPoint() *PiPoint {
  77. p := &PiPoint{
  78. Params: param.NewParams("pipoint"),
  79. latPred: &LinPred{},
  80. lonPred: &LinPred{},
  81. altPred: &LinPred{},
  82. elog: NewEventLogger("pipoint"),
  83. param: make(param.ParamChannel, 10),
  84. audio: NewAudioOut(),
  85. }
  86. p.log = p.elog.logger
  87. p.states = []State{
  88. &LocateState{pi: p},
  89. &OrientateState{pi: p},
  90. &RunState{pi: p},
  91. &HoldState{pi: p},
  92. &CycleState{pi: p},
  93. }
  94. p.link = p.Params.NewNum("link.status")
  95. p.remote = p.Params.New("remote")
  96. p.command = p.Params.NewNum("command")
  97. p.mark = p.Params.NewNum("mark")
  98. p.version = p.Params.NewWith("build_label", Version)
  99. p.tick = p.Params.NewNum("tick")
  100. p.seconds = p.Params.NewNum("tick")
  101. p.messages = p.Params.NewNum("rover.messages")
  102. p.state = p.Params.NewNum("state")
  103. p.heartbeat = p.Params.NewWith("heartbeat", &common.Heartbeat{})
  104. p.heartbeats = p.Params.NewNum("heartbeat")
  105. p.gps = p.Params.New("gps")
  106. p.gpsFix = p.Params.NewNum("gps.fix")
  107. p.vel = p.Params.NewNum("gps.vog")
  108. p.neu = p.Params.New("position")
  109. p.pred = p.Params.New("pred")
  110. p.attitude = p.Params.New("rover.attitude")
  111. p.rover = p.Params.New("rover.position")
  112. p.base = p.Params.New("base.position")
  113. p.baseOffset = p.Params.NewWith("base.offset", &NEUPosition{})
  114. p.sysStatus = p.Params.New("rover.status")
  115. p.sp = p.Params.NewWith("pantilt.sp", &Attitude{})
  116. p.offset = p.Params.NewWith("pantilt.offset", &Attitude{})
  117. p.pan = NewServo("pantilt.pan", p.Params)
  118. p.tilt = NewServo("pantilt.tilt", p.Params)
  119. p.Params.Listen(p.param)
  120. p.Params.Load()
  121. return p
  122. }
  123. // AddMQTT adds a new MQTT connection that bridges between MQTT and
  124. // params.
  125. func (pi *PiPoint) AddMQTT(mqtt *mqtt.Adaptor) {
  126. param.NewParamMQTTBridge(pi.Params, mqtt, "")
  127. }
  128. // Run is the main entry point that runs forever.
  129. func (pi *PiPoint) Run() {
  130. tick := time.NewTicker(dt)
  131. pi.audio.Say("Base ready")
  132. for {
  133. select {
  134. case param := <-pi.param:
  135. pi.update(param)
  136. case <-tick.C:
  137. pi.ticked()
  138. }
  139. }
  140. }
  141. func (pi *PiPoint) ticked() {
  142. now := util.Now()
  143. pi.tick.SetFloat64(now)
  144. pi.seconds.UpdateInt(int(now))
  145. if !pi.heartbeat.Ok() {
  146. pi.link.UpdateInt(2)
  147. }
  148. pred := &Position{
  149. Time: now,
  150. Lat: pi.latPred.GetEx(now),
  151. Lon: pi.lonPred.GetEx(now),
  152. Alt: pi.altPred.GetEx(now),
  153. }
  154. pi.pred.Set(pred)
  155. pi.pan.Tick()
  156. pi.tilt.Tick()
  157. }
  158. func (pi *PiPoint) predict(gps *Position) {
  159. now := pi.tick.GetFloat64()
  160. pi.latPred.SetEx(gps.Lat, now, gps.Time)
  161. pi.lonPred.SetEx(gps.Lon, now, gps.Time)
  162. pi.altPred.SetEx(gps.Alt, now, gps.Time)
  163. }
  164. func (pi *PiPoint) update(param *param.Param) {
  165. if state := pi.getState(); state != nil {
  166. state.Update(param)
  167. }
  168. pi.announce(param)
  169. pi.log.Printf("%s %T %#v\n", param.Name, param.Get(), param.Get())
  170. }
  171. func (pi *PiPoint) getState() State {
  172. state := pi.state.GetInt()
  173. if state >= 0 && state < len(pi.states) {
  174. return pi.states[state]
  175. }
  176. return nil
  177. }
  178. func (pi *PiPoint) announce(param *param.Param) {
  179. switch param {
  180. case pi.state:
  181. if state := pi.getState(); state != nil {
  182. pi.audio.Say(state.Name())
  183. }
  184. case pi.link:
  185. link := param.GetInt()
  186. if link != pi.linkLast {
  187. pi.linkLast = link
  188. switch link {
  189. case 1:
  190. pi.audio.Say("Rover ready")
  191. case 2:
  192. pi.audio.Say("Rover offline")
  193. }
  194. }
  195. case pi.gpsFix:
  196. if param.GetInt() >= 3 {
  197. pi.audio.Say("GPS ready")
  198. }
  199. }
  200. }
  201. // Message handles a MAVLink message.
  202. func (pi *PiPoint) Message(msg interface{}) {
  203. switch msg.(type) {
  204. case *common.Heartbeat:
  205. pi.link.SetInt(1)
  206. pi.heartbeats.Inc()
  207. pi.heartbeat.Set(msg.(*common.Heartbeat))
  208. case *common.SysStatus:
  209. pi.sysStatus.Set(msg.(*common.SysStatus))
  210. case *common.GpsRawInt:
  211. gps := msg.(*common.GpsRawInt)
  212. pi.gps.Set(&Position{
  213. Time: float64(gps.TIME_USEC) * 1e-6,
  214. Lat: float64(gps.LAT) * 1e-7,
  215. Lon: float64(gps.LON) * 1e-7,
  216. Alt: float64(gps.ALT) * 1e-3,
  217. Heading: float64(gps.COG) * 1e-2,
  218. })
  219. pi.neu.Set(pi.gps.Get().(*Position).ToNEU())
  220. pi.vel.SetFloat64(float64(gps.VEL) * 1e-2)
  221. pi.gpsFix.UpdateInt(int(gps.FIX_TYPE))
  222. case *common.Attitude:
  223. att := msg.(*common.Attitude)
  224. pi.attitude.Set(&Attitude{
  225. float64(att.ROLL),
  226. float64(att.PITCH),
  227. float64(att.YAW),
  228. })
  229. case *common.RcChannels:
  230. remote := msg.(*common.RcChannels)
  231. att := &Attitude{
  232. Roll: util.ServoToScale(remote.CHAN1_RAW),
  233. Pitch: util.ServoToScale(remote.CHAN2_RAW),
  234. Yaw: util.ServoToScale(remote.CHAN4_RAW),
  235. }
  236. pi.remote.Set(att)
  237. command := util.ScaleToPos(att.Yaw)
  238. if changed, _ := pi.command.UpdateInt(command); changed {
  239. if command == -2 {
  240. pi.mark.Inc()
  241. }
  242. }
  243. default:
  244. }
  245. pi.messages.Inc()
  246. pi.log.Printf("%s %T %#v\n", "message", msg, msg)
  247. }