mirror of
https://codeberg.org/portospaceteam/ground-dashboard.git
synced 2024-12-01 10:42:25 +00:00
Receive data in dashboard
This commit is contained in:
parent
a9214847c4
commit
12f414b166
@ -5,19 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func basePressure(stream FlightData) float64 {
|
func basePressure(stream FlightData) float64 {
|
||||||
pressures := make([]float64, 0)
|
|
||||||
for _, segment := range stream.AllSegments() {
|
|
||||||
if segment.Computed.SmoothedPressure > 0 {
|
|
||||||
pressures = append(pressures, segment.Computed.SmoothedPressure)
|
|
||||||
}
|
|
||||||
if len(pressures) >= 10 {
|
|
||||||
var sum float64 = 0
|
|
||||||
for _, v := range pressures {
|
|
||||||
sum += v
|
|
||||||
}
|
|
||||||
return nanSafe(sum / float64(len(pressures)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,38 +13,31 @@ func altitude(bp float64, raw RawDataSegment) float64 {
|
|||||||
if bp == 0 {
|
if bp == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return nanSafe(44307.7 * (1 - math.Pow((raw.Pressure/100)/bp, 0.190284)))
|
return nanSafe(0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizedPressure(raw RawDataSegment) float64 {
|
func normalizedPressure(raw RawDataSegment) float64 {
|
||||||
return nanSafe(raw.Pressure / 100.0)
|
return nanSafe(0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func velocity(stream FlightData, bp float64, raw RawDataSegment) float64 {
|
func velocity(stream FlightData, bp float64, raw RawDataSegment) float64 {
|
||||||
altitude := altitude(bp, raw)
|
|
||||||
segments := stream.AllSegments()
|
|
||||||
for i := len(segments) - 1; i >= 0; i -= 1 {
|
|
||||||
if segments[i].Computed.Altitude != altitude {
|
|
||||||
return nanSafe((altitude - segments[i].Computed.Altitude) / (raw.Timestamp - segments[i].Raw.Timestamp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
func yaw(raw RawDataSegment) float64 {
|
func yaw(raw RawDataSegment) float64 {
|
||||||
return nanSafe(math.Atan2(-1.0*raw.Acceleration.X, raw.Acceleration.Z) * (180.0 / math.Pi))
|
return nanSafe(0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func pitch(raw RawDataSegment) float64 {
|
func pitch(raw RawDataSegment) float64 {
|
||||||
return nanSafe(math.Atan2(-1.0*raw.Acceleration.Y, raw.Acceleration.Z) * (180.0 / math.Pi))
|
return nanSafe(0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toRadians(degrees float64) float64 {
|
func toRadians(degrees float64) float64 {
|
||||||
return nanSafe(degrees * math.Pi / 180)
|
return nanSafe(0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toDegrees(radians float64) float64 {
|
func toDegrees(radians float64) float64 {
|
||||||
return nanSafe(radians * 180 / math.Pi)
|
return nanSafe(0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func bearing(origin Coordinate, raw RawDataSegment) float64 {
|
func bearing(origin Coordinate, raw RawDataSegment) float64 {
|
||||||
@ -77,121 +58,60 @@ func bearing(origin Coordinate, raw RawDataSegment) float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func distance(origin Coordinate, raw RawDataSegment) float64 {
|
func distance(origin Coordinate, raw RawDataSegment) float64 {
|
||||||
if origin.Lat == 0 || origin.Lon == 0 || raw.Coordinate.Lat == 0 || raw.Coordinate.Lon == 0 {
|
return 0.0
|
||||||
return 0
|
|
||||||
}
|
|
||||||
R := 6371e3
|
|
||||||
φ1 := origin.Lat * math.Pi / 180
|
|
||||||
φ2 := raw.Coordinate.Lat * math.Pi / 180
|
|
||||||
Δφ := (raw.Coordinate.Lat - origin.Lat) * math.Pi / 180
|
|
||||||
Δλ := (raw.Coordinate.Lon - origin.Lon) * math.Pi / 180
|
|
||||||
a := math.Sin(Δφ/2)*math.Sin(Δφ/2) + math.Cos(φ1)*math.Cos(φ2)*math.Sin(Δλ/2)*math.Sin(Δλ/2)
|
|
||||||
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
|
|
||||||
return nanSafe(R * c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func dataRate(stream FlightData) float64 {
|
func dataRate(stream FlightData) float64 {
|
||||||
totalsMap := make(map[int]float64)
|
|
||||||
for _, timestamp := range stream.Time() {
|
return nanSafe(0.0)
|
||||||
second := int(math.Floor(timestamp))
|
|
||||||
if total, ok := totalsMap[second]; ok {
|
|
||||||
totalsMap[second] = total + 1
|
|
||||||
} else {
|
|
||||||
totalsMap[second] = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
total := 0.0
|
|
||||||
for _, secondTotal := range totalsMap {
|
|
||||||
total += secondTotal
|
|
||||||
}
|
|
||||||
return nanSafe(total / float64(len(totalsMap)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func averageComputedValue(seconds float64, stream FlightData, raw RawDataSegment, computed ComputedDataSegment, accessor func(seg ComputedDataSegment) float64) float64 {
|
func averageComputedValue(seconds float64, stream FlightData, raw RawDataSegment, computed ComputedDataSegment, accessor func(seg ComputedDataSegment) float64) float64 {
|
||||||
total := accessor(computed)
|
total := accessor(computed)
|
||||||
n := 1.0
|
return nanSafe(total)
|
||||||
i := len(stream.AllSegments()) - 1
|
|
||||||
for i >= 0 && raw.Timestamp-stream.Time()[i] <= seconds {
|
|
||||||
total += accessor(stream.AllSegments()[i].Computed)
|
|
||||||
n++
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
return nanSafe(total / n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func determineFlightMode(stream FlightData, raw RawDataSegment, computed ComputedDataSegment) FlightMode {
|
func determineFlightMode(stream FlightData, raw RawDataSegment, computed ComputedDataSegment) FlightMode {
|
||||||
length := len(stream.AllSegments())
|
switch int(raw.FlightPhase) {
|
||||||
if length == 0 {
|
case 1:
|
||||||
return ModePrelaunch
|
return ModeWaitingLaunch
|
||||||
|
case 2:
|
||||||
|
return ModeLaunch
|
||||||
|
case 4:
|
||||||
|
return ModeLowVelocity
|
||||||
|
case 5:
|
||||||
|
return ModeNoseOver
|
||||||
|
case 6:
|
||||||
|
return ModeDrogueFired
|
||||||
|
case 7:
|
||||||
|
return ModeMainFired
|
||||||
|
case 8:
|
||||||
|
return ModeFailsafe
|
||||||
|
case 9:
|
||||||
|
return ModeLanding
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
lastMode := stream.AllSegments()[length-1].Computed.FlightMode
|
|
||||||
avgVelocity := averageComputedValue(1, stream, raw, computed, func(seg ComputedDataSegment) float64 {
|
|
||||||
return seg.SmoothedVelocity
|
|
||||||
})
|
|
||||||
avgAcceleration := averageComputedValue(1, stream, raw, computed, func(seg ComputedDataSegment) float64 {
|
|
||||||
return seg.SmoothedVerticalAcceleration
|
|
||||||
})
|
|
||||||
if lastMode == ModePrelaunch && avgVelocity > 5 {
|
|
||||||
return ModeAscentPowered
|
|
||||||
}
|
|
||||||
if lastMode == ModeAscentPowered && avgAcceleration < 0 && avgVelocity > 0 {
|
|
||||||
return ModeAscentUnpowered
|
|
||||||
}
|
|
||||||
if (lastMode == ModeAscentPowered || lastMode == ModeAscentUnpowered) && avgVelocity < 0 {
|
|
||||||
return ModeDescentFreefall
|
|
||||||
}
|
|
||||||
if lastMode == ModeDescentFreefall && math.Abs(avgAcceleration) < 0.5 {
|
|
||||||
return ModeDescentParachute
|
|
||||||
}
|
|
||||||
if (lastMode == ModeDescentFreefall || lastMode == ModeDescentParachute) && math.Abs(avgAcceleration) < 0.5 && math.Abs(avgVelocity) < 0.5 {
|
|
||||||
return ModeRecovery
|
|
||||||
}
|
|
||||||
return lastMode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ComputeDataSegment(stream FlightData, raw RawDataSegment) (ComputedDataSegment, float64, Coordinate) {
|
func ComputeDataSegment(stream FlightData, raw RawDataSegment) (ComputedDataSegment, float64, Coordinate) {
|
||||||
bp := stream.BasePressure()
|
bp := 0.0
|
||||||
if bp == 0 {
|
|
||||||
bp = basePressure(stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
origin := stream.Origin()
|
origin := raw.Coordinate
|
||||||
if origin.Lat == 0 && origin.Lon == 0 && raw.Coordinate.Lat != 0 && raw.Coordinate.Lon != 0 {
|
|
||||||
origin = raw.Coordinate
|
|
||||||
}
|
|
||||||
|
|
||||||
alt := altitude(bp, raw)
|
|
||||||
vel := velocity(stream, bp, raw)
|
|
||||||
press := normalizedPressure(raw)
|
|
||||||
|
|
||||||
smoothedAlt := alt
|
|
||||||
smoothedVel := vel
|
|
||||||
smoothedVertAccel := 0.0
|
|
||||||
smoothedPress := press
|
|
||||||
smoothedTemp := raw.Temperature
|
|
||||||
s := len(stream.AllSegments())
|
|
||||||
if s > 0 {
|
|
||||||
alpha := 0.5
|
|
||||||
smoothedAlt = smoothed(alpha, alt, stream.SmoothedAltitude()[s-1])
|
|
||||||
smoothedVel = smoothed(alpha, vel, stream.SmoothedVelocity()[s-1])
|
|
||||||
smoothedPress = smoothed(alpha, press, stream.SmoothedPressure()[s-1])
|
|
||||||
smoothedTemp = smoothed(alpha, raw.Temperature, stream.SmoothedTemperature()[s-1])
|
|
||||||
smoothedVertAccel = (smoothedVel - stream.SmoothedVelocity()[s-1]) / (raw.Timestamp - stream.Time()[s-1])
|
|
||||||
}
|
|
||||||
|
|
||||||
computed := ComputedDataSegment{
|
computed := ComputedDataSegment{
|
||||||
Altitude: alt,
|
Altitude: raw.Altitude,
|
||||||
Velocity: vel,
|
Velocity: raw.Velocity,
|
||||||
Yaw: yaw(raw),
|
Yaw: 0,
|
||||||
Pitch: pitch(raw),
|
Pitch: 0,
|
||||||
Bearing: bearing(origin, raw),
|
Bearing: 0,
|
||||||
Distance: distance(origin, raw),
|
Distance: 0,
|
||||||
DataRate: dataRate(stream),
|
DataRate: dataRate(stream),
|
||||||
SmoothedAltitude: smoothedAlt,
|
SmoothedAltitude: raw.Altitude,
|
||||||
SmoothedVelocity: smoothedVel,
|
SmoothedVelocity: raw.Velocity,
|
||||||
SmoothedPressure: smoothedPress,
|
SmoothedPressure: 0.0,
|
||||||
SmoothedTemperature: smoothedTemp,
|
SmoothedTemperature: raw.Temperature,
|
||||||
SmoothedVerticalAcceleration: smoothedVertAccel,
|
SmoothedVerticalAcceleration: 0.0,
|
||||||
}
|
}
|
||||||
|
|
||||||
computed.FlightMode = determineFlightMode(stream, raw, computed)
|
computed.FlightMode = determineFlightMode(stream, raw, computed)
|
||||||
|
@ -1,176 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"math/rand"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBasePressureSet(t *testing.T) {
|
|
||||||
segments, avg := makeDataSeries(0)
|
|
||||||
b := basePressure(&FlightDataConcrete{
|
|
||||||
Base: 0,
|
|
||||||
Segments: segments,
|
|
||||||
OriginCoordinate: Coordinate{},
|
|
||||||
})
|
|
||||||
assert.Equal(t, b, avg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAltitudeNoBase(t *testing.T) {
|
|
||||||
alt := altitude(0, RawDataSegment{
|
|
||||||
Pressure: rand.Float64(),
|
|
||||||
})
|
|
||||||
assert.Equal(t, alt, 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAltitudeBase(t *testing.T) {
|
|
||||||
alt := altitude(1012, RawDataSegment{
|
|
||||||
Pressure: 1010,
|
|
||||||
})
|
|
||||||
assert.Equal(t, alt, 25868.260058108597)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNormalizedPressure(t *testing.T) {
|
|
||||||
p := rand.Float64() * 1000
|
|
||||||
v := normalizedPressure(RawDataSegment{Pressure: p})
|
|
||||||
assert.Equal(t, v, p/100)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVelocity(t *testing.T) {
|
|
||||||
bp := 1012.0
|
|
||||||
segments, _ := makeDataSeries(bp)
|
|
||||||
val := (rand.Float64()*20 + 1000) * 100.0
|
|
||||||
seg := RawDataSegment{
|
|
||||||
Timestamp: float64(len(segments)),
|
|
||||||
Pressure: val,
|
|
||||||
}
|
|
||||||
vel := velocity(&FlightDataConcrete{
|
|
||||||
Base: 0,
|
|
||||||
Segments: segments,
|
|
||||||
OriginCoordinate: Coordinate{},
|
|
||||||
}, bp, seg)
|
|
||||||
vel1 := (altitude(bp, seg) - segments[len(segments)-1].Computed.Altitude) / (seg.Timestamp - segments[len(segments)-1].Raw.Timestamp)
|
|
||||||
assert.Equal(t, vel, vel1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestYaw(t *testing.T) {
|
|
||||||
val := yaw(RawDataSegment{
|
|
||||||
Acceleration: XYZ{
|
|
||||||
X: 100,
|
|
||||||
Y: 110,
|
|
||||||
Z: 120,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
assert.Equal(t, val, -39.80557109226519)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPitch(t *testing.T) {
|
|
||||||
val := pitch(RawDataSegment{
|
|
||||||
Acceleration: XYZ{
|
|
||||||
X: 100,
|
|
||||||
Y: 110,
|
|
||||||
Z: 120,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
assert.Equal(t, val, -42.51044707800084)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToDegrees(t *testing.T) {
|
|
||||||
val := toDegrees(math.Pi)
|
|
||||||
assert.Equal(t, val, 180.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToRadians(t *testing.T) {
|
|
||||||
val := toRadians(90)
|
|
||||||
assert.Equal(t, val, math.Pi/2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBearing(t *testing.T) {
|
|
||||||
origin := Coordinate{
|
|
||||||
38.811423646113546,
|
|
||||||
-77.054951464077,
|
|
||||||
}
|
|
||||||
seg := RawDataSegment{
|
|
||||||
Coordinate: Coordinate{
|
|
||||||
38,
|
|
||||||
-77,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
b := bearing(origin, seg)
|
|
||||||
assert.Equal(t, b, 179.9862686631269)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDistance(t *testing.T) {
|
|
||||||
origin := Coordinate{
|
|
||||||
38.811423646113546,
|
|
||||||
-77.054951464077,
|
|
||||||
}
|
|
||||||
seg := RawDataSegment{
|
|
||||||
Coordinate: Coordinate{
|
|
||||||
38,
|
|
||||||
-77,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
b := distance(origin, seg)
|
|
||||||
assert.Equal(t, b, 90353.15173806295)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDataRate(t *testing.T) {
|
|
||||||
segments, _ := makeDataSeries(0)
|
|
||||||
rate := dataRate(&FlightDataConcrete{
|
|
||||||
Segments: segments,
|
|
||||||
})
|
|
||||||
assert.Equal(t, rate, 1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestComputeDataSegment(t *testing.T) {
|
|
||||||
segments, avg := makeDataSeries(0)
|
|
||||||
segment, bp, origin := ComputeDataSegment(&FlightDataConcrete{
|
|
||||||
Segments: segments,
|
|
||||||
OriginCoordinate: Coordinate{37, -76},
|
|
||||||
}, RawDataSegment{
|
|
||||||
WriteProgress: 1.0,
|
|
||||||
Timestamp: float64(len(segments) + 1),
|
|
||||||
Pressure: 1014.0,
|
|
||||||
Temperature: 30.0,
|
|
||||||
Acceleration: XYZ{1, 2, 3},
|
|
||||||
Magnetic: XYZ{1, 2, 3},
|
|
||||||
Coordinate: Coordinate{38, -77},
|
|
||||||
GPSInfo: GPSInfo{0.0, 0.0},
|
|
||||||
Rssi: 0,
|
|
||||||
})
|
|
||||||
assert.Equal(t, bp, avg)
|
|
||||||
assert.NotEqual(t, origin.Lat, 0.0)
|
|
||||||
assert.NotEqual(t, origin.Lon, 0.0)
|
|
||||||
assert.NotEqual(t, segment.Altitude, 0.0)
|
|
||||||
assert.NotEqual(t, segment.Velocity, 0.0)
|
|
||||||
assert.NotEqual(t, segment.Yaw, 0.0)
|
|
||||||
assert.NotEqual(t, segment.Pitch, 0.0)
|
|
||||||
assert.NotEqual(t, segment.Bearing, 0.0)
|
|
||||||
assert.NotEqual(t, segment.Distance, 0.0)
|
|
||||||
assert.NotEqual(t, segment.DataRate, 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeDataSeries(bp float64) ([]DataSegment, float64) {
|
|
||||||
series := make([]DataSegment, 10)
|
|
||||||
total := 0.0
|
|
||||||
for i := 0; i < len(series); i++ {
|
|
||||||
val := rand.Float64()*20 + 1000
|
|
||||||
total += val
|
|
||||||
series[i] = DataSegment{
|
|
||||||
RawDataSegment{
|
|
||||||
Timestamp: float64(i),
|
|
||||||
Pressure: val * 100.0,
|
|
||||||
},
|
|
||||||
ComputedDataSegment{
|
|
||||||
Altitude: altitude(bp, RawDataSegment{
|
|
||||||
Pressure: val * 100.0,
|
|
||||||
}),
|
|
||||||
SmoothedPressure: val,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return series, total / float64(len(series))
|
|
||||||
}
|
|
@ -1,26 +1,25 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ModePrelaunch = "P"
|
ModeWaitingLaunch = "WT"
|
||||||
ModeAscentPowered = "AP"
|
ModeLaunch = "LD"
|
||||||
ModeAscentUnpowered = "AU"
|
ModeLowVelocity = "LV"
|
||||||
ModeDescentFreefall = "DF"
|
ModeNoseOver = "AP"
|
||||||
ModeDescentParachute = "DP"
|
ModeDrogueFired = "DR"
|
||||||
ModeRecovery = "R"
|
ModeMainFired = "MN"
|
||||||
|
ModeFailsafe = "FS"
|
||||||
|
ModeLanding = "TD"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IndexTimestamp = 0
|
IndexFlightTime = 0
|
||||||
IndexPressure = 1
|
IndexFightPhase = 1
|
||||||
IndexTemperature = 2
|
IndexAltitude = 2
|
||||||
IndexAccelerationX = 3
|
IndexVelocity = 3
|
||||||
IndexAccelerationY = 4
|
IndexAcceleration = 4
|
||||||
IndexAccelerationZ = 5
|
IndexTemperature = 5
|
||||||
IndexMagneticX = 6
|
IndexCoordinateLat = 6
|
||||||
IndexMagneticY = 7
|
IndexCoordinateLon = 7
|
||||||
IndexMagneticZ = 8
|
IndexGpsQuality = 8
|
||||||
IndexCoordinateLat = 9
|
IndexGpsSats = 9
|
||||||
IndexCoordinateLon = 10
|
|
||||||
IndexGpsQuality = 11
|
|
||||||
IndexGpsSats = 12
|
|
||||||
)
|
)
|
||||||
|
@ -62,7 +62,7 @@ func (f *FlightDataConcrete) GpsSats() []float64 {
|
|||||||
|
|
||||||
func (f *FlightDataConcrete) Time() []float64 {
|
func (f *FlightDataConcrete) Time() []float64 {
|
||||||
return singleFlightDataElement(f, func(segment DataSegment) float64 {
|
return singleFlightDataElement(f, func(segment DataSegment) float64 {
|
||||||
return segment.Raw.Timestamp
|
return 0.0
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,15 +19,15 @@ type XYZ struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RawDataSegment struct {
|
type RawDataSegment struct {
|
||||||
WriteProgress float64 `json:"writeProgress"`
|
FlightTime float64 `json:"flightTime"`
|
||||||
Timestamp float64 `json:"timestamp"`
|
FlightPhase float64 `json:"flightPhase"`
|
||||||
Pressure float64 `json:"pressure"`
|
Altitude float64 `json:"altitude"`
|
||||||
Temperature float64 `json:"temperature"`
|
Velocity float64 `json:"velocity"`
|
||||||
Acceleration XYZ `json:"acceleration"`
|
Acceleration float64 `json:"accelereation"`
|
||||||
Magnetic XYZ `json:"magnetic"`
|
Temperature float64 `json:"temperature"`
|
||||||
Coordinate Coordinate `json:"coordinate"`
|
Coordinate Coordinate `json:"coordinate"`
|
||||||
GPSInfo GPSInfo `json:"gpsInfo"`
|
GPSInfo GPSInfo `json:"gpsInfo"`
|
||||||
Rssi int16 `json:"rssi"`
|
Rssi int16 `json:"rssi"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ComputedDataSegment struct {
|
type ComputedDataSegment struct {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PointsPerDataFrame = 2
|
PointsPerDataFrame = 1
|
||||||
)
|
)
|
||||||
|
@ -37,18 +37,15 @@ func StartTextLogger(p DataProvider, ds core.FlightData, logger LoggerControl) e
|
|||||||
|
|
||||||
headers := []string{
|
headers := []string{
|
||||||
"Time",
|
"Time",
|
||||||
"Prog",
|
"Phase",
|
||||||
"Pressure",
|
"Altitude",
|
||||||
"Temp",
|
"Velocity",
|
||||||
"Accel X",
|
"Acceleration",
|
||||||
"Accel Y",
|
"Temperature",
|
||||||
"Accel Z",
|
"Latitude",
|
||||||
"Mag X",
|
"longitude",
|
||||||
"Mag Y",
|
"GPS Quality",
|
||||||
"Mag Z",
|
"GPS Sats",
|
||||||
"Lat",
|
|
||||||
"Lon",
|
|
||||||
"Sats",
|
|
||||||
"Qual",
|
"Qual",
|
||||||
"RSSI",
|
"RSSI",
|
||||||
}
|
}
|
||||||
@ -99,16 +96,12 @@ func StartTextLogger(p DataProvider, ds core.FlightData, logger LoggerControl) e
|
|||||||
j := len(data) - nRows + 1 + i
|
j := len(data) - nRows + 1 + i
|
||||||
seg := data[j]
|
seg := data[j]
|
||||||
rows[i+1] = []string{
|
rows[i+1] = []string{
|
||||||
fmt.Sprintf("%0.2f", seg.Raw.Timestamp),
|
fmt.Sprintf("%0.2f", seg.Raw.FlightTime),
|
||||||
fmt.Sprintf("%0.2f", seg.Raw.WriteProgress),
|
fmt.Sprintf("%0.2f", seg.Raw.FlightPhase),
|
||||||
fmt.Sprintf("%0.2f", seg.Raw.Pressure),
|
fmt.Sprintf("%0.2f", seg.Raw.Altitude),
|
||||||
|
fmt.Sprintf("%0.2f", seg.Raw.Velocity),
|
||||||
|
fmt.Sprintf("%0.2f", seg.Raw.Acceleration),
|
||||||
fmt.Sprintf("%0.2f", seg.Raw.Temperature),
|
fmt.Sprintf("%0.2f", seg.Raw.Temperature),
|
||||||
fmt.Sprintf("%0.2f", seg.Raw.Acceleration.X),
|
|
||||||
fmt.Sprintf("%0.2f", seg.Raw.Acceleration.Y),
|
|
||||||
fmt.Sprintf("%0.2f", seg.Raw.Acceleration.Z),
|
|
||||||
fmt.Sprintf("%0.2f", seg.Raw.Magnetic.X),
|
|
||||||
fmt.Sprintf("%0.2f", seg.Raw.Magnetic.Y),
|
|
||||||
fmt.Sprintf("%0.2f", seg.Raw.Magnetic.Z),
|
|
||||||
fmt.Sprintf("%0.2f", seg.Raw.Coordinate.Lat),
|
fmt.Sprintf("%0.2f", seg.Raw.Coordinate.Lat),
|
||||||
fmt.Sprintf("%0.2f", seg.Raw.Coordinate.Lon),
|
fmt.Sprintf("%0.2f", seg.Raw.Coordinate.Lon),
|
||||||
fmt.Sprintf("%0.2f", seg.Raw.GPSInfo.Sats),
|
fmt.Sprintf("%0.2f", seg.Raw.GPSInfo.Sats),
|
||||||
@ -229,5 +222,6 @@ func StartWeb(p DataProvider, ds core.FlightData, logger LoggerControl) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return http.ListenAndServe(":8080", nil)
|
return http.ListenAndServe(":8080", nil)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
const webSocket = new WebSocket(`ws://${window.location.host}/api/data`)
|
const webSocket = new WebSocket(`ws://${window.location.host}/api/data`)
|
||||||
webSocket.onmessage = (e) => {
|
webSocket.onmessage = (e) => {
|
||||||
data = data.concat(JSON.parse(e.data))
|
data = data.concat(JSON.parse(e.data))
|
||||||
|
console.log( "DATA :" + data)
|
||||||
dashboard.update(data)
|
dashboard.update(data)
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
const modeMap = {
|
const modeMap = {
|
||||||
'P': 'Prelaunch',
|
"WT": "Waiting for Launch",
|
||||||
'AP': 'Powered Ascent',
|
"LD": "Launch Detected",
|
||||||
'AU': 'Unpowered Ascent',
|
"LV": "Low velocity Detected",
|
||||||
'DF': 'Freefall Descent',
|
"AP": "Nose-Over",
|
||||||
'DP': 'Parachute Descent',
|
"DR": "Drogue Fired",
|
||||||
'R': 'Recovery'
|
"MN": "Main Fired",
|
||||||
|
"FS": "Failsafe Triggered ",
|
||||||
|
"TD": "Landing Detected"
|
||||||
}
|
}
|
||||||
|
|
||||||
class MissionInfoWidget extends Widget {
|
class MissionInfoWidget extends Widget {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
function makeXYExtractor(propType, propName) {
|
function makeXYExtractor(propType, propName) {
|
||||||
return (data) => data.map(segment => ({
|
return (data) => data.map(segment => ({
|
||||||
x: segment.raw.timestamp,
|
x: segment.raw.flightTime,
|
||||||
y: segment[propType][propName]
|
y: segment[propType][propName]
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -15,8 +15,7 @@ function makeCoordinateExtractor() {
|
|||||||
|
|
||||||
function makeInfoExtractor() {
|
function makeInfoExtractor() {
|
||||||
return (data) => ({
|
return (data) => ({
|
||||||
pcnt: data[data.length - 1].raw.cameraProgress,
|
time: data[data.length - 1].raw.flightTime,
|
||||||
time: data[data.length - 1].raw.timestamp,
|
|
||||||
mode: data[data.length - 1].computed.flightMode
|
mode: data[data.length - 1].computed.flightMode
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -33,11 +33,13 @@ func telemetryIntFromBytes(b []byte) int16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func decodeTelemetryBytes(bytes []byte) ([]byte, []byte, error) {
|
func decodeTelemetryBytes(bytes []byte) ([]byte, []byte, error) {
|
||||||
|
|
||||||
parts := strings.Split(string(bytes), ",")
|
parts := strings.Split(string(bytes), ",")
|
||||||
if len(parts) != 3 || parts[0] != "T" {
|
if len(parts) != 3 || parts[0] != "T" {
|
||||||
return nil, nil, errors.New("bad telemetry")
|
return nil, nil, errors.New(string(bytes))
|
||||||
}
|
}
|
||||||
telemetryBytes, err := base64.StdEncoding.DecodeString(parts[1])
|
telemetryBytes, err := base64.StdEncoding.DecodeString(parts[1])
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -58,22 +60,15 @@ func bytesToDataSegment(stream core.FlightData, bytes []byte) ([]core.DataSegmen
|
|||||||
var basePressure float64
|
var basePressure float64
|
||||||
var origin core.Coordinate
|
var origin core.Coordinate
|
||||||
for i := len(segments) - 1; i >= 0; i-- {
|
for i := len(segments) - 1; i >= 0; i-- {
|
||||||
offset := 1 + (i * 13)
|
offset := (i * 10)
|
||||||
raw := core.RawDataSegment{
|
raw := core.RawDataSegment{
|
||||||
WriteProgress: telemetryFloatFromByteIndex(telemetryBytes, 0),
|
FlightTime: telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexFlightTime),
|
||||||
Timestamp: telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexTimestamp),
|
FlightPhase: telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexFightPhase),
|
||||||
Pressure: telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexPressure),
|
Altitude: telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexAltitude),
|
||||||
Temperature: telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexTemperature),
|
Velocity: telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexVelocity),
|
||||||
Acceleration: core.XYZ{
|
Acceleration: telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexAcceleration),
|
||||||
telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexAccelerationX),
|
Temperature: telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexTemperature),
|
||||||
telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexAccelerationY),
|
|
||||||
telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexAccelerationZ),
|
|
||||||
},
|
|
||||||
Magnetic: core.XYZ{
|
|
||||||
telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexMagneticX),
|
|
||||||
telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexMagneticY),
|
|
||||||
telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexMagneticZ),
|
|
||||||
},
|
|
||||||
Coordinate: core.Coordinate{
|
Coordinate: core.Coordinate{
|
||||||
telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexCoordinateLat),
|
telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexCoordinateLat),
|
||||||
telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexCoordinateLon),
|
telemetryFloatFromByteIndex(telemetryBytes, offset+core.IndexCoordinateLon),
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTelemetryFloatFromByteIndex(t *testing.T) {
|
|
||||||
b64 := "AAAAAAAA8D8AAAAAAAAAQAAAAAAAAAhA"
|
|
||||||
data, _ := base64.StdEncoding.DecodeString(b64)
|
|
||||||
n := telemetryFloatFromByteIndex(data, 1)
|
|
||||||
assert.Equal(t, n, 2.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTelemetryIntFromBytes(t *testing.T) {
|
|
||||||
n := telemetryIntFromBytes([]byte{0x01, 0x00})
|
|
||||||
assert.Equal(t, n, int16(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeTelemetryBytes(t *testing.T) {
|
|
||||||
telemetryBytes, rssiBytes, err := decodeTelemetryBytes([]byte("T,wcqhRbbz3T8NAIDUU3JoQGzPymub5/dAAQAM3C/rIkCoKRPINnrovxg/EbSXB+Y/qdDM1YdeMMD+XHTRRRctQAAAAAAAgELAT6wPjfWhL0D2QZYFE2dDQCC4yhMIQ1PAAAAAAAAAAAAAAAAAAAAAAA==,//8="))
|
|
||||||
assert.NotNil(t, telemetryBytes)
|
|
||||||
assert.NotNil(t, rssiBytes)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
}
|
|
@ -21,7 +21,7 @@ func (c AltitudeChart) Generate(offsetSeconds float64, fd []core.DataSegment) er
|
|||||||
Series: []chart.Series{
|
Series: []chart.Series{
|
||||||
chart.ContinuousSeries{
|
chart.ContinuousSeries{
|
||||||
Name: "Altitude",
|
Name: "Altitude",
|
||||||
XValues: singleFlightDataElement(fd, func(d core.DataSegment) float64 { return d.Raw.Timestamp - offsetSeconds }),
|
XValues: singleFlightDataElement(fd, func(d core.DataSegment) float64 { return d.Raw.flightTime - offsetSeconds }),
|
||||||
YValues: singleFlightDataElement(fd, func(d core.DataSegment) float64 { return d.Computed.SmoothedAltitude }),
|
YValues: singleFlightDataElement(fd, func(d core.DataSegment) float64 { return d.Computed.SmoothedAltitude }),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -21,7 +21,7 @@ func (c VelocityChart) Generate(offsetSeconds float64, fd []core.DataSegment) er
|
|||||||
Series: []chart.Series{
|
Series: []chart.Series{
|
||||||
chart.ContinuousSeries{
|
chart.ContinuousSeries{
|
||||||
Name: "Velocity",
|
Name: "Velocity",
|
||||||
XValues: singleFlightDataElement(fd, func(d core.DataSegment) float64 { return d.Raw.Timestamp - offsetSeconds }),
|
XValues: singleFlightDataElement(fd, func(d core.DataSegment) float64 { return d.Raw.flightTime - offsetSeconds }),
|
||||||
YValues: singleFlightDataElement(fd, func(d core.DataSegment) float64 { return d.Computed.SmoothedVelocity }),
|
YValues: singleFlightDataElement(fd, func(d core.DataSegment) float64 { return d.Computed.SmoothedVelocity }),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -6,9 +6,9 @@ func timeInMode(fd []core.DataSegment, mode core.FlightMode) float64 {
|
|||||||
startTime := -1.0
|
startTime := -1.0
|
||||||
for _, d := range fd {
|
for _, d := range fd {
|
||||||
if d.Computed.FlightMode == mode && startTime < 0 {
|
if d.Computed.FlightMode == mode && startTime < 0 {
|
||||||
startTime = d.Raw.Timestamp
|
startTime = d.Raw.flightTime
|
||||||
} else if d.Computed.FlightMode != mode && startTime > 0 {
|
} else if d.Computed.FlightMode != mode && startTime > 0 {
|
||||||
return d.Raw.Timestamp - startTime
|
return d.Raw.flightTime - startTime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
|
@ -24,8 +24,8 @@ func flightDataFromFile(input string) ([]core.DataSegment, error) {
|
|||||||
|
|
||||||
func determineOffsetSeconds(ds []core.DataSegment) float64 {
|
func determineOffsetSeconds(ds []core.DataSegment) float64 {
|
||||||
for _, d := range ds {
|
for _, d := range ds {
|
||||||
if d.Computed.FlightMode == core.ModeAscentPowered {
|
if d.Computed.FlightMode == core.ModeLaunch {
|
||||||
return d.Raw.Timestamp
|
return d.Raw.flightTime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
|
@ -1,6 +1,26 @@
|
|||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <base64.hpp>
|
||||||
|
|
||||||
|
#define BIT(n) (1 << (n))
|
||||||
|
|
||||||
|
#define BIT_FLIGHT_TIME BIT(0)
|
||||||
|
#define BIT_ALTITUDE_100 BIT(1)
|
||||||
|
#define BIT_ALTITUDE BIT(2)
|
||||||
|
#define BIT_VELOCITY BIT(3)
|
||||||
|
#define BIT_ACCELERATION BIT(4)
|
||||||
|
#define BIT_FLIGHT_PHASE BIT(5)
|
||||||
|
#define BIT_CHANNEL BIT(6)
|
||||||
|
#define BIT_TEMPERATURE_10 BIT(7)
|
||||||
|
#define BIT_NAME BIT(8)
|
||||||
|
#define BIT_BATERY_10 BIT(9)
|
||||||
|
#define BIT_APOGEE BIT(10)
|
||||||
|
#define BIT_MAX_VELOCITY BIT(11)
|
||||||
|
#define BIT_MAX_ACCELERATION BIT(12)
|
||||||
|
#define ALL_ELEMENTS 0b1111111111111
|
||||||
|
|
||||||
#define TRIGGER_FLIGHT_TIME '#'
|
#define TRIGGER_FLIGHT_TIME '#'
|
||||||
#define TRIGGER_ALTITUDE_100 '{'
|
#define TRIGGER_ALTITUDE_100 '{'
|
||||||
@ -17,60 +37,25 @@
|
|||||||
#define TRIGGER_MAX_ACCELERATION '['
|
#define TRIGGER_MAX_ACCELERATION '['
|
||||||
#define TRIGGER_END_PACKET '>'
|
#define TRIGGER_END_PACKET '>'
|
||||||
|
|
||||||
typedef union EggtimerData
|
#define MAX_DATA_SIZE 100
|
||||||
{
|
|
||||||
int fight_time;
|
|
||||||
int altitude_100;
|
|
||||||
int altitude;
|
|
||||||
int velocity;
|
|
||||||
int acceleration_10;
|
|
||||||
int fight_phase;
|
|
||||||
char channel[6];
|
|
||||||
int temperature_10;
|
|
||||||
char name[9];
|
|
||||||
int battery_voltage_10;
|
|
||||||
int apogee;
|
|
||||||
int max_velocity;
|
|
||||||
int max_acceleration;
|
|
||||||
}EggtimerData;
|
|
||||||
|
|
||||||
typedef struct EggtimerElementPacket{
|
typedef struct WhiteVestsPacket{
|
||||||
|
double flight_time;
|
||||||
|
double flight_phase;
|
||||||
|
double altitude;
|
||||||
|
double velocity;
|
||||||
|
double acceleration;
|
||||||
|
double temperature;
|
||||||
|
double latitude;
|
||||||
|
double longitude;
|
||||||
|
double gpsSignal; // GPS Signal Strength
|
||||||
|
double gpsstat; // GPS Status
|
||||||
|
}WhiteVestsPacket;
|
||||||
|
|
||||||
|
typedef struct EggtimerElement{
|
||||||
char type;
|
char type;
|
||||||
EggtimerData data;
|
char data[MAX_DATA_SIZE];
|
||||||
} EggtimerElementPacket;
|
} EggtimerElement;
|
||||||
|
|
||||||
static char _last_state = 0;
|
|
||||||
static size_t counter = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief this function stores the data in the element packet
|
|
||||||
*/
|
|
||||||
void _save_data(char byte_received, EggtimerElementPacket *element){
|
|
||||||
switch (_last_state)
|
|
||||||
{
|
|
||||||
case TRIGGER_FLIGHT_TIME:
|
|
||||||
case TRIGGER_ALTITUDE_100:
|
|
||||||
case TRIGGER_ALTITUDE:
|
|
||||||
case TRIGGER_VELOCITY:
|
|
||||||
case TRIGGER_ACCELERATION_10:
|
|
||||||
case TRIGGER_FLIGHT_PHASE:
|
|
||||||
case TRIGGER_TEMPERATURE_10:
|
|
||||||
case TRIGGER_BATERY_10:
|
|
||||||
case TRIGGER_APOGEE:
|
|
||||||
case TRIGGER_MAX_VELOCITY:
|
|
||||||
case TRIGGER_MAX_ACCELERATION:
|
|
||||||
element->data.altitude *= 10;
|
|
||||||
element->data.altitude += byte_received - '0';
|
|
||||||
break;
|
|
||||||
case TRIGGER_CHANNEL:
|
|
||||||
case TRIGGER_NAME:
|
|
||||||
element->data.name[counter++] = byte_received;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This function is used to parse the data received from the Eggtimer
|
* @brief This function is used to parse the data received from the Eggtimer
|
||||||
@ -79,7 +64,9 @@ void _save_data(char byte_received, EggtimerElementPacket *element){
|
|||||||
*
|
*
|
||||||
* @return true if receive new data packet, false otherwise
|
* @return true if receive new data packet, false otherwise
|
||||||
*/
|
*/
|
||||||
bool decode_eggtimer_data(char byte_received, EggtimerElementPacket * packet){
|
bool decode_eggtimer_data(char byte_received, EggtimerElement * packet){
|
||||||
|
static size_t counter = 0;
|
||||||
|
static char last_state = 0;
|
||||||
|
|
||||||
switch (byte_received)
|
switch (byte_received)
|
||||||
{
|
{
|
||||||
@ -97,59 +84,242 @@ bool decode_eggtimer_data(char byte_received, EggtimerElementPacket * packet){
|
|||||||
case TRIGGER_MAX_VELOCITY:
|
case TRIGGER_MAX_VELOCITY:
|
||||||
case TRIGGER_MAX_ACCELERATION:
|
case TRIGGER_MAX_ACCELERATION:
|
||||||
|
|
||||||
_last_state = byte_received;
|
last_state = byte_received;
|
||||||
packet->type = byte_received;
|
packet->type = byte_received;
|
||||||
|
|
||||||
memset(&packet->data, 0, sizeof packet->data);
|
memset(&packet->data, 0, sizeof packet->data);
|
||||||
Serial.println( byte_received);
|
|
||||||
counter = 0;
|
counter = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TRIGGER_END_PACKET:
|
case TRIGGER_END_PACKET:
|
||||||
if (_last_state){
|
if (last_state){
|
||||||
packet->type = _last_state;
|
packet->type = last_state;
|
||||||
|
packet->data[counter] = '\0';
|
||||||
_last_state = 0;
|
last_state = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
_last_state = 0;
|
last_state = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_save_data(byte_received, packet);
|
// save data
|
||||||
|
if(last_state)
|
||||||
|
packet->data[counter++] = byte_received;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool test(){
|
bool buildPacket(EggtimerElement *eggt_packet, WhiteVestsPacket *wv_packet){
|
||||||
char str[] = "{004>@5>#0132>~1B---->(00023>\\000>%04679>^0660>[025>?079>!201>=KM6ZFL>";
|
static int16_t elements_received = 0;
|
||||||
char *p = str;
|
static WhiteVestsPacket nextPacket; // packet used when the packet is sent but it is not complete
|
||||||
EggtimerElementPacket packet;
|
static bool sentIncomplete = false;
|
||||||
for (; *p; p++)
|
|
||||||
{
|
|
||||||
if(decode_eggtimer_data(*p, &packet)){
|
|
||||||
Serial.print("Data packet: ");
|
|
||||||
if(packet.type == TRIGGER_NAME || packet.type == TRIGGER_CHANNEL){
|
|
||||||
Serial.println(packet.data.name);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
Serial.println(packet.data.altitude);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
if(sentIncomplete){
|
||||||
|
memcpy(wv_packet, &nextPacket, sizeof(WhiteVestsPacket));
|
||||||
|
sentIncomplete = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t element_bit = NULL;
|
||||||
|
double *element_pointer = NULL;
|
||||||
|
double *nextElement_pointer = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
// convert data to double
|
||||||
|
bool converted = true;
|
||||||
|
char *endptr;
|
||||||
|
double element = strtol((char*)&eggt_packet->data, &endptr, 10);
|
||||||
|
if(endptr == (char*)&eggt_packet->data|| *endptr != NULL){ // error
|
||||||
|
converted = false;
|
||||||
|
element = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (eggt_packet->type){
|
||||||
|
case TRIGGER_FLIGHT_TIME:
|
||||||
|
element_bit = BIT_FLIGHT_TIME;
|
||||||
|
element_pointer = &wv_packet->flight_time;
|
||||||
|
nextElement_pointer = &nextPacket.flight_time;
|
||||||
|
break;
|
||||||
|
case TRIGGER_ALTITUDE_100:
|
||||||
|
element_bit = BIT_ALTITUDE_100;
|
||||||
|
element_pointer = &wv_packet->altitude;
|
||||||
|
nextElement_pointer = &nextPacket.altitude;
|
||||||
|
element *= 100.0;
|
||||||
|
break;
|
||||||
|
case TRIGGER_ALTITUDE:
|
||||||
|
element_bit = BIT_ALTITUDE;
|
||||||
|
element_pointer = &wv_packet->altitude;
|
||||||
|
nextElement_pointer = &nextPacket.altitude;
|
||||||
|
break;
|
||||||
|
case TRIGGER_VELOCITY:
|
||||||
|
element_bit = BIT_VELOCITY;
|
||||||
|
element_pointer = &wv_packet->velocity;
|
||||||
|
nextElement_pointer = &nextPacket.velocity;
|
||||||
|
break;
|
||||||
|
case TRIGGER_ACCELERATION_10:
|
||||||
|
element_bit = BIT_ACCELERATION;
|
||||||
|
element_pointer = &wv_packet->acceleration;
|
||||||
|
nextElement_pointer = &nextPacket.acceleration;
|
||||||
|
|
||||||
|
element /= 10;
|
||||||
|
break;
|
||||||
|
case TRIGGER_FLIGHT_PHASE:
|
||||||
|
element_bit = BIT_FLIGHT_PHASE;
|
||||||
|
element_pointer = &wv_packet->flight_phase;
|
||||||
|
nextElement_pointer = &nextPacket.flight_phase;
|
||||||
|
break;
|
||||||
|
case TRIGGER_TEMPERATURE_10:
|
||||||
|
element_bit = BIT_TEMPERATURE_10;
|
||||||
|
element_pointer = &wv_packet->temperature;
|
||||||
|
nextElement_pointer = &nextPacket.temperature;
|
||||||
|
element /= 10;
|
||||||
|
break;
|
||||||
|
case TRIGGER_BATERY_10:
|
||||||
|
element_bit = BIT_BATERY_10;
|
||||||
|
break;
|
||||||
|
case TRIGGER_APOGEE:
|
||||||
|
element_bit = BIT_APOGEE;
|
||||||
|
break;
|
||||||
|
case TRIGGER_MAX_VELOCITY:
|
||||||
|
element_bit = BIT_MAX_VELOCITY;
|
||||||
|
break;
|
||||||
|
case TRIGGER_MAX_ACCELERATION:
|
||||||
|
element_bit = BIT_MAX_ACCELERATION;
|
||||||
|
break;
|
||||||
|
case TRIGGER_CHANNEL:
|
||||||
|
element_bit = BIT_CHANNEL;
|
||||||
|
break;
|
||||||
|
case TRIGGER_NAME:
|
||||||
|
element_bit = BIT_NAME;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the data received is from current packet, if not, it will be stored in the next packet
|
||||||
|
if((elements_received & element_bit) && element_pointer != NULL && nextElement_pointer != NULL && converted){
|
||||||
|
memcpy(&nextPacket, wv_packet, sizeof(WhiteVestsPacket));
|
||||||
|
*nextElement_pointer = element; // stores element in nextPacket
|
||||||
|
elements_received = element_bit;
|
||||||
|
sentIncomplete = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( element_pointer != NULL){
|
||||||
|
if(converted)
|
||||||
|
*element_pointer = element;
|
||||||
|
else
|
||||||
|
return false; // error converting data
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
elements_received |= element_bit;
|
||||||
|
|
||||||
|
// check if the packet is complete
|
||||||
|
if(elements_received == ALL_ELEMENTS){
|
||||||
|
elements_received = 0;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function send the packet to the server
|
||||||
|
*/
|
||||||
|
void sendPacket(WhiteVestsPacket * packet){
|
||||||
|
unsigned char data_base64[255];
|
||||||
|
encode_base64((unsigned char*)packet, sizeof(WhiteVestsPacket), data_base64);
|
||||||
|
|
||||||
|
char rssi_base64[] = "//8=";
|
||||||
|
|
||||||
|
Serial.print("T,");
|
||||||
|
Serial.print((char *) data_base64);
|
||||||
|
Serial.print(",");
|
||||||
|
Serial.print((char *) rssi_base64);
|
||||||
|
Serial.print("\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test(){
|
||||||
|
char str[] ="{000>@1>#0000>~AB---->?079>!211>=KM6ZFL>"
|
||||||
|
"{000>@1>#0000>~AB---->?079>!211>=KM6ZFL>"
|
||||||
|
"{040>@2>#0010>~AB---->(0213>\\-001>?079>!212>=KM6ZFL>01>?079>!212>=KM6ZFL>"
|
||||||
|
"{045>@2>#0014>~AB---->(0072>\\-001>?079>!212>=KM6ZFL>"
|
||||||
|
"{046>@4>#0016>~AB---->(0033>\\-001>?079>!212>=KM6ZFL>"
|
||||||
|
"{046>@5>#0018>~1B---->(2787>\\000>%04679>^0660>[025>?079>!210>=KM6ZFL> "
|
||||||
|
"{045>@5>#0020>~1B---->(-0354>\\0=KM6ZFL>"
|
||||||
|
"{043>@5>#0022>~1B---->(-0029>\\004>%04679>^0660>[025>?079>!209>=KM6ZFL>"
|
||||||
|
"{043>@5>#0024>~1B---->(-0023>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
|
||||||
|
"{042>@5>#0026>~1B---->(-0028>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
|
||||||
|
"{042>@5>#0028>~1B---->(-0035>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
|
||||||
|
"{041>@5>#0030>~1B---->(-0029>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
|
||||||
|
"{040>@5>#0032>~1B---->(-0032>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
|
||||||
|
"{040>@5>#0034>~1B---->(-0021>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
|
||||||
|
"{039>@5>#0036>~1B---->(-0028>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
|
||||||
|
"{038>@5>#0038>~1B---->(-0028>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
|
||||||
|
"{038>@5>#0040>~1B---->(-0031>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
|
||||||
|
"{037>@5>#0042>~1B---->(-0043>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
|
||||||
|
"{037>@5>#0044>~1B---->(-0040>\\000>%04679>^0660>[025>?079>!209>=KM6ZFL>"
|
||||||
|
"{036>@5>#0046>~1B---->(-0022>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
|
||||||
|
"{035>@5>#0048>~1B---->(-0038>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
|
||||||
|
"{034>@5>#0050>~1B---->(-0028>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
|
||||||
|
"{034>@5>#0052>~1B---->(-0043>\\0=KM6ZFL>"
|
||||||
|
"{033>@5>#0054>~1B---->(-0033>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
|
||||||
|
"{032>@5>#0056>~1B---->(-0041>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
|
||||||
|
"{032>@5>#0058>~1B---->(-0026>\\000>%04679>^0660>[025>?079>!207>=KM6ZFL>"
|
||||||
|
"{031>@5>#0060>~1B---->(-0035>\\000>%04679>^0660>[025>?079>!208>=KM6ZFL>"
|
||||||
|
"{030>@5>#0062>~1B---->(-0037>\\000>%04679>^0660>[025>?079>!207>=KM6ZFL>"
|
||||||
|
"{029>@5>#0064>~1B---->(-0032>\\000>%04679>^0660>[025>?079>!207>=KM6ZFL>"
|
||||||
|
"{029>@5>#0066>~1B---->(-0035>\\000>%04679>^0660>[025>?079>!207>=KM6ZFL>"
|
||||||
|
"{028>@5>#0068>~1B---->(-0044>\\000>%04679>^0660>[025>?079>!207>=KM6ZFL>"
|
||||||
|
"{027>@5>#0070>~1B---->(-0045>\\000>%04679>^0660>[025>?079>!207>=KM6ZFL>"
|
||||||
|
"{026>@5>#0072>~1B---->(-0035>\\000>%04679>^0660>[025>?079>!206>=KM6ZFL>"
|
||||||
|
"{025>@5>#0074>~1B---->(-0045>\\000>%04679>^0660>[025>?079>!206>=KM6ZFL>"
|
||||||
|
"{025>@5>#0076>~1B---->(-0043>\\000>%04679>^0660>[025>?079>!206>=KM6ZFL>"
|
||||||
|
"{024>@5>#0078>~1B---->(-0046>\\000>%04679>^0660>[025>?079>!206>=KM6ZFL>"
|
||||||
|
"{023>@5>#0080>~1B---->(-0046>\\000>%04679>^0660>[025>?079>!206>=KM6ZFL>"
|
||||||
|
"{022>@5>#0082>~1B---->(-0037>\\000>%04679>^0660>[025>?079>!206>=KM6ZFL>"
|
||||||
|
"{021>@5>#0084>~1B---->(-0039>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
|
||||||
|
"{020>@5>#0086>~1B---->(-0043>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
|
||||||
|
"{020>@5>#0088>~1B---->(-0046>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
|
||||||
|
"{019>@5>#0090>~1B---->(-0037>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
|
||||||
|
"{018>@5>#0092>~1B---->(-0046>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
|
||||||
|
"{017>@5>#0094>~1B---->(-0035>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
|
||||||
|
"{016>@5>#0096>~1B---->(-0046>\\000>%04679>^0660>[025>?079>!205>=KM6ZFL>"
|
||||||
|
"{016>@5>#0098>~1B---->(-0032>\\000>%04679>^0660>[025>?079>!204>=KM6ZFL>"
|
||||||
|
"{015>@5>#0100>~1B---->(-0043>\\000>%04679>^0660>[025>?079>!204>=KM6ZFL>"
|
||||||
|
"{014>@5>#0102>~1B---->(-0037>\\000>%04679>^0660>[025>?079>!204>=KM6ZFL>"
|
||||||
|
"{013>@5>#0104>~1B---->(-0033>\\000>%04679>^0660>[025>?079>!204>=KM6ZFL>"
|
||||||
|
"{012>@5>#0106>~1B---->(-0026>\\000>%04679>^0660>[025>?079>!204>=KM6ZFL>"
|
||||||
|
"{012>@5>#0108>~1B---->(-0029>\\000>%04679>^0660>[025>?079>!204>=KM6ZFL>"
|
||||||
|
"{011>@5>#0110>~1B---->(-0042>\\000>%04679>^0660>[025>?079>!203>=KM6ZFL>"
|
||||||
|
"{010>@5>#0112>~1B---->(-0041>\\000>%04679>^0660>[025>?079>!203>=KM6ZFL>"
|
||||||
|
"{009>@5>#0114>~1B---->(-0037>\\000>%04679>^0660>[025>?079>!203>=KM6ZFL>"
|
||||||
|
"{009>@5>#0116>~1B---->(-0040>\\000>%04679>^0660>[025>?079>!203>=KM6ZFL>"
|
||||||
|
"{008>@5>#0118>~1B---->(-0043>\\000>%04679>^0660>[025>?079>!202>=KM6ZFL>";
|
||||||
|
|
||||||
|
char *p = str;
|
||||||
|
EggtimerElement eggt_packet;
|
||||||
|
WhiteVestsPacket wv_packet;
|
||||||
|
memset(&wv_packet, 0, sizeof(WhiteVestsPacket));
|
||||||
|
for (; *p; p++){
|
||||||
|
if(decode_eggtimer_data(*p, &eggt_packet) && buildPacket(&eggt_packet, &wv_packet)){
|
||||||
|
sendPacket(&wv_packet);
|
||||||
|
delay(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
|
Serial.flush();
|
||||||
while (!Serial);
|
while (!Serial);
|
||||||
|
Serial.println();
|
||||||
Serial.println("Ready!");
|
|
||||||
|
|
||||||
test();
|
test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user