gobot.io/x/gobot/v2@v2.1.0/platforms/parrot/bebop/client/client.go (about)

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"net"
     8  	"time"
     9  )
    10  
    11  func validatePitch(val int) int {
    12  	if val > 100 {
    13  		return 100
    14  	} else if val < 0 {
    15  		return 0
    16  	}
    17  
    18  	return val
    19  }
    20  
    21  type tmpFrame struct {
    22  	arstreamACK   ARStreamACK
    23  	fragments     map[int][]byte
    24  	frame         []byte
    25  	waitForIframe bool
    26  	frameFlags    int
    27  }
    28  
    29  type ARStreamACK struct {
    30  	FrameNumber    int
    31  	HighPacketsAck uint64
    32  	LowPacketsAck  uint64
    33  }
    34  
    35  type ARStreamFrame struct {
    36  	FrameNumber       int
    37  	FrameFlags        int
    38  	FragmentNumber    int
    39  	FragmentsPerFrame int
    40  	Frame             []byte
    41  }
    42  
    43  func NewARStreamFrame(buf []byte) ARStreamFrame {
    44  	//
    45  	// ARSTREAM_NetworkHeaders_DataHeader_t;
    46  	//
    47  	// uint16_t frameNumber;
    48  	// uint8_t  frameFlags; // Infos on the current frame
    49  	// uint8_t  fragmentNumber; // Index of the current fragment in current frame
    50  	// uint8_t  fragmentsPerFrame; // Number of fragments in current frame
    51  	//
    52  	// * frameFlags structure :
    53  	// *  x x x x x x x x
    54  	// *  | | | | | | | \-> FLUSH FRAME
    55  	// *  | | | | | | \-> UNUSED
    56  	// *  | | | | | \-> UNUSED
    57  	// *  | | | | \-> UNUSED
    58  	// *  | | | \-> UNUSED
    59  	// *  | | \-> UNUSED
    60  	// *  | \-> UNUSED
    61  	// *  \-> UNUSED
    62  	// *
    63  	//
    64  
    65  	frame := ARStreamFrame{
    66  		FrameFlags:        int(buf[2]),
    67  		FragmentNumber:    int(buf[3]),
    68  		FragmentsPerFrame: int(buf[4]),
    69  	}
    70  
    71  	var number uint16
    72  	binary.Read(bytes.NewReader(buf[0:2]), binary.LittleEndian, &number)
    73  
    74  	frame.FrameNumber = int(number)
    75  
    76  	frame.Frame = buf[5:]
    77  
    78  	return frame
    79  }
    80  
    81  type NetworkFrame struct {
    82  	Type int
    83  	Seq  int
    84  	Id   int
    85  	Size int
    86  	Data []byte
    87  }
    88  
    89  func NewNetworkFrame(buf []byte) NetworkFrame {
    90  	frame := NetworkFrame{
    91  		Type: int(buf[0]),
    92  		Id:   int(buf[1]),
    93  		Seq:  int(buf[2]),
    94  		Data: []byte{},
    95  	}
    96  
    97  	var size uint32
    98  	binary.Read(bytes.NewReader(buf[3:7]), binary.LittleEndian, &size)
    99  	frame.Size = int(size)
   100  
   101  	frame.Data = buf[7:frame.Size]
   102  
   103  	return frame
   104  }
   105  
   106  func networkFrameGenerator() func(*bytes.Buffer, byte, byte) *bytes.Buffer {
   107  	//func networkFrameGenerator() func(*bytes.Buffer, byte, byte) NetworkFrame {
   108  	//
   109  	// ARNETWORKAL_Frame_t
   110  	//
   111  	// uint8  type  - frame type ARNETWORK_FRAME_TYPE
   112  	// uint8  id    - identifier of the buffer sending the frame
   113  	// uint8  seq   - sequence number of the frame
   114  	// uint32 size  - size of the frame
   115  	//
   116  
   117  	// each frame id has it's own sequence number
   118  	seq := make(map[byte]byte)
   119  
   120  	hlen := 7 // size of ARNETWORKAL_Frame_t header
   121  
   122  	return func(cmd *bytes.Buffer, frameType byte, id byte) *bytes.Buffer {
   123  		if _, ok := seq[id]; !ok {
   124  			seq[id] = 0
   125  		}
   126  
   127  		seq[id]++
   128  
   129  		if seq[id] > 255 {
   130  			seq[id] = 0
   131  		}
   132  
   133  		ret := &bytes.Buffer{}
   134  		ret.WriteByte(frameType)
   135  		ret.WriteByte(id)
   136  		ret.WriteByte(seq[id])
   137  
   138  		size := &bytes.Buffer{}
   139  		binary.Write(size, binary.LittleEndian, uint32(cmd.Len()+hlen))
   140  
   141  		ret.Write(size.Bytes())
   142  		ret.Write(cmd.Bytes())
   143  
   144  		return ret
   145  	}
   146  }
   147  
   148  type Pcmd struct {
   149  	Flag  int
   150  	Roll  int
   151  	Pitch int
   152  	Yaw   int
   153  	Gaz   int
   154  	Psi   float32
   155  }
   156  
   157  type Bebop struct {
   158  	IP                    string
   159  	NavData               map[string]string
   160  	Pcmd                  Pcmd
   161  	tmpFrame              tmpFrame
   162  	C2dPort               int
   163  	D2cPort               int
   164  	RTPStreamPort         int
   165  	RTPControlPort        int
   166  	DiscoveryPort         int
   167  	c2dClient             *net.UDPConn
   168  	d2cClient             *net.UDPConn
   169  	discoveryClient       *net.TCPConn
   170  	networkFrameGenerator func(*bytes.Buffer, byte, byte) *bytes.Buffer
   171  	video                 chan []byte
   172  	writeChan             chan []byte
   173  }
   174  
   175  func New() *Bebop {
   176  	return &Bebop{
   177  		IP:                    "192.168.42.1",
   178  		NavData:               make(map[string]string),
   179  		C2dPort:               54321,
   180  		D2cPort:               43210,
   181  		RTPStreamPort:         55004,
   182  		RTPControlPort:        55005,
   183  		DiscoveryPort:         44444,
   184  		networkFrameGenerator: networkFrameGenerator(),
   185  		Pcmd: Pcmd{
   186  			Flag:  0,
   187  			Roll:  0,
   188  			Pitch: 0,
   189  			Yaw:   0,
   190  			Gaz:   0,
   191  			Psi:   0,
   192  		},
   193  		tmpFrame:  tmpFrame{},
   194  		video:     make(chan []byte),
   195  		writeChan: make(chan []byte),
   196  	}
   197  }
   198  
   199  func (b *Bebop) write(buf []byte) (int, error) {
   200  	b.writeChan <- buf
   201  	return 0, nil
   202  }
   203  
   204  func (b *Bebop) Discover() error {
   205  	addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", b.IP, b.DiscoveryPort))
   206  
   207  	if err != nil {
   208  		return err
   209  	}
   210  
   211  	b.discoveryClient, err = net.DialTCP("tcp", nil, addr)
   212  
   213  	if err != nil {
   214  		return err
   215  	}
   216  
   217  	b.discoveryClient.Write(
   218  		[]byte(
   219  			fmt.Sprintf(`{
   220  						"controller_type": "computer",
   221  						"controller_name": "go-bebop",
   222  						"d2c_port": "%d",
   223  						"arstream2_client_stream_port": "%d",
   224  						"arstream2_client_control_port": "%d",
   225  						}`,
   226  				b.D2cPort,
   227  				b.RTPStreamPort,
   228  				b.RTPControlPort),
   229  		),
   230  	)
   231  
   232  	data := make([]byte, 10240)
   233  
   234  	_, err = b.discoveryClient.Read(data)
   235  
   236  	if err != nil {
   237  		return err
   238  	}
   239  
   240  	return b.discoveryClient.Close()
   241  }
   242  
   243  func (b *Bebop) Connect() error {
   244  	err := b.Discover()
   245  
   246  	if err != nil {
   247  		return err
   248  	}
   249  
   250  	c2daddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", b.IP, b.C2dPort))
   251  
   252  	if err != nil {
   253  		return err
   254  	}
   255  
   256  	b.c2dClient, err = net.DialUDP("udp", nil, c2daddr)
   257  
   258  	if err != nil {
   259  		return err
   260  	}
   261  
   262  	d2caddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", b.D2cPort))
   263  
   264  	if err != nil {
   265  		return err
   266  	}
   267  	b.d2cClient, err = net.ListenUDP("udp", d2caddr)
   268  	if err != nil {
   269  		return err
   270  	}
   271  
   272  	go func() {
   273  		for {
   274  			_, err := b.c2dClient.Write(<-b.writeChan)
   275  
   276  			if err != nil {
   277  				fmt.Println(err)
   278  			}
   279  		}
   280  	}()
   281  
   282  	go func() {
   283  		for {
   284  			data := make([]byte, 40960)
   285  			i, _, err := b.d2cClient.ReadFromUDP(data)
   286  			if err != nil {
   287  				fmt.Println("d2cClient error:", err)
   288  			}
   289  
   290  			b.packetReceiver(data[0:i])
   291  		}
   292  	}()
   293  
   294  	// send pcmd values at 40hz
   295  	go func() {
   296  		// wait a little bit so that there is enough time to get some ACKs
   297  		time.Sleep(500 * time.Millisecond)
   298  		for {
   299  			_, err := b.write(b.generatePcmd().Bytes())
   300  			if err != nil {
   301  				fmt.Println("pcmd c2dClient.Write", err)
   302  			}
   303  			time.Sleep(25 * time.Millisecond)
   304  		}
   305  	}()
   306  
   307  	if err := b.GenerateAllStates(); err != nil {
   308  		return err
   309  	}
   310  	if err := b.FlatTrim(); err != nil {
   311  		return err
   312  	}
   313  
   314  	return nil
   315  }
   316  
   317  func (b *Bebop) FlatTrim() error {
   318  	//
   319  	// ARCOMMANDS_Generator_GenerateARDrone3PilotingFlatTrim
   320  	//
   321  
   322  	cmd := &bytes.Buffer{}
   323  
   324  	cmd.WriteByte(ARCOMMANDS_ID_PROJECT_ARDRONE3)
   325  	cmd.WriteByte(ARCOMMANDS_ID_ARDRONE3_CLASS_PILOTING)
   326  
   327  	tmp := &bytes.Buffer{}
   328  	binary.Write(tmp, binary.LittleEndian, uint16(ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_FLATTRIM))
   329  
   330  	cmd.Write(tmp.Bytes())
   331  
   332  	_, err := b.write(b.networkFrameGenerator(cmd, ARNETWORKAL_FRAME_TYPE_DATA, BD_NET_CD_NONACK_ID).Bytes())
   333  	return err
   334  }
   335  
   336  func (b *Bebop) GenerateAllStates() error {
   337  	//
   338  	// ARCOMMANDS_Generator_GenerateCommonCommonAllStates
   339  	//
   340  
   341  	cmd := &bytes.Buffer{}
   342  
   343  	cmd.WriteByte(ARCOMMANDS_ID_PROJECT_COMMON)
   344  	cmd.WriteByte(ARCOMMANDS_ID_COMMON_CLASS_COMMON)
   345  
   346  	tmp := &bytes.Buffer{}
   347  	binary.Write(tmp, binary.LittleEndian, uint16(ARCOMMANDS_ID_COMMON_COMMON_CMD_ALLSTATES))
   348  
   349  	cmd.Write(tmp.Bytes())
   350  
   351  	_, err := b.write(b.networkFrameGenerator(cmd, ARNETWORKAL_FRAME_TYPE_DATA, BD_NET_CD_NONACK_ID).Bytes())
   352  	return err
   353  }
   354  
   355  func (b *Bebop) TakeOff() error {
   356  	//
   357  	//  ARCOMMANDS_Generator_GenerateARDrone3PilotingTakeOff
   358  	//
   359  
   360  	cmd := &bytes.Buffer{}
   361  
   362  	cmd.WriteByte(ARCOMMANDS_ID_PROJECT_ARDRONE3)
   363  	cmd.WriteByte(ARCOMMANDS_ID_ARDRONE3_CLASS_PILOTING)
   364  
   365  	tmp := &bytes.Buffer{}
   366  	binary.Write(tmp, binary.LittleEndian, uint16(ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_TAKEOFF))
   367  
   368  	cmd.Write(tmp.Bytes())
   369  
   370  	_, err := b.write(b.networkFrameGenerator(cmd, ARNETWORKAL_FRAME_TYPE_DATA, BD_NET_CD_NONACK_ID).Bytes())
   371  	return err
   372  }
   373  
   374  func (b *Bebop) Land() error {
   375  	//
   376  	// ARCOMMANDS_Generator_GenerateARDrone3PilotingLanding
   377  	//
   378  
   379  	cmd := &bytes.Buffer{}
   380  
   381  	cmd.WriteByte(ARCOMMANDS_ID_PROJECT_ARDRONE3)
   382  	cmd.WriteByte(ARCOMMANDS_ID_ARDRONE3_CLASS_PILOTING)
   383  
   384  	tmp := &bytes.Buffer{}
   385  	binary.Write(tmp, binary.LittleEndian, uint16(ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_LANDING))
   386  
   387  	cmd.Write(tmp.Bytes())
   388  
   389  	_, err := b.write(b.networkFrameGenerator(cmd, ARNETWORKAL_FRAME_TYPE_DATA, BD_NET_CD_NONACK_ID).Bytes())
   390  	return err
   391  }
   392  
   393  func (b *Bebop) Up(val int) error {
   394  	b.Pcmd.Flag = 1
   395  	b.Pcmd.Gaz = validatePitch(val)
   396  	return nil
   397  }
   398  
   399  func (b *Bebop) Down(val int) error {
   400  	b.Pcmd.Flag = 1
   401  	b.Pcmd.Gaz = validatePitch(val) * -1
   402  	return nil
   403  }
   404  
   405  func (b *Bebop) Forward(val int) error {
   406  	b.Pcmd.Flag = 1
   407  	b.Pcmd.Pitch = validatePitch(val)
   408  	return nil
   409  }
   410  
   411  func (b *Bebop) Backward(val int) error {
   412  	b.Pcmd.Flag = 1
   413  	b.Pcmd.Pitch = validatePitch(val) * -1
   414  	return nil
   415  }
   416  
   417  func (b *Bebop) Right(val int) error {
   418  	b.Pcmd.Flag = 1
   419  	b.Pcmd.Roll = validatePitch(val)
   420  	return nil
   421  }
   422  
   423  func (b *Bebop) Left(val int) error {
   424  	b.Pcmd.Flag = 1
   425  	b.Pcmd.Roll = validatePitch(val) * -1
   426  	return nil
   427  }
   428  
   429  func (b *Bebop) Clockwise(val int) error {
   430  	b.Pcmd.Flag = 1
   431  	b.Pcmd.Yaw = validatePitch(val)
   432  	return nil
   433  }
   434  
   435  func (b *Bebop) CounterClockwise(val int) error {
   436  	b.Pcmd.Flag = 1
   437  	b.Pcmd.Yaw = validatePitch(val) * -1
   438  	return nil
   439  }
   440  
   441  func (b *Bebop) Stop() error {
   442  	b.Pcmd = Pcmd{
   443  		Flag:  0,
   444  		Roll:  0,
   445  		Pitch: 0,
   446  		Yaw:   0,
   447  		Gaz:   0,
   448  		Psi:   0,
   449  	}
   450  
   451  	return nil
   452  }
   453  
   454  func (b *Bebop) generatePcmd() *bytes.Buffer {
   455  	//
   456  	// ARCOMMANDS_Generator_GenerateARDrone3PilotingPCMD
   457  	//
   458  	// uint8 - flag Boolean flag to activate roll/pitch movement
   459  	// int8  - roll Roll consign for the drone [-100;100]
   460  	// int8  - pitch Pitch consign for the drone [-100;100]
   461  	// int8  - yaw Yaw consign for the drone [-100;100]
   462  	// int8  - gaz Gaz consign for the drone [-100;100]
   463  	// float - psi [NOT USED] - Magnetic north heading of the
   464  	//         controlling device (deg) [-180;180]
   465  	//
   466  
   467  	cmd := &bytes.Buffer{}
   468  	tmp := &bytes.Buffer{}
   469  
   470  	cmd.WriteByte(ARCOMMANDS_ID_PROJECT_ARDRONE3)
   471  	cmd.WriteByte(ARCOMMANDS_ID_ARDRONE3_CLASS_PILOTING)
   472  
   473  	tmp = &bytes.Buffer{}
   474  	binary.Write(tmp, binary.LittleEndian, uint16(ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_PCMD))
   475  	cmd.Write(tmp.Bytes())
   476  
   477  	tmp = &bytes.Buffer{}
   478  	binary.Write(tmp, binary.LittleEndian, uint8(b.Pcmd.Flag))
   479  	cmd.Write(tmp.Bytes())
   480  
   481  	tmp = &bytes.Buffer{}
   482  	binary.Write(tmp, binary.LittleEndian, int8(b.Pcmd.Roll))
   483  	cmd.Write(tmp.Bytes())
   484  
   485  	tmp = &bytes.Buffer{}
   486  	binary.Write(tmp, binary.LittleEndian, int8(b.Pcmd.Pitch))
   487  	cmd.Write(tmp.Bytes())
   488  
   489  	tmp = &bytes.Buffer{}
   490  	binary.Write(tmp, binary.LittleEndian, int8(b.Pcmd.Yaw))
   491  	cmd.Write(tmp.Bytes())
   492  
   493  	tmp = &bytes.Buffer{}
   494  	binary.Write(tmp, binary.LittleEndian, int8(b.Pcmd.Gaz))
   495  	cmd.Write(tmp.Bytes())
   496  
   497  	tmp = &bytes.Buffer{}
   498  	binary.Write(tmp, binary.LittleEndian, uint32(b.Pcmd.Psi))
   499  	cmd.Write(tmp.Bytes())
   500  
   501  	return b.networkFrameGenerator(cmd, ARNETWORKAL_FRAME_TYPE_DATA, BD_NET_CD_NONACK_ID)
   502  }
   503  
   504  func (b *Bebop) createAck(frame NetworkFrame) *bytes.Buffer {
   505  	//
   506  	// ARNETWORK_Receiver_ThreadRun
   507  	//
   508  
   509  	//
   510  	// libARNetwork/Sources/ARNETWORK_Manager.h#ARNETWORK_Manager_IDOutputToIDAck
   511  	//
   512  
   513  	return b.networkFrameGenerator(bytes.NewBuffer([]byte{uint8(frame.Seq)}),
   514  		ARNETWORKAL_FRAME_TYPE_ACK,
   515  		byte(uint16(frame.Id)+(ARNETWORKAL_MANAGER_DEFAULT_ID_MAX/2)),
   516  	)
   517  }
   518  
   519  func (b *Bebop) createPong(frame NetworkFrame) *bytes.Buffer {
   520  	return b.networkFrameGenerator(bytes.NewBuffer(frame.Data),
   521  		ARNETWORKAL_FRAME_TYPE_DATA,
   522  		ARNETWORK_MANAGER_INTERNAL_BUFFER_ID_PONG,
   523  	)
   524  }
   525  
   526  func (b *Bebop) packetReceiver(buf []byte) {
   527  	frame := NewNetworkFrame(buf)
   528  
   529  	//
   530  	// libARNetwork/Sources/ARNETWORK_Receiver.c#ARNETWORK_Receiver_ThreadRun
   531  	//
   532  	if frame.Type == int(ARNETWORKAL_FRAME_TYPE_DATA_WITH_ACK) {
   533  		ack := b.createAck(frame).Bytes()
   534  		_, err := b.write(ack)
   535  
   536  		if err != nil {
   537  			fmt.Println("ARNETWORKAL_FRAME_TYPE_DATA_WITH_ACK", err)
   538  		}
   539  	}
   540  
   541  	if frame.Type == int(ARNETWORKAL_FRAME_TYPE_DATA_LOW_LATENCY) &&
   542  		frame.Id == int(BD_NET_DC_VIDEO_DATA_ID) {
   543  
   544  		arstreamFrame := NewARStreamFrame(frame.Data)
   545  
   546  		ack := b.createARStreamACK(arstreamFrame).Bytes()
   547  		_, err := b.write(ack)
   548  		if err != nil {
   549  			fmt.Println("ARNETWORKAL_FRAME_TYPE_DATA_LOW_LATENCY", err)
   550  		}
   551  	}
   552  
   553  	//
   554  	// libARNetwork/Sources/ARNETWORK_Receiver.c#ARNETWORK_Receiver_ThreadRun
   555  	//
   556  	if frame.Id == int(ARNETWORK_MANAGER_INTERNAL_BUFFER_ID_PING) {
   557  		pong := b.createPong(frame).Bytes()
   558  		_, err := b.write(pong)
   559  		if err != nil {
   560  			fmt.Println("ARNETWORK_MANAGER_INTERNAL_BUFFER_ID_PING", err)
   561  		}
   562  	}
   563  }
   564  
   565  func (b *Bebop) StartRecording() error {
   566  	buf := b.videoRecord(ARCOMMANDS_ARDRONE3_MEDIARECORD_VIDEO_RECORD_START)
   567  
   568  	b.write(b.networkFrameGenerator(buf, ARNETWORKAL_FRAME_TYPE_DATA, BD_NET_CD_NONACK_ID).Bytes())
   569  	return nil
   570  }
   571  
   572  func (b *Bebop) StopRecording() error {
   573  	buf := b.videoRecord(ARCOMMANDS_ARDRONE3_MEDIARECORD_VIDEO_RECORD_STOP)
   574  
   575  	b.write(b.networkFrameGenerator(buf, ARNETWORKAL_FRAME_TYPE_DATA, BD_NET_CD_NONACK_ID).Bytes())
   576  	return nil
   577  }
   578  
   579  func (b *Bebop) videoRecord(state byte) *bytes.Buffer {
   580  	//
   581  	// ARCOMMANDS_Generator_GenerateARDrone3MediaRecordVideo
   582  	//
   583  
   584  	cmd := &bytes.Buffer{}
   585  
   586  	cmd.WriteByte(ARCOMMANDS_ID_PROJECT_ARDRONE3)
   587  	cmd.WriteByte(ARCOMMANDS_ID_ARDRONE3_CLASS_MEDIARECORD)
   588  
   589  	tmp := &bytes.Buffer{}
   590  	binary.Write(tmp,
   591  		binary.LittleEndian,
   592  		uint16(ARCOMMANDS_ID_ARDRONE3_MEDIARECORD_CMD_VIDEO),
   593  	)
   594  
   595  	cmd.Write(tmp.Bytes())
   596  
   597  	tmp = &bytes.Buffer{}
   598  	binary.Write(tmp, binary.LittleEndian, uint32(state))
   599  
   600  	cmd.Write(tmp.Bytes())
   601  
   602  	cmd.WriteByte(0)
   603  
   604  	return cmd
   605  }
   606  
   607  func (b *Bebop) Video() chan []byte {
   608  	return b.video
   609  }
   610  
   611  func (b *Bebop) HullProtection(protect bool) error {
   612  	//
   613  	// ARCOMMANDS_Generator_GenerateARDrone3SpeedSettingsHullProtection
   614  	//
   615  
   616  	cmd := &bytes.Buffer{}
   617  
   618  	cmd.WriteByte(ARCOMMANDS_ID_PROJECT_ARDRONE3)
   619  	cmd.WriteByte(ARCOMMANDS_ID_ARDRONE3_CLASS_SPEEDSETTINGS)
   620  
   621  	tmp := &bytes.Buffer{}
   622  	binary.Write(tmp,
   623  		binary.LittleEndian,
   624  		uint16(ARCOMMANDS_ID_ARDRONE3_SPEEDSETTINGS_CMD_HULLPROTECTION),
   625  	)
   626  
   627  	cmd.Write(tmp.Bytes())
   628  
   629  	tmp = &bytes.Buffer{}
   630  	binary.Write(tmp, binary.LittleEndian, bool2int8(protect))
   631  	cmd.Write(tmp.Bytes())
   632  
   633  	_, err := b.write(b.networkFrameGenerator(cmd, ARNETWORKAL_FRAME_TYPE_DATA, BD_NET_CD_NONACK_ID).Bytes())
   634  	return err
   635  }
   636  
   637  func (b *Bebop) Outdoor(outdoor bool) error {
   638  	//
   639  	// ARCOMMANDS_Generator_GenerateARDrone3SpeedSettingsOutdoor
   640  	//
   641  
   642  	cmd := &bytes.Buffer{}
   643  
   644  	cmd.WriteByte(ARCOMMANDS_ID_PROJECT_ARDRONE3)
   645  	cmd.WriteByte(ARCOMMANDS_ID_ARDRONE3_CLASS_SPEEDSETTINGS)
   646  
   647  	tmp := &bytes.Buffer{}
   648  	binary.Write(tmp,
   649  		binary.LittleEndian,
   650  		uint16(ARCOMMANDS_ID_ARDRONE3_SPEEDSETTINGS_CMD_OUTDOOR),
   651  	)
   652  
   653  	cmd.Write(tmp.Bytes())
   654  
   655  	tmp = &bytes.Buffer{}
   656  	binary.Write(tmp, binary.LittleEndian, bool2int8(outdoor))
   657  	cmd.Write(tmp.Bytes())
   658  
   659  	_, err := b.write(b.networkFrameGenerator(cmd, ARNETWORKAL_FRAME_TYPE_DATA, BD_NET_CD_NONACK_ID).Bytes())
   660  	return err
   661  }
   662  
   663  func (b *Bebop) VideoEnable(enable bool) error {
   664  	cmd := &bytes.Buffer{}
   665  
   666  	cmd.WriteByte(ARCOMMANDS_ID_PROJECT_ARDRONE3)
   667  	cmd.WriteByte(ARCOMMANDS_ID_ARDRONE3_CLASS_MEDIASTREAMING)
   668  
   669  	tmp := &bytes.Buffer{}
   670  	binary.Write(tmp,
   671  		binary.LittleEndian,
   672  		uint16(ARCOMMANDS_ID_ARDRONE3_MEDIASTREAMING_CMD_VIDEOENABLE),
   673  	)
   674  
   675  	cmd.Write(tmp.Bytes())
   676  
   677  	tmp = &bytes.Buffer{}
   678  	binary.Write(tmp, binary.LittleEndian, bool2int8(enable))
   679  	cmd.Write(tmp.Bytes())
   680  
   681  	_, err := b.write(b.networkFrameGenerator(cmd, ARNETWORKAL_FRAME_TYPE_DATA, BD_NET_CD_NONACK_ID).Bytes())
   682  	return err
   683  }
   684  
   685  func (b *Bebop) VideoStreamMode(mode int8) error {
   686  	cmd := &bytes.Buffer{}
   687  
   688  	cmd.WriteByte(ARCOMMANDS_ID_PROJECT_ARDRONE3)
   689  	cmd.WriteByte(ARCOMMANDS_ID_ARDRONE3_CLASS_MEDIASTREAMING)
   690  
   691  	tmp := &bytes.Buffer{}
   692  	binary.Write(tmp,
   693  		binary.LittleEndian,
   694  		uint16(ARCOMMANDS_ID_ARDRONE3_MEDIASTREAMING_CMD_VIDEOSTREAMMODE),
   695  	)
   696  
   697  	cmd.Write(tmp.Bytes())
   698  
   699  	tmp = &bytes.Buffer{}
   700  	binary.Write(tmp, binary.LittleEndian, mode)
   701  	cmd.Write(tmp.Bytes())
   702  
   703  	_, err := b.write(b.networkFrameGenerator(cmd, ARNETWORKAL_FRAME_TYPE_DATA, BD_NET_CD_NONACK_ID).Bytes())
   704  	return err
   705  }
   706  
   707  func bool2int8(b bool) int8 {
   708  	if b {
   709  		return 1
   710  	}
   711  	return 0
   712  }
   713  
   714  func (b *Bebop) createARStreamACK(frame ARStreamFrame) *bytes.Buffer {
   715  	//
   716  	// ARSTREAM_NetworkHeaders_AckPacket_t;
   717  	//
   718  	// uint16_t frameNumber;    // id of the current frame
   719  	// uint64_t highPacketsAck; // Upper 64 packets bitfield
   720  	// uint64_t lowPacketsAck;  // Lower 64 packets bitfield
   721  	//
   722  	// libARStream/Sources/ARSTREAM_NetworkHeaders.c#ARSTREAM_NetworkHeaders_AckPacketSetFlag
   723  	//
   724  
   725  	//
   726  	// each bit in the highPacketsAck and lowPacketsAck correspond to the
   727  	// fragmentsPerFrame which have been received per frameNumber, so time to
   728  	// flip some bits!
   729  	//
   730  	if frame.FrameNumber != b.tmpFrame.arstreamACK.FrameNumber {
   731  		if len(b.tmpFrame.fragments) > 0 {
   732  			emit := false
   733  
   734  			// if we missed some frames, wait for the next iframe
   735  			if frame.FrameNumber != b.tmpFrame.arstreamACK.FrameNumber+1 {
   736  				b.tmpFrame.waitForIframe = true
   737  			}
   738  
   739  			// if it's an iframe
   740  			if b.tmpFrame.frameFlags == 1 {
   741  				b.tmpFrame.waitForIframe = false
   742  				emit = true
   743  			} else if !b.tmpFrame.waitForIframe {
   744  				emit = true
   745  			}
   746  
   747  			if emit {
   748  				skip := false
   749  
   750  				for i := 0; i < len(b.tmpFrame.fragments); i++ {
   751  					// check if any fragments are missing
   752  					if len(b.tmpFrame.fragments[i]) == 0 {
   753  						skip = true
   754  						break
   755  					}
   756  					b.tmpFrame.frame = append(b.tmpFrame.frame, b.tmpFrame.fragments[i]...)
   757  				}
   758  
   759  				if !skip {
   760  					select {
   761  					case b.video <- b.tmpFrame.frame:
   762  					default:
   763  					}
   764  				}
   765  			}
   766  		}
   767  
   768  		b.tmpFrame.fragments = make(map[int][]byte)
   769  		b.tmpFrame.frame = []byte{}
   770  		b.tmpFrame.arstreamACK.FrameNumber = frame.FrameNumber
   771  		b.tmpFrame.frameFlags = frame.FrameFlags
   772  	}
   773  	b.tmpFrame.fragments[frame.FragmentNumber] = frame.Frame
   774  
   775  	if frame.FragmentNumber < 64 {
   776  		b.tmpFrame.arstreamACK.LowPacketsAck |= uint64(1) << uint64(frame.FragmentNumber)
   777  	} else {
   778  		b.tmpFrame.arstreamACK.HighPacketsAck |= uint64(1) << uint64(frame.FragmentNumber-64)
   779  	}
   780  
   781  	ackPacket := &bytes.Buffer{}
   782  	tmp := &bytes.Buffer{}
   783  
   784  	binary.Write(tmp, binary.LittleEndian, uint16(b.tmpFrame.arstreamACK.FrameNumber))
   785  	ackPacket.Write(tmp.Bytes())
   786  
   787  	tmp = &bytes.Buffer{}
   788  	binary.Write(tmp, binary.LittleEndian, uint64(b.tmpFrame.arstreamACK.HighPacketsAck))
   789  	ackPacket.Write(tmp.Bytes())
   790  
   791  	tmp = &bytes.Buffer{}
   792  	binary.Write(tmp, binary.LittleEndian, uint64(b.tmpFrame.arstreamACK.LowPacketsAck))
   793  	ackPacket.Write(tmp.Bytes())
   794  
   795  	return b.networkFrameGenerator(ackPacket, ARNETWORKAL_FRAME_TYPE_DATA, BD_NET_CD_VIDEO_ACK_ID)
   796  }