Skip to content

Commit

Permalink
dasbhoard updates
Browse files Browse the repository at this point in the history
  • Loading branch information
johnjones4 committed Sep 9, 2021
1 parent be4b8e9 commit fceaa41
Show file tree
Hide file tree
Showing 31 changed files with 1,203 additions and 339 deletions.
2 changes: 1 addition & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ $ make install
$ make build
```

Then, run the dashboard using the following `build/dashboard-Darwin-i386 /dev/cu.usbmodem143101`. Note that `dashboard-Darwin-i386` will change based on the system you are using and `/dev/cu.usbmodem143101` is the path to the Arduino serial connection.
Then, run the dashboard using the following `build/dashboard-Darwin-i386 --input /dev/cu.usbmodem143101 --output text`. Note that `dashboard-Darwin-i386` will change based on the system you are using and `/dev/cu.usbmodem143101` is the path to the Arduino serial connection. To view the web dashboard, pass in `web` for the `--output` option and open [http://localhost:8080/](http://localhost:8080/).

### Air

Expand Down
2 changes: 1 addition & 1 deletion air/whitevest/bin/air.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def main():
start_time = time.time()

# Thread safe place to store altitude reading
current_readings = AtomicBuffer(2)
current_readings = AtomicBuffer(50)

# Holds the most recent GPS data
gps_value = AtomicValue((0.0, 0.0, 0.0, 0.0))
Expand Down
4 changes: 2 additions & 2 deletions air/whitevest/bin/test_sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
transmit_latest_readings,
)

TEST_TIME_LENGTH = 5
TEST_TIME_LENGTH = 30


def main():
Expand Down Expand Up @@ -134,7 +134,7 @@ def test_gps(configuration: Configuration):
gps_value = AtomicValue()
start_time = time.time()
readings = 0
while readings == 0 or time.time() - start_time < TEST_TIME_LENGTH:
while readings < 10 or time.time() - start_time < TEST_TIME_LENGTH:
try:
if take_gps_reading(gps, gps_value):
readings += 1
Expand Down
6 changes: 6 additions & 0 deletions air/whitevest/lib/atomic_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ def read(self):
for i, _ in enumerate(self.buffer):
output[i] = self.buffer[(i + self.pointer) % len(self.buffer)]
return output

def clear(self):
"""Empty all of the data in buffer"""
with self.lock:
self.buffer = [self.default_value] * len(self.buffer)
self.pointer = 0
58 changes: 42 additions & 16 deletions air/whitevest/lib/hardware.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@
from whitevest.lib.atomic_value import AtomicValue
from whitevest.lib.configuration import Configuration

class DummyBMP:
"""Dummy class for the BMP3xx sensor"""
def _read(self):
"""Return dummy data"""
return (0.0, 0.0)

class DummyMag:
"""Dummy class for the Magnetometer sensor"""
def __init__(self):
self.magnetic = (0.0, 0.0, 0.0)
class DummyAccel:
"""Dummy class for the Magnetometer sensor"""
def __init__(self):
self.acceleration = (0.0, 0.0, 0.0)


def init_radio(configuration: Configuration):
"""Initialize the radio"""
Expand All @@ -35,26 +50,37 @@ def init_radio(configuration: Configuration):

def init_altimeter(configuration: Configuration):
"""Initialize the sensor for pressure, temperature, and altitude"""
logging.info("Initializing altimeter")
assignments = configuration.get_pin_assignments("bmp3xx")
if not assignments:
return None
i2c = busio.I2C(assignments.get("scl"), assignments.get("sda"))
bmp = adafruit_bmp3xx.BMP3XX_I2C(i2c)
bmp._wait_time = 0 # pylint: disable=protected-access
return bmp
try:
logging.info("Initializing altimeter")
assignments = configuration.get_pin_assignments("bmp3xx")
if not assignments:
return None
i2c = busio.I2C(assignments.get("scl"), assignments.get("sda"))
bmp = adafruit_bmp3xx.BMP3XX_I2C(i2c)
bmp._wait_time = 0 # pylint: disable=protected-access
return bmp
except Exception as ex: # pylint: disable=broad-except
bmp = DummyBMP()
logging.exception(ex)
return bmp


def init_magnetometer_accelerometer(configuration: Configuration):
"""Initialize the sensor for magnetic and acceleration"""
logging.info("Initializing magnetometer/accelerometer")
assignments = configuration.get_pin_assignments("lsm303")
if not assignments:
return None, None
i2c = busio.I2C(assignments.get("scl"), assignments.get("sda"))
mag = adafruit_lsm303dlh_mag.LSM303DLH_Mag(i2c)
accel = adafruit_lsm303_accel.LSM303_Accel(i2c)
return mag, accel
try:
logging.info("Initializing magnetometer/accelerometer")
assignments = configuration.get_pin_assignments("lsm303")
if not assignments:
return None, None
i2c = busio.I2C(assignments.get("scl"), assignments.get("sda"))
mag = adafruit_lsm303dlh_mag.LSM303DLH_Mag(i2c)
accel = adafruit_lsm303_accel.LSM303_Accel(i2c)
return mag, accel
except Exception as ex: # pylint: disable=broad-except
mag = DummyMag()
accel = DummyAccel()
logging.exception(ex)
return mag, accel


def init_gps(configuration: Configuration):
Expand Down
57 changes: 30 additions & 27 deletions air/whitevest/lib/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Functions shared between air and ground runtimes"""
import logging
import math
import struct
import time
from queue import Queue
Expand Down Expand Up @@ -38,16 +39,14 @@ def write_queue_log(outfile, new_data_queue: Queue, max_lines: int = 1000) -> in
def take_gps_reading(sio, gps_value: AtomicValue) -> bool:
"""Grab the most recent data from GPS feed"""
line = sio.readline()
if line[0:6] == "$GPGGA":
gps = pynmea2.parse(line)
gps_value.update(
(
gps.latitude if gps else 0.0,
gps.longitude if gps else 0.0,
float(gps.gps_qual) if gps else 0.0,
float(gps.num_sats) if gps else 0.0,
)
)
gps = pynmea2.parse(line)
if isinstance(gps, pynmea2.types.talker.GGA):
gps_value.update((
gps.latitude if gps else 0.0,
gps.longitude if gps else 0.0,
float(gps.gps_qual) if gps else 0.0,
float(gps.num_sats) if gps else 0.0,
))
return True
return False

Expand Down Expand Up @@ -141,22 +140,26 @@ def transmit_latest_readings(
) -> Tuple[int, float]:
"""Get the latest value from the sensor store and transmit it as a byte array"""
infos = current_readings.read()
info = []
for i in infos:
info += i
if info:
clean_info = [float(i) for i in info]
encoded = struct.pack(
"d" + TELEMETRY_STRUCT_STRING + TELEMETRY_STRUCT_STRING,
*(pcnt_to_limit.get_value(), *clean_info)
if len(infos) < 2:
return readings_sent, last_check
info1 = infos[0]
info2 = infos[int(math.ceil(len(infos) / 2))]
if not info1 or not info2:
return readings_sent, last_check
info = (*info1, *info2)
clean_info = [float(i) for i in info]
encoded = struct.pack(
"d" + TELEMETRY_STRUCT_STRING + TELEMETRY_STRUCT_STRING,
*(pcnt_to_limit.get_value(), *clean_info)
)
current_readings.clear()
logging.debug("Transmitting %d bytes", len(encoded))
rfm9x.send(encoded)
readings_sent += 1
if last_check > 0 and last_check + 10.0 < time.time():
last_check = time.time()
logging.info(
"Transmit rate: %f/s",
float(readings_sent) / float(last_check - start_time),
)
logging.debug("Transmitting %d bytes", len(encoded))
rfm9x.send(encoded)
readings_sent += 1
if last_check > 0 and last_check + 10.0 < time.time():
last_check = time.time()
logging.info(
"Transmit rate: %f/s",
float(readings_sent) / float(last_check - start_time),
)
return readings_sent, last_check
99 changes: 86 additions & 13 deletions dashboard/dashboard/computed.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
func basePressure(stream FlightData) float64 {
pressures := make([]float64, 0)
for _, segment := range stream.AllSegments() {
if segment.Computed.NormalizedPressure > 0 {
pressures = append(pressures, segment.Computed.NormalizedPressure)
if segment.Computed.SmoothedPressure > 0 {
pressures = append(pressures, segment.Computed.SmoothedPressure)
}
if len(pressures) >= 10 {
var sum float64 = 0
Expand Down Expand Up @@ -104,7 +104,53 @@ func dataRate(stream FlightData) float64 {
for _, secondTotal := range totalsMap {
total += secondTotal
}
return total / float64(len(totalsMap))
rate := total / float64(len(totalsMap))
if math.IsNaN(rate) {
return 0
}
return rate
}

func averageComputedValue(seconds float64, stream FlightData, raw RawDataSegment, computed ComputedDataSegment, accessor func(seg ComputedDataSegment) float64) float64 {
total := accessor(computed)
n := 1.0
i := len(stream.AllSegments()) - 1
for i >= 0 && raw.Timestamp-stream.Time()[i] <= seconds {
total += accessor(stream.AllSegments()[i].Computed)
n++
i--
}
return total / n
}

func determineFlightMode(stream FlightData, raw RawDataSegment, computed ComputedDataSegment) FlightMode {
length := len(stream.AllSegments())
if length == 0 {
return ModePrelaunch
}
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 > 1 {
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(avgVelocity) < 0.5 {
return ModeRecovery
}
return lastMode
}

func computeDataSegment(stream FlightData, raw RawDataSegment) (ComputedDataSegment, float64, Coordinate) {
Expand All @@ -118,14 +164,41 @@ func computeDataSegment(stream FlightData, raw RawDataSegment) (ComputedDataSegm
origin = raw.Coordinate
}

return ComputedDataSegment{
Altitude: altitude(bp, raw),
Velocity: velocity(stream, bp, raw),
Yaw: yaw(raw),
Pitch: pitch(raw),
NormalizedPressure: normalizedPressure(raw),
Bearing: bearing(origin, raw),
Distance: distance(origin, raw),
DataRate: dataRate(stream),
}, bp, origin
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{
Altitude: alt,
Velocity: vel,
Yaw: yaw(raw),
Pitch: pitch(raw),
Bearing: bearing(origin, raw),
Distance: distance(origin, raw),
DataRate: dataRate(stream),
SmoothedAltitude: smoothedAlt,
SmoothedVelocity: smoothedVel,
SmoothedPressure: smoothedPress,
SmoothedTemperature: smoothedTemp,
SmoothedVerticalAcceleration: smoothedVertAccel,
}

computed.FlightMode = determineFlightMode(stream, raw, computed)

return computed, bp, origin
}
20 changes: 9 additions & 11 deletions dashboard/dashboard/computed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,15 @@ func TestComputeDataSegment(t *testing.T) {
Segments: segments,
OriginCoordinate: Coordinate{37, -76},
}, RawDataSegment{
CameraProgress: 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,
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)
Expand All @@ -148,7 +148,6 @@ func TestComputeDataSegment(t *testing.T) {
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.NormalizedPressure, 0.0)
assert.NotEqual(t, segment.Bearing, 0.0)
assert.NotEqual(t, segment.Distance, 0.0)
assert.NotEqual(t, segment.DataRate, 0.0)
Expand All @@ -166,7 +165,6 @@ func makeDataSeries(bp float64) ([]DataSegment, float64) {
Pressure: val * 100.0,
},
ComputedDataSegment{
NormalizedPressure: val,
Altitude: altitude(bp, RawDataSegment{
Pressure: val * 100.0,
}),
Expand Down
9 changes: 9 additions & 0 deletions dashboard/dashboard/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ const (
const (
PointsPerDataFrame = 2
)

const (
ModePrelaunch = "P"
ModeAscentPowered = "AP"
ModeAscentUnpowered = "AU"
ModeDescentFreefall = "DF"
ModeDescentParachute = "DP"
ModeRecovery = "R"
)
2 changes: 1 addition & 1 deletion dashboard/dashboard/data_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (f DataProviderFile) Stream() <-chan []byte {
go func() {
lastLine := 0
for {
time.Sleep(time.Second / 30)
time.Sleep(time.Second)
if lastLine >= len(f.Bytes) {
return
}
Expand Down
Loading

0 comments on commit fceaa41

Please sign in to comment.