kopia lustrzana https://github.com/cyoung/stratux
Use RY835AI or RY836AI; add more fields to GDL90 report
rodzic
ddaf673006
commit
d91a2a10e7
|
@ -163,8 +163,8 @@ func prepareMessage(data []byte) []byte {
|
||||||
// Compute CRC before modifying the message.
|
// Compute CRC before modifying the message.
|
||||||
crc := crcCompute(data)
|
crc := crcCompute(data)
|
||||||
// Add the two CRC16 bytes before replacing control characters.
|
// Add the two CRC16 bytes before replacing control characters.
|
||||||
data = append(data, byte(crc&0xFF))
|
data = append(data, byte(crc & 0xFF))
|
||||||
data = append(data, byte(crc>>8))
|
data = append(data, byte((crc >> 8) & 0xFF))
|
||||||
|
|
||||||
tmp := []byte{0x7E} // Flag start.
|
tmp := []byte{0x7E} // Flag start.
|
||||||
|
|
||||||
|
@ -426,8 +426,8 @@ func makeStratuxStatus() []byte {
|
||||||
|
|
||||||
// Connected hardware: number of radios.
|
// Connected hardware: number of radios.
|
||||||
msg[15] = msg[15] | (byte(globalStatus.Devices) & 0x3)
|
msg[15] = msg[15] | (byte(globalStatus.Devices) & 0x3)
|
||||||
// Connected hardware: RY835AI.
|
// Connected hardware: RY83XAI.
|
||||||
if globalStatus.RY835AI_connected {
|
if globalStatus.RY83XAI_connected {
|
||||||
msg[15] = msg[15] | (1 << 2)
|
msg[15] = msg[15] | (1 << 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -975,7 +975,7 @@ type status struct {
|
||||||
GPS_satellites_tracked uint16
|
GPS_satellites_tracked uint16
|
||||||
GPS_connected bool
|
GPS_connected bool
|
||||||
GPS_solution string
|
GPS_solution string
|
||||||
RY835AI_connected bool
|
RY83XAI_connected bool
|
||||||
Uptime int64
|
Uptime int64
|
||||||
Clock time.Time
|
Clock time.Time
|
||||||
UptimeClock time.Time
|
UptimeClock time.Time
|
||||||
|
@ -1290,7 +1290,7 @@ func main() {
|
||||||
//FIXME: Only do this if data logging is enabled.
|
//FIXME: Only do this if data logging is enabled.
|
||||||
initDataLog()
|
initDataLog()
|
||||||
|
|
||||||
initRY835AI()
|
initRY83XAI()
|
||||||
|
|
||||||
// Start the heartbeat message loop in the background, once per second.
|
// Start the heartbeat message loop in the background, once per second.
|
||||||
go heartBeatSender()
|
go heartBeatSender()
|
||||||
|
|
|
@ -135,7 +135,10 @@ func sendToAllConnectedClients(msg networkMessage) {
|
||||||
if sleepFlag {
|
if sleepFlag {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
netconn.Conn.Write(msg.msg) // Write immediately.
|
_, err := netconn.Conn.Write(msg.msg) // Write immediately.
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("GDL Message error: %s\n", err.Error())
|
||||||
|
}
|
||||||
totalNetworkMessagesSent++
|
totalNetworkMessagesSent++
|
||||||
globalStatus.NetworkDataMessagesSent++
|
globalStatus.NetworkDataMessagesSent++
|
||||||
globalStatus.NetworkDataMessagesSentNonqueueable++
|
globalStatus.NetworkDataMessagesSentNonqueueable++
|
||||||
|
|
156
main/ry83Xai.go
156
main/ry83Xai.go
|
@ -4,7 +4,7 @@
|
||||||
that can be found in the LICENSE file, herein included
|
that can be found in the LICENSE file, herein included
|
||||||
as part of this header.
|
as part of this header.
|
||||||
|
|
||||||
ry835ai.go: GPS functions, GPS init, AHRS status messages, other external sensor monitoring.
|
ry83Xai.go: GPS functions, GPS init, AHRS status messages, other external sensor monitoring.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
@ -28,6 +28,7 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"../mpu"
|
"../mpu"
|
||||||
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -37,6 +38,7 @@ const (
|
||||||
SAT_TYPE_GALILEO = 3 // GAxxx; NMEA IDs unknown
|
SAT_TYPE_GALILEO = 3 // GAxxx; NMEA IDs unknown
|
||||||
SAT_TYPE_BEIDOU = 4 // GBxxx; NMEA IDs 201-235
|
SAT_TYPE_BEIDOU = 4 // GBxxx; NMEA IDs 201-235
|
||||||
SAT_TYPE_SBAS = 10 // NMEA IDs 33-54
|
SAT_TYPE_SBAS = 10 // NMEA IDs 33-54
|
||||||
|
MPURETRYNUM = 5 // Number of times to retry connecting to MPU
|
||||||
)
|
)
|
||||||
|
|
||||||
type SatelliteInfo struct {
|
type SatelliteInfo struct {
|
||||||
|
@ -432,9 +434,9 @@ func validateNMEAChecksum(s string) (string, bool) {
|
||||||
// changes while on the ground and "movement" is really only changes in GPS fix as it settles down.
|
// changes while on the ground and "movement" is really only changes in GPS fix as it settles down.
|
||||||
//TODO: Some more robust checking above current and last speed.
|
//TODO: Some more robust checking above current and last speed.
|
||||||
//TODO: Dynamic adjust for gain based on groundspeed
|
//TODO: Dynamic adjust for gain based on groundspeed
|
||||||
//westphae: Do I need to do anything here?
|
//TODO westphae: Do I need to do anything here?
|
||||||
func setTrueCourse(groundSpeed uint16, trueCourse float64) {
|
func setTrueCourse(groundSpeed uint16, trueCourse float64) {
|
||||||
if myMPU != nil && globalStatus.RY835AI_connected && globalSettings.AHRS_Enabled {
|
if myMPU != nil && globalStatus.RY83XAI_connected && globalSettings.AHRS_Enabled {
|
||||||
if mySituation.GroundSpeed >= 7 && groundSpeed >= 7 {
|
if mySituation.GroundSpeed >= 7 && groundSpeed >= 7 {
|
||||||
myMPU.ResetHeading(trueCourse, 0.10)
|
myMPU.ResetHeading(trueCourse, 0.10)
|
||||||
}
|
}
|
||||||
|
@ -942,7 +944,7 @@ func processNMEALine(l string) (sentenceUsed bool) {
|
||||||
tmpSituation := mySituation // If we decide to not use the data in this message, then don't make incomplete changes in mySituation.
|
tmpSituation := mySituation // If we decide to not use the data in this message, then don't make incomplete changes in mySituation.
|
||||||
|
|
||||||
//$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A
|
//$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A
|
||||||
/* check RY835 man for NMEA version, if >2.2, add mode field
|
/* check RY83XAI man for NMEA version, if >2.2, add mode field
|
||||||
Where:
|
Where:
|
||||||
RMC Recommended Minimum sentence C
|
RMC Recommended Minimum sentence C
|
||||||
123519 Fix taken at 12:35:19 UTC
|
123519 Fix taken at 12:35:19 UTC
|
||||||
|
@ -1363,10 +1365,31 @@ func initBMP180() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO westphae: set up myMPU as MPU6050 or MPU9250, depending on which exists
|
|
||||||
func initMPU() error {
|
func initMPU() error {
|
||||||
myMPU, _ = mpu.NewMPU6050() //TODO: error checking.
|
var err error
|
||||||
return nil
|
|
||||||
|
for i:=0; i < MPURETRYNUM; i++ {
|
||||||
|
myMPU, err = mpu.NewMPU9250()
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
} else {
|
||||||
|
log.Println("AHRS: Successfully initialized MPU9250")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < MPURETRYNUM; i++ {
|
||||||
|
myMPU, err = mpu.NewMPU6050()
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
} else {
|
||||||
|
log.Println("AHRS: Successfully initialized MPU6050")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("AHRS Error: couldn't initialize MPU9250 or MPU6050")
|
||||||
|
return errors.New("AHRS Error: couldn't initialize MPU9250 or MPU6050")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initI2C() error {
|
func initI2C() error {
|
||||||
|
@ -1377,21 +1400,21 @@ func initI2C() error {
|
||||||
// Unused at the moment. 5 second update, since read functions in bmp180 are slow.
|
// Unused at the moment. 5 second update, since read functions in bmp180 are slow.
|
||||||
func tempAndPressureReader() {
|
func tempAndPressureReader() {
|
||||||
timer := time.NewTicker(5 * time.Second)
|
timer := time.NewTicker(5 * time.Second)
|
||||||
for globalStatus.RY835AI_connected && globalSettings.AHRS_Enabled {
|
for globalStatus.RY83XAI_connected && globalSettings.AHRS_Enabled {
|
||||||
<-timer.C
|
<-timer.C
|
||||||
// Read temperature and pressure altitude.
|
// Read temperature and pressure altitude.
|
||||||
temp, alt, err_bmp180 := readBMP180()
|
temp, alt, err_bmp180 := readBMP180()
|
||||||
// Process.
|
// Process.
|
||||||
if err_bmp180 != nil {
|
if err_bmp180 != nil {
|
||||||
log.Printf("readBMP180(): %s\n", err_bmp180.Error())
|
log.Printf("readBMP180(): %s\n", err_bmp180.Error())
|
||||||
globalStatus.RY835AI_connected = false
|
globalStatus.RY83XAI_connected = false
|
||||||
} else {
|
} else {
|
||||||
mySituation.Temp = temp
|
mySituation.Temp = temp
|
||||||
mySituation.Pressure_alt = alt
|
mySituation.Pressure_alt = alt
|
||||||
mySituation.LastTempPressTime = stratuxClock.Time
|
mySituation.LastTempPressTime = stratuxClock.Time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
globalStatus.RY835AI_connected = false
|
globalStatus.RY83XAI_connected = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeFFAHRSSimReport() {
|
func makeFFAHRSSimReport() {
|
||||||
|
@ -1401,19 +1424,23 @@ func makeFFAHRSSimReport() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeAHRSGDL90Report() {
|
func makeAHRSGDL90Report() {
|
||||||
msg := make([]byte, 16)
|
msg := make([]byte, 24)
|
||||||
msg[0] = 0x4c
|
msg[0] = 0x4c
|
||||||
msg[1] = 0x45
|
msg[1] = 0x45
|
||||||
msg[2] = 0x01
|
msg[2] = 0x01
|
||||||
msg[3] = 0x00
|
msg[3] = 0x01
|
||||||
|
|
||||||
pitch := int16(float64(mySituation.Pitch) * float64(10.0))
|
roll := int16(mySituation.Roll*10)
|
||||||
roll := int16(float64(mySituation.Roll) * float64(10.0))
|
pitch := int16(mySituation.Pitch*10)
|
||||||
hdg := uint16(float64(mySituation.Gyro_heading) * float64(10.0))
|
yaw := uint16(mySituation.Gyro_heading*10)
|
||||||
slip_skid := int16(float64(mySituation.SlipSkid) * float64(10.0))
|
slip_skid := int16(mySituation.SlipSkid*10)
|
||||||
turn_rate := int16(float64(mySituation.RateOfTurn) * float64(10.0))
|
turn_rate := int16(mySituation.RateOfTurn*10)
|
||||||
g := int16(float64(mySituation.GLoad) * float64(10.0))
|
g := int16(mySituation.GLoad*10)
|
||||||
|
airspeed := 0x7FFF // Can add this once we can read airspeed
|
||||||
|
palt := uint16(mySituation.Pressure_alt+5000)
|
||||||
|
vs := int16(mySituation.GPSVertVel) //TODO: record BMP rate of climb
|
||||||
|
|
||||||
|
//TODO westphae: invalidate each with 0x7FFF when data is invalid
|
||||||
// Roll.
|
// Roll.
|
||||||
msg[4] = byte((roll >> 8) & 0xFF)
|
msg[4] = byte((roll >> 8) & 0xFF)
|
||||||
msg[5] = byte(roll & 0xFF)
|
msg[5] = byte(roll & 0xFF)
|
||||||
|
@ -1423,14 +1450,14 @@ func makeAHRSGDL90Report() {
|
||||||
msg[7] = byte(pitch & 0xFF)
|
msg[7] = byte(pitch & 0xFF)
|
||||||
|
|
||||||
// Heading.
|
// Heading.
|
||||||
msg[8] = byte((hdg >> 8) & 0xFF)
|
msg[8] = byte((yaw >> 8) & 0xFF)
|
||||||
msg[9] = byte(hdg & 0xFF)
|
msg[9] = byte(yaw & 0xFF)
|
||||||
|
|
||||||
// Slip/skid.
|
// Slip/skid.
|
||||||
msg[10] = byte((slip_skid >> 8) & 0xFF)
|
msg[10] = byte((slip_skid >> 8) & 0xFF)
|
||||||
msg[11] = byte(slip_skid & 0xFF)
|
msg[11] = byte(slip_skid & 0xFF)
|
||||||
|
|
||||||
// Yaw rate.
|
// Turn rate.
|
||||||
msg[12] = byte((turn_rate >> 8) & 0xFF)
|
msg[12] = byte((turn_rate >> 8) & 0xFF)
|
||||||
msg[13] = byte(turn_rate & 0xFF)
|
msg[13] = byte(turn_rate & 0xFF)
|
||||||
|
|
||||||
|
@ -1438,33 +1465,78 @@ func makeAHRSGDL90Report() {
|
||||||
msg[14] = byte((g >> 8) & 0xFF)
|
msg[14] = byte((g >> 8) & 0xFF)
|
||||||
msg[15] = byte(g & 0xFF)
|
msg[15] = byte(g & 0xFF)
|
||||||
|
|
||||||
|
// Indicated Airspeed
|
||||||
|
msg[16] = byte((airspeed >> 8) & 0xFF)
|
||||||
|
msg[17] = byte(airspeed & 0xFF)
|
||||||
|
|
||||||
|
// Pressure Altitude
|
||||||
|
//TODO westphae: this is just for testing; 0x7FFF it until BMP280 is working
|
||||||
|
msg[18] = byte((palt >> 8) & 0xFF)
|
||||||
|
msg[19] = byte(palt & 0xFF)
|
||||||
|
|
||||||
|
// Vertical Speed
|
||||||
|
msg[20] = byte((vs >> 8) & 0xFF)
|
||||||
|
msg[21] = byte(vs & 0xFF)
|
||||||
|
|
||||||
|
// Reserved
|
||||||
|
msg[22] = 0x7F
|
||||||
|
msg[23] = 0xFF
|
||||||
|
|
||||||
sendMsg(prepareMessage(msg), NETWORK_AHRS_GDL90, false)
|
sendMsg(prepareMessage(msg), NETWORK_AHRS_GDL90, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func attitudeReaderSender() {
|
func attitudeReaderSender() {
|
||||||
timer := time.NewTicker(100 * time.Millisecond) // ~10Hz update.
|
timer := time.NewTicker(100 * time.Millisecond) // ~10Hz update.
|
||||||
for globalStatus.RY835AI_connected && globalSettings.AHRS_Enabled {
|
for globalStatus.RY83XAI_connected && globalSettings.AHRS_Enabled {
|
||||||
<-timer.C
|
<-timer.C
|
||||||
// Read pitch and roll.
|
// Read pitch and roll.
|
||||||
pitch, err_pitch := myMPU.Pitch()
|
pitch, err_pitch := myMPU.Pitch()
|
||||||
if err_pitch != nil {
|
if err_pitch != nil {
|
||||||
log.Printf("readMPU6050(): %s\n", err_pitch.Error())
|
log.Printf("AHRS MPU Error: %s\n", err_pitch.Error())
|
||||||
globalStatus.RY835AI_connected = false
|
globalStatus.RY83XAI_connected = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
roll, err_roll := myMPU.Roll()
|
roll, err_roll := myMPU.Roll()
|
||||||
|
|
||||||
if err_roll != nil {
|
if err_roll != nil {
|
||||||
log.Printf("readMPU6050(): %s\n", err_roll.Error())
|
log.Printf("AHRS MPU Error: %s\n", err_roll.Error())
|
||||||
globalStatus.RY835AI_connected = false
|
globalStatus.RY83XAI_connected = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
heading, err_heading := myMPU.Heading() //FIXME. Experimental.
|
heading, err_heading := myMPU.Heading() //FIXME. Experimental.
|
||||||
if err_heading != nil {
|
if err_heading != nil {
|
||||||
log.Printf("readMPU6050(): %s\n", err_heading.Error())
|
log.Printf("AHRS MPU Error: %s\n", err_heading.Error())
|
||||||
globalStatus.RY835AI_connected = false
|
globalStatus.RY83XAI_connected = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
headingMag, err_headingMag := myMPU.MagHeading()
|
||||||
|
if err_headingMag != nil {
|
||||||
|
log.Printf("AHRS MPU Error: %s\n", err_headingMag.Error())
|
||||||
|
globalStatus.RY83XAI_connected = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
slipSkid, err_slipSkid := myMPU.SlipSkid()
|
||||||
|
if err_slipSkid != nil {
|
||||||
|
log.Printf("AHRS MPU Error: %s\n", err_slipSkid.Error())
|
||||||
|
globalStatus.RY83XAI_connected = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
turnRate, err_turnRate := myMPU.RateOfTurn()
|
||||||
|
if err_turnRate != nil {
|
||||||
|
log.Printf("AHRS MPU Error: %s\n", err_turnRate.Error())
|
||||||
|
globalStatus.RY83XAI_connected = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
gLoad, err_gLoad := myMPU.GLoad()
|
||||||
|
if err_gLoad != nil {
|
||||||
|
log.Printf("AHRS MPU Error: %s\n", err_gLoad.Error())
|
||||||
|
globalStatus.RY83XAI_connected = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1473,6 +1545,10 @@ func attitudeReaderSender() {
|
||||||
mySituation.Pitch = pitch
|
mySituation.Pitch = pitch
|
||||||
mySituation.Roll = roll
|
mySituation.Roll = roll
|
||||||
mySituation.Gyro_heading = heading
|
mySituation.Gyro_heading = heading
|
||||||
|
mySituation.Mag_heading = headingMag
|
||||||
|
mySituation.SlipSkid = slipSkid
|
||||||
|
mySituation.RateOfTurn = turnRate
|
||||||
|
mySituation.GLoad = gLoad
|
||||||
mySituation.LastAttitudeTime = stratuxClock.Time
|
mySituation.LastAttitudeTime = stratuxClock.Time
|
||||||
|
|
||||||
// Send, if valid.
|
// Send, if valid.
|
||||||
|
@ -1483,7 +1559,7 @@ func attitudeReaderSender() {
|
||||||
|
|
||||||
mySituation.mu_Attitude.Unlock()
|
mySituation.mu_Attitude.Unlock()
|
||||||
}
|
}
|
||||||
globalStatus.RY835AI_connected = false
|
globalStatus.RY83XAI_connected = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1559,27 +1635,29 @@ func isTempPressValid() bool {
|
||||||
|
|
||||||
func initAHRS() error {
|
func initAHRS() error {
|
||||||
if err := initI2C(); err != nil { // I2C bus.
|
if err := initI2C(); err != nil { // I2C bus.
|
||||||
|
log.Println("AHRS Error: Couldn't initialize i2c bus")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := initBMP180(); err != nil { // I2C temperature and pressure altitude.
|
if err := initBMP180(); err != nil { // I2C temperature and pressure altitude.
|
||||||
|
log.Println("AHRS Error: No BMP180, Closing i2c bus")
|
||||||
i2cbus.Close()
|
i2cbus.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// TODO westphae: Initialize MPU9250 here
|
|
||||||
if err := initMPU(); err != nil { // I2C accel/gyro.
|
if err := initMPU(); err != nil { // I2C accel/gyro.
|
||||||
|
log.Println("AHRS Error: Couldn't init MPU, closing i2c bus")
|
||||||
i2cbus.Close()
|
i2cbus.Close()
|
||||||
myBMP180.Close()
|
myBMP180.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
globalStatus.RY835AI_connected = true
|
globalStatus.RY83XAI_connected = true
|
||||||
go attitudeReaderSender()
|
go attitudeReaderSender()
|
||||||
go tempAndPressureReader()
|
//go tempAndPressureReader()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pollRY835AI() {
|
func pollRY83XAI() {
|
||||||
readyToInitGPS = true //TO-DO: Implement more robust method (channel control) to kill zombie serial readers
|
readyToInitGPS = true //TODO: Implement more robust method (channel control) to kill zombie serial readers
|
||||||
timer := time.NewTicker(4 * time.Second)
|
timer := time.NewTicker(4 * time.Second)
|
||||||
for {
|
for {
|
||||||
<-timer.C
|
<-timer.C
|
||||||
|
@ -1590,22 +1668,22 @@ func pollRY835AI() {
|
||||||
go gpsSerialReader()
|
go gpsSerialReader()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// RY835AI I2C enabled, was not connected previously?
|
// RY83XAI I2C enabled, was not connected previously?
|
||||||
if globalSettings.AHRS_Enabled && !globalStatus.RY835AI_connected {
|
if globalSettings.AHRS_Enabled && !globalStatus.RY83XAI_connected {
|
||||||
err := initAHRS()
|
err := initAHRS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("initAHRS(): %s\ndisabling AHRS sensors.\n", err.Error())
|
log.Printf("initAHRS(): %s\ndisabling AHRS sensors.\n", err.Error())
|
||||||
globalStatus.RY835AI_connected = false
|
globalStatus.RY83XAI_connected = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initRY835AI() {
|
func initRY83XAI() {
|
||||||
mySituation.mu_GPS = &sync.Mutex{}
|
mySituation.mu_GPS = &sync.Mutex{}
|
||||||
mySituation.mu_Attitude = &sync.Mutex{}
|
mySituation.mu_Attitude = &sync.Mutex{}
|
||||||
satelliteMutex = &sync.Mutex{}
|
satelliteMutex = &sync.Mutex{}
|
||||||
Satellites = make(map[string]SatelliteInfo)
|
Satellites = make(map[string]SatelliteInfo)
|
||||||
|
|
||||||
go pollRY835AI()
|
go pollRY83XAI()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
package mpu
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"time"
|
"time"
|
||||||
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
//https://www.olimex.com/Products/Modules/Sensors/MOD-MPU6050/resources/RM-MPU-60xxA_rev_4.pdf
|
//https://www.olimex.com/Products/Modules/Sensors/MOD-MPU6050/resources/RM-MPU-60xxA_rev_4.pdf
|
||||||
|
@ -42,14 +43,19 @@ type MPU6050 struct {
|
||||||
// New returns a handle to a MPU6050 sensor.
|
// New returns a handle to a MPU6050 sensor.
|
||||||
func NewMPU6050() (*MPU6050, error) {
|
func NewMPU6050() (*MPU6050, error) {
|
||||||
n := &MPU6050{poll: pollDelay}
|
n := &MPU6050{poll: pollDelay}
|
||||||
n.startUp()
|
if err := n.startUp(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MPU6050) startUp() error {
|
func (d *MPU6050) startUp() error {
|
||||||
mpu_sample_rate := 10 // 10 Hz read rate of hardware IMU
|
mpu_sample_rate := 10 // 10 Hz read rate of hardware IMU
|
||||||
yaw_mix_factor := 0 // must be zero if no magnetometer
|
yaw_mix_factor := 0 // must be zero if no magnetometer
|
||||||
mpu.InitMPU(mpu_sample_rate, yaw_mix_factor)
|
err := mpu.InitMPU(mpu_sample_rate, yaw_mix_factor)
|
||||||
|
if err != 0 {
|
||||||
|
return errors.New("MPU6050 Error: couldn't start MPU")
|
||||||
|
}
|
||||||
|
|
||||||
d.pitch_history = make([]float64, 0)
|
d.pitch_history = make([]float64, 0)
|
||||||
d.roll_history = make([]float64, 0)
|
d.roll_history = make([]float64, 0)
|
||||||
|
|
|
@ -0,0 +1,192 @@
|
||||||
|
// Package MPU9250 provides a stratux interface to the MPU9250 IMU
|
||||||
|
package mpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/westphae/goflying/mpu9250"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DECAY = 0.98
|
||||||
|
GYRORANGE = 250
|
||||||
|
ACCELRANGE = 4
|
||||||
|
UPDATEFREQ = 100
|
||||||
|
CALIBTIME int64 = 5*60*1000000000
|
||||||
|
)
|
||||||
|
|
||||||
|
type MPU9250 struct {
|
||||||
|
mpu *mpu9250.MPU9250
|
||||||
|
pitch, roll, heading float64
|
||||||
|
headingMag float64
|
||||||
|
slipSkid float64
|
||||||
|
turnRate float64
|
||||||
|
gLoad float64
|
||||||
|
T int64
|
||||||
|
valid bool
|
||||||
|
nextCalibrateT int64
|
||||||
|
quit chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMPU9250() (*MPU9250, error) {
|
||||||
|
var (
|
||||||
|
m MPU9250
|
||||||
|
mpu *mpu9250.MPU9250
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
mpu, err = mpu9250.NewMPU9250(GYRORANGE, ACCELRANGE, UPDATEFREQ, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("AHRS Error: couldn't initialize MPU9250")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mpu.CalibrateGyro(1)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("AHRS: Gyro calibration failed: %s\n", err.Error())
|
||||||
|
} else {
|
||||||
|
log.Println("AHRS: Gyro calibration successful")
|
||||||
|
m.nextCalibrateT = time.Now().UnixNano() + CALIBTIME
|
||||||
|
}
|
||||||
|
|
||||||
|
m.mpu = mpu
|
||||||
|
m.valid = true
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
m.run()
|
||||||
|
|
||||||
|
return &m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MPU9250) run() {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
go func() {
|
||||||
|
m.quit = make(chan struct{})
|
||||||
|
clock := time.NewTicker(100 * time.Millisecond)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-clock.C:
|
||||||
|
Ts, Gx, Gy, Gz, Ax, Ay, Az, Mx, My, _, gaError, magError := m.mpu.Read()
|
||||||
|
|
||||||
|
if gaError == nil {
|
||||||
|
m.T = Ts
|
||||||
|
smooth(&m.turnRate, Gz)
|
||||||
|
smooth(&m.gLoad, math.Sqrt(Ax * Ax + Ay * Ay + Az * Az))
|
||||||
|
smooth(&m.slipSkid, Ay / Az)
|
||||||
|
|
||||||
|
// Quick and dirty calcs just to test - these are no good for pitch >> 0
|
||||||
|
m.pitch += 0.1 * Gx
|
||||||
|
m.roll += 0.1 * Gy
|
||||||
|
m.heading -= 0.1 * Gz
|
||||||
|
|
||||||
|
if m.pitch > 90 {
|
||||||
|
m.pitch = 180-m.pitch
|
||||||
|
}
|
||||||
|
if m.pitch < -90 {
|
||||||
|
m.pitch = -180 - m.pitch
|
||||||
|
}
|
||||||
|
if (m.roll > 180) || (m.roll < -180) {
|
||||||
|
m.roll = -m.roll
|
||||||
|
}
|
||||||
|
if m.heading > 360 {
|
||||||
|
m.heading -= 360
|
||||||
|
}
|
||||||
|
if m.heading < 0 {
|
||||||
|
m.heading += 360
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if magError == nil {
|
||||||
|
smooth(&m.headingMag, math.Atan2(My, Mx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calibrate if past-due
|
||||||
|
if time.Now().UnixNano() > m.nextCalibrateT {
|
||||||
|
err := m.mpu.CalibrateGyro(1)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("AHRS: Error calibrating gyro, %s\n", err)
|
||||||
|
} else {
|
||||||
|
log.Println("AHRS: Gyro calibration successful")
|
||||||
|
m.nextCalibrateT = time.Now().UnixNano() + CALIBTIME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case <-m.quit:
|
||||||
|
m.mpu.CloseMPU()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func smooth(val *float64, new float64) {
|
||||||
|
*val = DECAY * *val + (1-DECAY)*new
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MPU9250) ResetHeading(newHeading float64, gain float64) {
|
||||||
|
m.heading = newHeading
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MPU9250) Pitch() (float64, error) {
|
||||||
|
if m.valid {
|
||||||
|
return m.pitch, nil
|
||||||
|
} else {
|
||||||
|
return 0, errors.New("MPU error: data not available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MPU9250) Roll() (float64, error) {
|
||||||
|
if m.valid {
|
||||||
|
return m.roll, nil
|
||||||
|
} else {
|
||||||
|
return 0, errors.New("MPU error: data not available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MPU9250) Heading() (float64, error) {
|
||||||
|
if m.valid {
|
||||||
|
return m.heading, nil
|
||||||
|
} else {
|
||||||
|
return 0, errors.New("MPU error: data not available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MPU9250) MagHeading() (float64, error) {
|
||||||
|
if m.valid {
|
||||||
|
return m.headingMag, nil
|
||||||
|
} else {
|
||||||
|
return 0, errors.New("MPU error: data not available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MPU9250) SlipSkid() (float64, error) {
|
||||||
|
if m.valid {
|
||||||
|
return m.slipSkid, nil
|
||||||
|
} else {
|
||||||
|
return 0, errors.New("MPU error: data not available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MPU9250) RateOfTurn() (float64, error) {
|
||||||
|
if m.valid {
|
||||||
|
return m.turnRate, nil
|
||||||
|
} else {
|
||||||
|
return 0, errors.New("MPU error: data not available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MPU9250) GLoad() (float64, error) {
|
||||||
|
if m.valid {
|
||||||
|
return m.gLoad, nil
|
||||||
|
} else {
|
||||||
|
return 0, errors.New("MPU error: data not available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MPU9250) Close() {
|
||||||
|
if m.quit != nil {
|
||||||
|
m.quit <- struct{}{}
|
||||||
|
}
|
||||||
|
}
|
Ładowanie…
Reference in New Issue