Incorporate dump1090-mutability. UI signal strength and traffic improvements.

pull/292/head
AvSquirrel 2016-02-25 05:53:35 +00:00
rodzic 27dc75ca0e
commit 5d082e3292
11 zmienionych plików z 203 dodań i 171 usunięć

@ -1 +1 @@
Subproject commit b360db74b99a408cb83833af802042d29cccffd9 Subproject commit 7eca082524fa135eb8efd78780f25739efb6a82c

Wyświetl plik

@ -131,7 +131,7 @@ void make_atan2_table(void) {
double scaled_ang = round(32768 * ang / M_PI); double scaled_ang = round(32768 * ang / M_PI);
double amp = sqrt(d_i * d_i + d_q * d_q); double amp = sqrt(d_i * d_i + d_q * d_q);
uint16_t scaled_amp = amp * 1000.0 / 127.5 + .5; uint16_t scaled_amp = amp * 1000.0 / 127.5;
u.iq[0] = i; u.iq[0] = i;
u.iq[1] = q; u.iq[1] = q;
@ -150,13 +150,13 @@ static void convert_to_phi(uint16_t *dest, uint16_t *src, int n) {
dest[i] = iqphase[src[i]]; dest[i] = iqphase[src[i]];
} }
static void calc_power(uint16_t *samples, int len) { static void calc_power(uint16_t *samples, int len) { // sets signal_strength to scaled amplitude. 0 = no signal, 1000 = saturated receiver on all samples in measurement.
long avg = 0; long avg = 0;
int n = len; int n = len;
while (n--) { while (n--) {
avg += iqamplitude[*samples++]; avg += iqamplitude[*samples++];
} }
signal_strength = (avg + len / 2) / len; signal_strength = avg / len;
} }
#ifndef BUILD_LIB #ifndef BUILD_LIB

Wyświetl plik

@ -20,6 +20,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
"math"
"os" "os"
"os/signal" "os/signal"
"runtime" "runtime"
@ -108,12 +109,13 @@ var dump1090ReplayWriter WriteCloser
var developerMode bool var developerMode bool
type msg struct { type msg struct {
MessageClass uint MessageClass uint
TimeReceived time.Time TimeReceived time.Time
Data []byte Data []byte
Products []uint32 Products []uint32
Signal_strength int Signal_amplitude int
ADSBTowerID string // Index in the 'ADSBTowers' map, if this is a parseable uplink message. Signal_strength float64
ADSBTowerID string // Index in the 'ADSBTowers' map, if this is a parseable uplink message.
} }
// Raw inputs. // Raw inputs.
@ -125,9 +127,10 @@ var timeStarted time.Time
type ADSBTower struct { type ADSBTower struct {
Lat float64 Lat float64
Lng float64 Lng float64
Signal_strength_last_minute int Signal_strength_now float64 // Current RSSI (dB)
signal_power_last_minute int64 // Over total messages. Signal_strength_max float64 // all-time peak RSSI (dB) observed for this tower
Signal_strength_max int Energy_last_minute uint64 // Summation of power observed for this tower across all messages last minute
Signal_strength_last_minute float64 // Average RSSI (dB) observed for this tower last minute
Messages_last_minute uint64 Messages_last_minute uint64
Messages_total uint64 Messages_total uint64
} }
@ -594,7 +597,7 @@ func relayMessage(msgtype uint16, msg []byte) {
func heartBeatSender() { func heartBeatSender() {
timer := time.NewTicker(1 * time.Second) timer := time.NewTicker(1 * time.Second)
timerMessageStats := time.NewTicker(5 * time.Second) timerMessageStats := time.NewTicker(2 * time.Second)
for { for {
select { select {
case <-timer.C: case <-timer.C:
@ -620,7 +623,7 @@ func heartBeatSender() {
} }
*/ */
// ---end traffic demo code --- // ---end traffic demo code ---
sendTrafficUpdates() sendTrafficUpdates()
updateStatus() updateStatus()
@ -641,7 +644,7 @@ func updateMessageStats() {
// Clear out ADSBTowers stats. // Clear out ADSBTowers stats.
for t, tinf := range ADSBTowers { for t, tinf := range ADSBTowers {
tinf.Messages_last_minute = 0 tinf.Messages_last_minute = 0
tinf.Signal_strength_last_minute = 0 tinf.Energy_last_minute = 0
ADSBTowers[t] = tinf ADSBTowers[t] = tinf
} }
@ -656,8 +659,8 @@ func updateMessageStats() {
if len(MsgLog[i].ADSBTowerID) > 0 { // Update tower stats. if len(MsgLog[i].ADSBTowerID) > 0 { // Update tower stats.
tid := MsgLog[i].ADSBTowerID tid := MsgLog[i].ADSBTowerID
twr := ADSBTowers[tid] twr := ADSBTowers[tid]
twr.Energy_last_minute += uint64((MsgLog[i].Signal_amplitude) * (MsgLog[i].Signal_amplitude))
twr.Messages_last_minute++ twr.Messages_last_minute++
twr.signal_power_last_minute += int64(MsgLog[i].Signal_strength)
if MsgLog[i].Signal_strength > twr.Signal_strength_max { // Update alltime max signal strength. if MsgLog[i].Signal_strength > twr.Signal_strength_max { // Update alltime max signal strength.
twr.Signal_strength_max = MsgLog[i].Signal_strength twr.Signal_strength_max = MsgLog[i].Signal_strength
} }
@ -684,9 +687,9 @@ func updateMessageStats() {
// Update average signal strength over last minute for all ADSB towers. // Update average signal strength over last minute for all ADSB towers.
for t, tinf := range ADSBTowers { for t, tinf := range ADSBTowers {
if tinf.Messages_last_minute == 0 { if tinf.Messages_last_minute == 0 {
tinf.Signal_strength_last_minute = 0 tinf.Signal_strength_last_minute = -99
} else { } else {
tinf.Signal_strength_last_minute = int(tinf.signal_power_last_minute / int64(tinf.Messages_last_minute)) tinf.Signal_strength_last_minute = 10 * (math.Log10(float64((tinf.Energy_last_minute / tinf.Messages_last_minute))) - 6)
} }
ADSBTowers[t] = tinf ADSBTowers[t] = tinf
} }
@ -868,10 +871,9 @@ func parseInput(buf string) ([]byte, uint16) {
} }
if s[0] == '-' { if s[0] == '-' {
parseDownlinkReport(s, int32(thisSignalStrength)) parseDownlinkReport(s, int(thisSignalStrength))
} }
s = s[1:] s = s[1:]
msglen := len(s) / 2 msglen := len(s) / 2
@ -901,7 +903,8 @@ func parseInput(buf string) ([]byte, uint16) {
thisMsg.MessageClass = MSGCLASS_UAT thisMsg.MessageClass = MSGCLASS_UAT
thisMsg.TimeReceived = stratuxClock.Time thisMsg.TimeReceived = stratuxClock.Time
thisMsg.Data = frame thisMsg.Data = frame
thisMsg.Signal_strength = thisSignalStrength thisMsg.Signal_amplitude = thisSignalStrength
thisMsg.Signal_strength = 10 * (math.Log10(float64((thisSignalStrength * thisSignalStrength))) - 6)
thisMsg.Products = make([]uint32, 0) thisMsg.Products = make([]uint32, 0)
if msgtype == MSGTYPE_UPLINK { if msgtype == MSGTYPE_UPLINK {
// Parse the UAT message. // Parse the UAT message.
@ -914,10 +917,13 @@ func parseInput(buf string) ([]byte, uint16) {
var newTower ADSBTower var newTower ADSBTower
newTower.Lat = uatMsg.Lat newTower.Lat = uatMsg.Lat
newTower.Lng = uatMsg.Lon newTower.Lng = uatMsg.Lon
newTower.Signal_strength_now = thisMsg.Signal_strength
newTower.Signal_strength_max = -999 // dBmax = 0, so this needs to initialize below scale ( << -48 dB)
ADSBTowers[towerid] = newTower ADSBTowers[towerid] = newTower
} }
twr := ADSBTowers[towerid] twr := ADSBTowers[towerid]
twr.Messages_total++ twr.Messages_total++
twr.Signal_strength_now = thisMsg.Signal_strength
ADSBTowers[towerid] = twr ADSBTowers[towerid] = twr
// Get all of the "product ids". // Get all of the "product ids".
for _, f := range uatMsg.Frames { for _, f := range uatMsg.Frames {

Wyświetl plik

@ -294,8 +294,10 @@ func messageQueueSender() {
averageSendableQueueSize = averageSendableQueueSize / float64(len(outSockets)) // It's a total, not an average, up until this point. averageSendableQueueSize = averageSendableQueueSize / float64(len(outSockets)) // It's a total, not an average, up until this point.
pd = math.Max(float64(1.0/750.0), float64(1.0/(4.0*averageSendableQueueSize))) // Say 250ms is enough to get through the whole queue. pd = math.Max(float64(1.0/750.0), float64(1.0/(4.0*averageSendableQueueSize))) // Say 250ms is enough to get through the whole queue.
} else { } else {
pd = float64(1.0 / 0.1) // 100ms. pd = float64(0.1) // 100ms.
} }
log.Printf("Average sendable queue is %v messages. Changing queue timer to %f seconds\n", averageSendableQueueSize, pd)
queueTimer.Stop() queueTimer.Stop()
queueTimer = time.NewTicker(time.Duration(pd*1000000000.0) * time.Nanosecond) queueTimer = time.NewTicker(time.Duration(pd*1000000000.0) * time.Nanosecond)
lastQueueTimeChange = stratuxClock.Time lastQueueTimeChange = stratuxClock.Time

Wyświetl plik

@ -64,7 +64,7 @@ func readToChan(fp io.ReadCloser, ch chan []byte) {
func (e *ES) read() { func (e *ES) read() {
defer es_wg.Done() defer es_wg.Done()
log.Println("Entered ES read() ...") log.Println("Entered ES read() ...")
cmd := exec.Command("/usr/bin/dump1090", "--net", "--device-index", strconv.Itoa(e.indexID), "--ppm", strconv.Itoa(e.ppm)) cmd := exec.Command("/usr/bin/dump1090", "--oversample", "--net", "--device-index", strconv.Itoa(e.indexID), "--ppm", strconv.Itoa(e.ppm))
stdout, _ := cmd.StdoutPipe() stdout, _ := cmd.StdoutPipe()
stderr, _ := cmd.StderrPipe() stderr, _ := cmd.StderrPipe()
@ -128,21 +128,21 @@ func (u *UAT) read() {
} }
func getPPM(serial string) int { func getPPM(serial string) int {
r, err := regexp.Compile("str?a?t?u?x:\\d+:?(-?\\d*)"); r, err := regexp.Compile("str?a?t?u?x:\\d+:?(-?\\d*)")
if err != nil { if err != nil {
return globalSettings.PPM return globalSettings.PPM
} }
arr := r.FindStringSubmatch(serial); arr := r.FindStringSubmatch(serial)
if arr == nil { if arr == nil {
return globalSettings.PPM return globalSettings.PPM
} }
if ppm, err := strconv.Atoi(arr[1]); err != nil { if ppm, err := strconv.Atoi(arr[1]); err != nil {
return globalSettings.PPM return globalSettings.PPM
} else { } else {
return ppm return ppm
} }
} }
func (e *ES) sdrConfig() (err error) { func (e *ES) sdrConfig() (err error) {
@ -403,7 +403,7 @@ func sdrWatcher() {
serial = "" serial = ""
} }
if (rES != nil) { if rES != nil {
doSkip = rES.MatchString(serial) doSkip = rES.MatchString(serial)
} else { } else {
doSkip = strings.Compare(serial, "stratux:1090") == 0 doSkip = strings.Compare(serial, "stratux:1090") == 0
@ -452,7 +452,7 @@ func sdrWatcher() {
serial = "" serial = ""
} }
if (rUAT != nil) { if rUAT != nil {
doSkip = rUAT.MatchString(serial) doSkip = rUAT.MatchString(serial)
} else { } else {
doSkip = strings.Compare(serial, "stratux:978") == 0 doSkip = strings.Compare(serial, "stratux:978") == 0

Wyświetl plik

@ -65,60 +65,60 @@ const (
TARGET_TYPE_MODE_S = 0 TARGET_TYPE_MODE_S = 0
TARGET_TYPE_ADSB = 1 TARGET_TYPE_ADSB = 1
TARGET_TYPE_ADSR = 2 TARGET_TYPE_ADSR = 2
TARGET_TYPE_TISB = 3
// Assign next type to UAT messages with address qualifier == 2 // Assign next type to UAT messages with address qualifier == 2
// (code corresponds to and UAT GBT targets with Mode S addresses.. // (code corresponds to and UAT GBT targets with Mode S addresses..
// for we'll treat them as ADS-R in the UI. Might be able to assign as TYPE_ADSR if we see a proper emitter category and NIC > 7. // for we'll treat them as ADS-R in the UI. If we see a proper emitter category and NIC > 7, they'll be reassigned to TYPE_ADSR.
TARGET_TYPE_TISB_S = 4 TARGET_TYPE_TISB_S = 3
TARGET_TYPE_TISB = 4
) )
type TrafficInfo struct { type TrafficInfo struct {
Icao_addr uint32 Icao_addr uint32
Tail string Tail string
Emitter_category uint8 // Formatted using GDL90 standard, e.g. in a Mode ES report, A7 becomes 0x07, B0 becomes 0x08, etc. Emitter_category uint8 // Formatted using GDL90 standard, e.g. in a Mode ES report, A7 becomes 0x07, B0 becomes 0x08, etc.
OnGround bool // Air-ground status. On-ground is "true". OnGround bool // Air-ground status. On-ground is "true".
Addr_type uint8 // UAT address qualifier. Used by GDL90 format, so translations for ES TIS-B/ADS-R are needed. Addr_type uint8 // UAT address qualifier. Used by GDL90 format, so translations for ES TIS-B/ADS-R are needed.
TargetType uint8 // types decribed in const above TargetType uint8 // types decribed in const above
SignalLevel int32 // Arbitrary signal level reported by dump1090 and dump978. SignalLevel float64 // Signal level, dB RSSI.
Position_valid bool // set when position report received. Unset after n seconds? (To-do) Position_valid bool // set when position report received. Unset after n seconds? (To-do)
Lat float32 // decimal degrees, north positive Lat float32 // decimal degrees, north positive
Lng float32 // decimal degrees, east positive Lng float32 // decimal degrees, east positive
Alt int32 // Pressure altitude, feet Alt int32 // Pressure altitude, feet
GnssDiffFromBaroAlt int32 // GNSS altitude above WGS84 datum. Reported in TC 20-22 messages GnssDiffFromBaroAlt int32 // GNSS altitude above WGS84 datum. Reported in TC 20-22 messages
AltIsGNSS bool // Pressure alt = 0; GNSS alt = 1 AltIsGNSS bool // Pressure alt = 0; GNSS alt = 1
NIC int // Navigation Integrity Category. NIC int // Navigation Integrity Category.
NACp int // Navigation Accuracy Category for Position. NACp int // Navigation Accuracy Category for Position.
Track uint16 // degrees true Track uint16 // degrees true
Speed uint16 // knots Speed uint16 // knots
Speed_valid bool // set when speed report received. Speed_valid bool // set when speed report received.
Vvel int16 // feet per minute Vvel int16 // feet per minute
Timestamp time.Time // timestamp of traffic message, UTC Timestamp time.Time // timestamp of traffic message, UTC
// Parameters starting at 'Age' are calculated after message receipt. // Parameters starting at 'Age' are calculated after message receipt.
// Mode S transmits position and track in separate messages, and altitude can also be // Mode S transmits position and track in separate messages, and altitude can also be
// received from interrogations. // received from interrogations.
Age float64 // seconds ago traffic last seen Age float64 // seconds ago traffic last seen
Last_seen time.Time // time of last position update, relative to Stratux startup. Used for timing out expired data. Last_seen time.Time // time of last position update, relative to Stratux startup. Used for timing out expired data.
Last_alt time.Time // time of last altitude update, relative to Stratux startup Last_alt time.Time // time of last altitude update, relative to Stratux startup
Last_GnssDiff time.Time // time of last GnssDiffFromBaroAlt update, relative to Stratux startup Last_GnssDiff time.Time // time of last GnssDiffFromBaroAlt update, relative to Stratux startup
Last_GnssDiffAlt int32 // altitude at last GnssDiffFromBaroAlt update Last_GnssDiffAlt int32 // altitude at last GnssDiffFromBaroAlt update
Last_speed time.Time // time of last velocity / track update, relative to Stratux startup Last_speed time.Time // time of last velocity / track update, relative to Stratux startup
Last_source uint8 // last SDR on which this target was observed Last_source uint8 // last SDR on which this target was observed
ExtrapolatedPosition bool // TO-DO: True if Stratux is "coasting" the target from last known position. ExtrapolatedPosition bool // TO-DO: True if Stratux is "coasting" the target from last known position.
Bearing float64 // TO-DO: Bearing in degrees true to traffic from ownship Bearing float64 // TO-DO: Bearing in degrees true to traffic from ownship
Distance float64 // TO-DO: Distance to traffic from ownship Distance float64 // TO-DO: Distance to traffic from ownship
} }
type dump1090Data struct { type dump1090Data struct {
Icao_addr uint32 Icao_addr uint32
DF int // Mode S downlink format. DF int // Mode S downlink format.
CA int // Lowest 3 bits of first byte of Mode S message (DF11 and DF17 capability; DF18 control field, zero for all other DF types) CA int // Lowest 3 bits of first byte of Mode S message (DF11 and DF17 capability; DF18 control field, zero for all other DF types)
TypeCode int // Mode S type code TypeCode int // Mode S type code
SubtypeCode int // Mode S subtype code SubtypeCode int // Mode S subtype code
SBS_MsgType int // type of SBS message (used in "old" 1090 parsing) SBS_MsgType int // type of SBS message (used in "old" 1090 parsing)
SignalLevel int // 0-255 SignalLevel float64 // Decimal RSSI (0-1 nominal) as reported by dump1090-mutability. Convert to dB RSSI before setting in TrafficInfo.
Tail *string Tail *string
Squawk *int // 12-bit squawk code in octal format Squawk *int // 12-bit squawk code in octal format
Emitter_category *int Emitter_category *int
OnGround *bool OnGround *bool
Lat *float32 Lat *float32
@ -126,13 +126,13 @@ type dump1090Data struct {
Position_valid bool Position_valid bool
NACp *int NACp *int
Alt *int Alt *int
AltIsGNSS bool // AltIsGNSS bool //
GnssDiffFromBaroAlt *int16 // GNSS height above baro altitude in feet; valid range is -3125 to 3125. +/- 3138 indicates larger difference. GnssDiffFromBaroAlt *int16 // GNSS height above baro altitude in feet; valid range is -3125 to 3125. +/- 3138 indicates larger difference.
Vvel *int16 Vvel *int16
Speed_valid bool Speed_valid bool
Speed *uint16 Speed *uint16
Track *uint16 Track *uint16
Timestamp time.Time // time traffic last seen, UTC Timestamp time.Time // time traffic last seen, UTC
} }
var traffic map[uint32]TrafficInfo var traffic map[uint32]TrafficInfo
@ -152,14 +152,14 @@ func sendTrafficUpdates() {
defer trafficMutex.Unlock() defer trafficMutex.Unlock()
cleanupOldEntries() cleanupOldEntries()
var msg []byte var msg []byte
if ((stratuxClock.Time.Second() % 10) == 0) { if (stratuxClock.Time.Second() % 30) == 0 {
log.Printf("List of all aircraft being tracked:\n") log.Printf("List of all aircraft being tracked:\n")
log.Printf("==================================================================\n") log.Printf("==================================================================\n")
} }
for icao, ti := range traffic { // TO-DO: Limit number of aircraft in traffic message. ForeFlight 7.5 chokes at ~1000-2000 messages depending on iDevice RAM. Practical limit likely around ~500 aircraft without filtering. for icao, ti := range traffic { // TO-DO: Limit number of aircraft in traffic message. ForeFlight 7.5 chokes at ~1000-2000 messages depending on iDevice RAM. Practical limit likely around ~500 aircraft without filtering.
// DEBUG: Print the list of all tracked targets (with data) to the log every ten seconds // DEBUG: Print the list of all tracked targets (with data) to the log every ten seconds
if ((stratuxClock.Time.Second() % 10) == 0) { if (stratuxClock.Time.Second() % 10) == 0 {
s_out, err := json.Marshal(ti) s_out, err := json.Marshal(ti)
if err != nil { if err != nil {
log.Printf("Error generating output: %s\n", err.Error()) log.Printf("Error generating output: %s\n", err.Error())
@ -241,7 +241,7 @@ func makeTrafficReportMsg(ti TrafficInfo) []byte {
} }
msg[11] = byte((alt & 0xFF0) >> 4) // Altitude. msg[11] = byte((alt & 0xFF0) >> 4) // Altitude.
msg[12] = byte((alt & 0x00F) << 4) msg[12] = byte((alt & 0x00F) << 4)
// "m" field. Lower four bits define indicator bits: // "m" field. Lower four bits define indicator bits:
// - - 0 0 "tt" (msg[17]) is not valid // - - 0 0 "tt" (msg[17]) is not valid
// - - 0 1 "tt" is true track // - - 0 1 "tt" is true track
@ -251,22 +251,22 @@ func makeTrafficReportMsg(ti TrafficInfo) []byte {
// - 1 - - Report is extrapolated // - 1 - - Report is extrapolated
// 0 - - - On ground // 0 - - - On ground
// 1 - - - Airborne // 1 - - - Airborne
// Define tt type / validity // Define tt type / validity
if ti.Speed_valid { if ti.Speed_valid {
msg[12] = msg[12] | 0x01 // assume true track msg[12] = msg[12] | 0x01 // assume true track
} }
if ti.ExtrapolatedPosition { if ti.ExtrapolatedPosition {
msg[12] = msg[12] | 0x04 msg[12] = msg[12] | 0x04
} }
if !ti.OnGround { if !ti.OnGround {
msg[12] = msg[12] | 0x08 // Airborne. msg[12] = msg[12] | 0x08 // Airborne.
} }
// Position containment / navigational accuracy // Position containment / navigational accuracy
msg[13] = ((byte(ti.NIC) << 4) & 0xF0) | ((byte(ti.NACp) & 0x0F)) msg[13] = ((byte(ti.NIC) << 4) & 0xF0) | (byte(ti.NACp) & 0x0F)
// Horizontal velocity (speed). // Horizontal velocity (speed).
@ -296,7 +296,7 @@ func makeTrafficReportMsg(ti TrafficInfo) []byte {
return prepareMessage(msg) return prepareMessage(msg)
} }
func parseDownlinkReport(s string, signalLevel int32) { func parseDownlinkReport(s string, signalLevel int) {
var ti TrafficInfo var ti TrafficInfo
s = s[1:] s = s[1:]
@ -307,10 +307,10 @@ func parseDownlinkReport(s string, signalLevel int32) {
msg_type := (uint8(frame[0]) >> 3) & 0x1f msg_type := (uint8(frame[0]) >> 3) & 0x1f
addr_type := uint8(frame[0]) & 0x07 addr_type := uint8(frame[0]) & 0x07
icao_addr := (uint32(frame[1]) << 16) | (uint32(frame[2]) << 8) | uint32(frame[3]) icao_addr := (uint32(frame[1]) << 16) | (uint32(frame[2]) << 8) | uint32(frame[3])
trafficMutex.Lock() trafficMutex.Lock()
defer trafficMutex.Unlock() defer trafficMutex.Unlock()
// Retrieve previous information on this ICAO code. // Retrieve previous information on this ICAO code.
if val, ok := traffic[icao_addr]; ok { // if we've already seen it, copy it in to do updates as it may contain some useful information like "tail" from 1090ES. if val, ok := traffic[icao_addr]; ok { // if we've already seen it, copy it in to do updates as it may contain some useful information like "tail" from 1090ES.
ti = val ti = val
@ -321,9 +321,9 @@ func parseDownlinkReport(s string, signalLevel int32) {
ti.Icao_addr = icao_addr ti.Icao_addr = icao_addr
ti.ExtrapolatedPosition = false ti.ExtrapolatedPosition = false
} }
ti.Addr_type = addr_type ti.Addr_type = addr_type
// Parse tail number, if available. // Parse tail number, if available.
if msg_type == 1 || msg_type == 3 { // Need "MS" portion of message. if msg_type == 1 || msg_type == 3 { // Need "MS" portion of message.
base40_alphabet := string("0123456789ABCDEFGHIJKLMNOPQRTSUVWXYZ ..") base40_alphabet := string("0123456789ABCDEFGHIJKLMNOPQRTSUVWXYZ ..")
@ -351,7 +351,7 @@ func parseDownlinkReport(s string, signalLevel int32) {
ti.Tail = "u" + ti.Tail ti.Tail = "u" + ti.Tail
} }
} }
// Extract emitter category. // Extract emitter category.
if msg_type == 1 || msg_type == 3 { if msg_type == 1 || msg_type == 3 {
v := (uint16(frame[17]) << 8) | (uint16(frame[18])) v := (uint16(frame[17]) << 8) | (uint16(frame[18]))
@ -362,12 +362,16 @@ func parseDownlinkReport(s string, signalLevel int32) {
// fmt.Printf("%d, %d, %06X\n", msg_type, ti.Addr_type, ti.Icao_addr) // fmt.Printf("%d, %d, %06X\n", msg_type, ti.Addr_type, ti.Icao_addr)
ti.NIC = int(frame[11] & 0x0F) ti.NIC = int(frame[11] & 0x0F)
if ((msg_type == 1) || (msg_type == 3)) { // Since NACp is passed with normal UATreports, no need to use our ES hack. if (msg_type == 1) || (msg_type == 3) { // Since NACp is passed with normal UATreports, no need to use our ES hack.
ti.NACp = int((frame[25] >> 4) & 0x0F) ti.NACp = int((frame[25] >> 4) & 0x0F)
} }
ti.SignalLevel = signalLevel power := 10 * (math.Log10(float64((signalLevel * signalLevel))) - 6) // reported amplitude is 0-1000. Subtract 60 (10*log 1e6) to set nominal full scale = 0.
//log.Printf("%s (%X) seen with amplitude of %d, corresponding to normalized power of %f.2 dB\n",ti.Tail,ti.Icao_addr,signalLevel,power)
ti.SignalLevel = power
if ti.Addr_type == 0 { if ti.Addr_type == 0 {
ti.TargetType = TARGET_TYPE_ADSB ti.TargetType = TARGET_TYPE_ADSB
@ -377,11 +381,11 @@ func parseDownlinkReport(s string, signalLevel int32) {
ti.TargetType = TARGET_TYPE_ADSR ti.TargetType = TARGET_TYPE_ADSR
} else if ti.Addr_type == 2 { } else if ti.Addr_type == 2 {
ti.TargetType = TARGET_TYPE_TISB_S ti.TargetType = TARGET_TYPE_TISB_S
if ((ti.NIC >= 7) && (ti.Emitter_category > 0)) { // If NIC is sufficiently and emitter type is transmitted, we'll assume it's ADS-R. if (ti.NIC >= 7) && (ti.Emitter_category > 0) { // If NIC is sufficiently and emitter type is transmitted, we'll assume it's ADS-R.
ti.TargetType = TARGET_TYPE_ADSR ti.TargetType = TARGET_TYPE_ADSR
} }
} }
raw_lat := (uint32(frame[4]) << 15) | (uint32(frame[5]) << 7) | (uint32(frame[6]) >> 1) raw_lat := (uint32(frame[4]) << 15) | (uint32(frame[5]) << 7) | (uint32(frame[6]) >> 1)
raw_lon := ((uint32(frame[6]) & 0x01) << 23) | (uint32(frame[7]) << 15) | (uint32(frame[8]) << 7) | (uint32(frame[9]) >> 1) raw_lon := ((uint32(frame[6]) & 0x01) << 23) | (uint32(frame[7]) << 15) | (uint32(frame[8]) << 7) | (uint32(frame[9]) >> 1)
@ -418,7 +422,7 @@ func parseDownlinkReport(s string, signalLevel int32) {
ti.Alt = alt ti.Alt = alt
ti.AltIsGNSS = alt_geo ti.AltIsGNSS = alt_geo
ti.Last_alt = stratuxClock.Time ti.Last_alt = stratuxClock.Time
//OK. //OK.
// fmt.Printf("%d, %t, %f, %f, %t, %d\n", nic, position_valid, lat, lng, alt_geo, alt) // fmt.Printf("%d, %t, %f, %f, %t, %d\n", nic, position_valid, lat, lng, alt_geo, alt)
@ -502,7 +506,7 @@ func parseDownlinkReport(s string, signalLevel int32) {
if ti.Speed_valid { if ti.Speed_valid {
ti.Last_speed = stratuxClock.Time ti.Last_speed = stratuxClock.Time
} }
//OK. //OK.
// fmt.Printf("ns_vel %d, ew_vel %d, track %d, speed_valid %t, speed %d, vvel_geo %t, vvel %d\n", ns_vel, ew_vel, track, speed_valid, speed, vvel_geo, vvel) // fmt.Printf("ns_vel %d, ew_vel %d, track %d, speed_valid %t, speed %d, vvel_geo %t, vvel %d\n", ns_vel, ew_vel, track, speed_valid, speed, vvel_geo, vvel)
@ -521,7 +525,7 @@ func parseDownlinkReport(s string, signalLevel int32) {
// fmt.Printf("tisb_site_id %d, utc_coupled %t\n", tisb_site_id, utc_coupled) // fmt.Printf("tisb_site_id %d, utc_coupled %t\n", tisb_site_id, utc_coupled)
ti.Timestamp = time.Now() ti.Timestamp = time.Now()
ti.Last_source = TRAFFIC_SOURCE_UAT ti.Last_source = TRAFFIC_SOURCE_UAT
traffic[ti.Icao_addr] = ti traffic[ti.Icao_addr] = ti
@ -566,12 +570,12 @@ func esListen() {
thisMsg.TimeReceived = stratuxClock.Time thisMsg.TimeReceived = stratuxClock.Time
thisMsg.Data = []byte(buf) thisMsg.Data = []byte(buf)
MsgLog = append(MsgLog, thisMsg) MsgLog = append(MsgLog, thisMsg)
icao := uint32(newTi.Icao_addr) icao := uint32(newTi.Icao_addr)
var ti TrafficInfo var ti TrafficInfo
trafficMutex.Lock() trafficMutex.Lock()
// Retrieve previous information on this ICAO code. // Retrieve previous information on this ICAO code.
if val, ok := traffic[icao]; ok { // if we've already seen it, copy it in to do updates if val, ok := traffic[icao]; ok { // if we've already seen it, copy it in to do updates
ti = val ti = val
@ -582,39 +586,39 @@ func esListen() {
ti.Icao_addr = icao ti.Icao_addr = icao
ti.ExtrapolatedPosition = false ti.ExtrapolatedPosition = false
} }
ti.SignalLevel = int32(newTi.SignalLevel) ti.SignalLevel = 10 * math.Log10(newTi.SignalLevel)
// generate human readable summary of message types for debug // generate human readable summary of message types for debug
// TO-DO: Use for ES message statistics? // TO-DO: Use for ES message statistics?
/* /*
var s1 string var s1 string
if newTi.DF == 17 { if newTi.DF == 17 {
s1 = "ADS-B" s1 = "ADS-B"
} }
if newTi.DF == 18 { if newTi.DF == 18 {
s1 = "ADS-R / TIS-B" s1 = "ADS-R / TIS-B"
} }
if newTi.DF == 4 || newTi.DF == 20 { if newTi.DF == 4 || newTi.DF == 20 {
s1 = "Surveillance, Alt. Reply" s1 = "Surveillance, Alt. Reply"
} }
if newTi.DF == 5 || newTi.DF == 21 { if newTi.DF == 5 || newTi.DF == 21 {
s1 = "Surveillance, Ident. Reply" s1 = "Surveillance, Ident. Reply"
} }
if newTi.DF == 11 { if newTi.DF == 11 {
s1 = "All-call Reply" s1 = "All-call Reply"
} }
if newTi.DF == 0 { if newTi.DF == 0 {
s1 = "Short Air-Air Surv." s1 = "Short Air-Air Surv."
} }
if newTi.DF == 16 { if newTi.DF == 16 {
s1 = "Long Air-Air Surv." s1 = "Long Air-Air Surv."
} }
*/ */
//log.Printf("Mode S message from icao=%X, DF=%02d, CA=%02d, TC=%02d (%s)\n", ti.Icao_addr, newTi.DF, newTi.CA, newTi.TypeCode, s1) //log.Printf("Mode S message from icao=%X, DF=%02d, CA=%02d, TC=%02d (%s)\n", ti.Icao_addr, newTi.DF, newTi.CA, newTi.TypeCode, s1)
@ -622,18 +626,18 @@ func esListen() {
// and Mode S messages (DF=0, DF = 4, and DF = 20). // and Mode S messages (DF=0, DF = 4, and DF = 20).
ti.AltIsGNSS = newTi.AltIsGNSS ti.AltIsGNSS = newTi.AltIsGNSS
if newTi.Alt != nil { if newTi.Alt != nil {
ti.Alt = int32(*newTi.Alt) ti.Alt = int32(*newTi.Alt)
ti.Last_alt = stratuxClock.Time ti.Last_alt = stratuxClock.Time
} }
if newTi.GnssDiffFromBaroAlt != nil { if newTi.GnssDiffFromBaroAlt != nil {
ti.GnssDiffFromBaroAlt = int32(*newTi.GnssDiffFromBaroAlt) // we can estimate pressure altitude from GNSS height with this parameter! ti.GnssDiffFromBaroAlt = int32(*newTi.GnssDiffFromBaroAlt) // we can estimate pressure altitude from GNSS height with this parameter!
ti.Last_GnssDiff = stratuxClock.Time ti.Last_GnssDiff = stratuxClock.Time
ti.Last_GnssDiffAlt = ti.Alt ti.Last_GnssDiffAlt = ti.Alt
} }
// Position updates are provided only by ES messages (DF=17 and DF=18; multiple TCs) // Position updates are provided only by ES messages (DF=17 and DF=18; multiple TCs)
if newTi.Position_valid { // i.e. DF17 or DF18 message decoded successfully by dump1090 if newTi.Position_valid { // i.e. DF17 or DF18 message decoded successfully by dump1090
valid_position := true valid_position := true
@ -691,8 +695,8 @@ func esListen() {
ti.Speed = speed ti.Speed = speed
ti.Speed_valid = true ti.Speed_valid = true
ti.Last_speed = stratuxClock.Time // only update "last seen" data on position updates ti.Last_speed = stratuxClock.Time // only update "last seen" data on position updates
} }
} else if (((newTi.DF == 17) || (newTi.DF == 18)) && (newTi.TypeCode == 19)) { // invalid speed on velocity message only } else if ((newTi.DF == 17) || (newTi.DF == 18)) && (newTi.TypeCode == 19) { // invalid speed on velocity message only
ti.Speed_valid = false ti.Speed_valid = false
} }
@ -730,20 +734,20 @@ func esListen() {
nic = 11 nic = 11
} }
ti.NIC = nic ti.NIC = nic
if ((ti.NACp < 7) && (ti.NACp < ti.NIC)) { if (ti.NACp < 7) && (ti.NACp < ti.NIC) {
ti.NACp = ti.NIC // initialize to NIC, since NIC is sent with every position report, and not all emitters report NACp. ti.NACp = ti.NIC // initialize to NIC, since NIC is sent with every position report, and not all emitters report NACp.
} }
} }
if newTi.NACp != nil { if newTi.NACp != nil {
ti.NACp = *newTi.NACp ti.NACp = *newTi.NACp
} }
if newTi.Emitter_category != nil { if newTi.Emitter_category != nil {
ti.Emitter_category = uint8(*newTi.Emitter_category) // validate dump1090 on live traffic ti.Emitter_category = uint8(*newTi.Emitter_category) // validate dump1090 on live traffic
} }
// Set the target type. DF=18 messages are sent by ground station, so we look at CA // Set the target type. DF=18 messages are sent by ground station, so we look at CA
// (repurposed to Control Field in DF18) to determine if it's ADS-R or TIS-B. // (repurposed to Control Field in DF18) to determine if it's ADS-R or TIS-B.
if newTi.DF == 17 { if newTi.DF == 17 {
@ -753,15 +757,15 @@ func esListen() {
if newTi.CA == 6 { if newTi.CA == 6 {
ti.TargetType = TARGET_TYPE_ADSR ti.TargetType = TARGET_TYPE_ADSR
ti.Addr_type = 2 ti.Addr_type = 2
} else if (newTi.CA == 2) { // 2 = TIS-B with ICAO address, 5 = TIS-B without ICAO address } else if newTi.CA == 2 { // 2 = TIS-B with ICAO address, 5 = TIS-B without ICAO address
ti.TargetType = TARGET_TYPE_TISB ti.TargetType = TARGET_TYPE_TISB
ti.Addr_type = 2 ti.Addr_type = 2
} else if (newTi.CA == 5) { } else if newTi.CA == 5 {
ti.TargetType = TARGET_TYPE_TISB ti.TargetType = TARGET_TYPE_TISB
ti.Addr_type = 3 ti.Addr_type = 3
} }
} }
if newTi.OnGround != nil { // DF=11 messages don't report "on ground" status so we need to check for valid values. if newTi.OnGround != nil { // DF=11 messages don't report "on ground" status so we need to check for valid values.
ti.OnGround = bool(*newTi.OnGround) ti.OnGround = bool(*newTi.OnGround)
} }
@ -780,14 +784,14 @@ func esListen() {
ti.Timestamp = newTi.Timestamp // only update "last seen" data on position updates ti.Timestamp = newTi.Timestamp // only update "last seen" data on position updates
ti.Last_source = TRAFFIC_SOURCE_1090ES ti.Last_source = TRAFFIC_SOURCE_1090ES
/* /*
s_out, err := json.Marshal(ti) s_out, err := json.Marshal(ti)
if err != nil { if err != nil {
log.Printf("Error generating output: %s\n", err.Error()) log.Printf("Error generating output: %s\n", err.Error())
} else { } else {
log.Printf("%X (DF%d) => %s\n", ti.Icao_addr, newTi.DF, string(s_out)) log.Printf("%X (DF%d) => %s\n", ti.Icao_addr, newTi.DF, string(s_out))
} }
*/ */
traffic[ti.Icao_addr] = ti // Update information on this ICAO code. traffic[ti.Icao_addr] = ti // Update information on this ICAO code.
registerTrafficUpdate(ti) registerTrafficUpdate(ti)
seenTraffic[ti.Icao_addr] = true // Mark as seen. seenTraffic[ti.Icao_addr] = true // Mark as seen.
@ -835,6 +839,17 @@ func updateDemoTraffic(icao uint32, tail string, relAlt float32, gs float64, off
if ti.Addr_type == 1 { //reserved value if ti.Addr_type == 1 { //reserved value
ti.Addr_type = 0 ti.Addr_type = 0
} }
if ti.Addr_type == 0 {
ti.TargetType = TARGET_TYPE_ADSB
}
if ti.Addr_type == 2 {
ti.TargetType = TARGET_TYPE_ADSR
}
if ti.Addr_type == 3 {
ti.TargetType = TARGET_TYPE_TISB
}
ti.Emitter_category = 1 ti.Emitter_category = 1
ti.Lat = float32(lat + traffRelLat) ti.Lat = float32(lat + traffRelLat)
ti.Lng = float32(lng + traffRelLng) ti.Lng = float32(lng + traffRelLng)
@ -857,13 +872,12 @@ func updateDemoTraffic(icao uint32, tail string, relAlt float32, gs float64, off
ti.Last_seen = stratuxClock.Time ti.Last_seen = stratuxClock.Time
ti.Last_alt = stratuxClock.Time ti.Last_alt = stratuxClock.Time
ti.Last_speed = stratuxClock.Time ti.Last_speed = stratuxClock.Time
ti.TargetType = TARGET_TYPE_ADSB
ti.NACp = 8 ti.NACp = 8
ti.NIC = 8 ti.NIC = 8
//ti.Age = math.Floor(ti.Age) + hdg / 1000 //ti.Age = math.Floor(ti.Age) + hdg / 1000
ti.Last_source = 1 ti.Last_source = 1
if icao%7 == 1 { // make some of the traffic look like it came from UAT if icao%5 == 1 { // make some of the traffic look like it came from UAT
ti.Last_source = 2 ti.Last_source = 2
} }

Wyświetl plik

@ -160,7 +160,8 @@
} }
.traffic-style12, .traffic-style12,
.traffic-style13 { .traffic-style13,
.traffic-style14 {
color: #000000; color: #000000;
background-color: skyblue; background-color: skyblue;
} }
@ -174,7 +175,8 @@
} }
.traffic-style22, .traffic-style22,
.traffic-style23 { .traffic-style23,
.traffic-style24 {
color: #000000; color: #000000;
background-color: khaki background-color: khaki
} }

Wyświetl plik

@ -19,8 +19,9 @@ function TowersCtrl($rootScope, $scope, $state, $http, $interval) {
function setTower(obj, new_tower) { function setTower(obj, new_tower) {
new_tower.lat = dmsString(obj.Lat); new_tower.lat = dmsString(obj.Lat);
new_tower.lon = dmsString(obj.Lng); new_tower.lon = dmsString(obj.Lng);
new_tower.signal = obj.Signal_strength_last_minute; new_tower.power = obj.Signal_strength_now.toFixed(2);
// Signal_strength_max int new_tower.power_last_min = obj.Signal_strength_last_minute.toFixed(2);
new_tower.power_max = obj.Signal_strength_max.toFixed(2);
// Messages_last_minute uint64 // Messages_last_minute uint64
new_tower.messages = obj.Messages_total; new_tower.messages = obj.Messages_total;
} }
@ -57,7 +58,7 @@ function TowersCtrl($rootScope, $scope, $state, $http, $interval) {
var updateTowers = $interval(function () { var updateTowers = $interval(function () {
// refresh tower list once every 5 seconds (aka polling) // refresh tower list once every 5 seconds (aka polling)
getTowers(); getTowers();
}, (5 * 1000), 0, false); }, (2 * 1000), 0, false);
$state.get('towers').onEnter = function () { $state.get('towers').onEnter = function () {
// everything gets handled correctly by the controller // everything gets handled correctly by the controller

Wyświetl plik

@ -35,10 +35,11 @@ function TrafficCtrl($rootScope, $scope, $state, $http, $interval) {
function setAircraft(obj, new_traffic) { function setAircraft(obj, new_traffic) {
new_traffic.icao_int = obj.Icao_addr; new_traffic.icao_int = obj.Icao_addr;
new_traffic.addr_type = obj.Addr_type; new_traffic.targettype = obj.TargetType;
new_traffic.signal = obj.SignalLevel.toFixed(2);
new_traffic.addr_symb ='\u2708'; new_traffic.addr_symb ='\u2708';
if (new_traffic.addr_type > 1) { if (new_traffic.targettype > 2) {
new_traffic.addr_symb ='\u{1F4E1}'; new_traffic.addr_symb ='\ud83d\udce1';
} }
new_traffic.icao = obj.Icao_addr.toString(16).toUpperCase(); new_traffic.icao = obj.Icao_addr.toString(16).toUpperCase();
new_traffic.tail = obj.Tail; new_traffic.tail = obj.Tail;

Wyświetl plik

@ -7,16 +7,20 @@
</div> </div>
<div class="panel-body towers-page"> <div class="panel-body towers-page">
<div class="row"> <div class="row">
<span class="col-xs-6"><strong>Location</strong></span> <span class="col-xs-4"><strong>Location</strong></span>
<span class="col-xs-3 text-right">Signal</span> <span class="col-xs-2 text-right">Current Power (dB)</span>
<span class="col-xs-3 text-right">Msgs</span> <span class="col-xs-2 text-right">Avg Power Last Min(dB)</span>
<span class="col-xs-2 text-right">Max Power (dB)</span>
<span class="col-xs-2 text-right">Msgs Last Minute</span>
</div> </div>
<div class="row" ng-repeat="tower in data_list | orderBy: messages"> <div class="row" ng-repeat="tower in data_list | orderBy: messages">
<div class="separator"></div> <div class="separator"></div>
<span class="col-xs-6">{{tower.lat}} {{tower.lon}}</span> <span class="col-xs-4">{{tower.lat}} {{tower.lon}}</span>
<span class="col-xs-3 text-right">{{tower.signal}}</span> <span class="col-xs-2 text-right">{{tower.power}}</span>
<span class="col-xs-3 text-right">{{tower.messages}}</span> <span class="col-xs-2 text-right">{{tower.power_last_min}}</span>
<span class="col-xs-2 text-right">{{tower.power_max}}</span>
<span class="col-xs-2 text-right">{{tower.messages}}</span>
</div> </div>
</div> </div>
</div> </div>

Wyświetl plik

@ -15,7 +15,8 @@
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<span class="col-xs-2">&nbsp;</span> <span class="col-xs-2">&nbsp;</span>
<span class="col-xs-7">Location</span> <span class="col-xs-5">Location</span>
<span class="col-xs-2">Power (dB)</span>
<span class="col-xs-3 text-right">Age</span> <span class="col-xs-3 text-right">Age</span>
</div> </div>
</div> </div>
@ -24,8 +25,8 @@
<div class="separator"></div> <div class="separator"></div>
<div class="col-sm-6"> <div class="col-sm-6">
<span class="col-xs-3"> <span class="col-xs-3">
<span ng-show="aircraft.tail" ng-class="'label traffic-style'+aircraft.src+aircraft.addr_type"><strong>{{aircraft.addr_symb}}&nbsp;{{aircraft.tail}}</strong></span> <span ng-show="aircraft.tail" ng-class="'label traffic-style'+aircraft.src+aircraft.targettype"><strong>{{aircraft.addr_symb}}&nbsp;{{aircraft.tail}}</strong></span>
<span ng-hide="aircraft.tail" ng-class="'label traffic-style'+aircraft.src+aircraft.addr_type"><strong class="text-muted">{{aircraft.addr_symb}}&nbsp;{{aircraft.icao}}</strong></span> <span ng-hide="aircraft.tail" ng-class="'label traffic-style'+aircraft.src+aircraft.targettype"><strong class="text-muted">{{aircraft.addr_symb}}&nbsp;{{aircraft.icao}}</strong></span>
</span> </span>
<span class="col-xs-3 text-right">{{aircraft.speed}} KTS</span> <span class="col-xs-3 text-right">{{aircraft.speed}} KTS</span>
@ -38,7 +39,8 @@
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<span class="col-xs-2">&nbsp;</span> <span class="col-xs-2">&nbsp;</span>
<span class="col-xs-7">{{aircraft.lat}} {{aircraft.lon}}</span> <span class="col-xs-5">{{aircraft.lat}} {{aircraft.lon}}</span>
<span class="col-xs-2">{{aircraft.signal}}</span>
<!--<span class="col-xs-3 text-right">{{aircraft.time}}</span>--> <!--<span class="col-xs-3 text-right">{{aircraft.time}}</span>-->
<span class="col-xs-3 text-right">{{aircraft.age}}s</span> <span class="col-xs-3 text-right">{{aircraft.age}}s</span>
</div> </div>