diff --git a/res/layout/stationview.xml b/res/layout/stationview.xml
index da66c8c..0103dfb 100644
--- a/res/layout/stationview.xml
+++ b/res/layout/stationview.xml
@@ -47,6 +47,15 @@
android:typeface="monospace"
/>
+
None }
+ }
+
+ def parseWx(comment : String) : String = {
+ import StationListAdapter._
+ // Sample
+ // 000/000g000t056r000p000P000h75b10164L000eCumulusDsVP
+ //val comment = "000/000g000t056r000p000P000h75b10164L000eCumulusDsVP"
+
+ // Handle PEET events
+ if(comment.matches(WX_PEET_RE.toString)) return parseWxPeet(comment)
+
+ val WX_WIND_RE = ".*(\\d{3})/(\\d{3}).*".r
+ val WX_GUST_RE = ".*g(\\d{3}).*".r
+ val WX_SPEED_RE = ".*s(\\d{3}).*".r
+ val WX_DIR_RE = ".*c(\\d{3}).*".r
+ val WX_TEMP_RE = ".*t(\\d{3}).*".r
+ val WX_RAIN_HOUR_RE = ".*r(\\d{3}).*".r
+ val WX_RAIN_24_RE = ".*p(\\d{3}).*".r
+ val WX_RAIN_TODAY_RE = ".*P(\\d{3}).*".r
+ val WX_HUMID_RE = ".*h(\\d{2}).*".r
+ val WX_QNH_RE = ".*b(\\d{5}).*".r
+ val WX_LUM_LOW_RE = ".*L(\\d{3}).*".r
+ val WX_LUM_HIGH_RE = ".*l(\\d{4}).*".r
+ val WX_SNOW = ".*s(\\d{3}).*".r
+
+ var windDir = parseWxElement(comment, WX_WIND_RE, 1)
+ var windSpeed = parseWxElement(comment, WX_WIND_RE, 2)
+ var windGust = parseWxElement(comment, WX_GUST_RE, 1)
+ var snow = parseWxElement(comment, WX_SNOW, 1)
+ if (windSpeed == None) {
+ // For some reason, snow and windspeed use the same prefix. If we didn't get wind using the
+ // XXX/XXX format, then it must be here as sXXX wind speed,
+ // therefore snow is not possible to get in this situation
+ snow = None
+ windSpeed = parseWxElement(comment, WX_SPEED_RE, 1)
+ }
+ if (windDir == None) windDir = parseWxElement(comment, WX_DIR_RE, 1)
+ var ambTemp = parseWxElement(comment, WX_TEMP_RE, 1)
+ var rainHour = parseWxElement(comment, WX_RAIN_HOUR_RE, 1)
+ var rainDay = parseWxElement(comment, WX_RAIN_24_RE, 1)
+ var rainToday = parseWxElement(comment, WX_RAIN_TODAY_RE, 1)
+ var humidity = parseWxElement(comment, WX_HUMID_RE, 1)
+ var qnh = parseWxElement(comment, WX_QNH_RE, 1)
+ var lumLow = parseWxElement(comment, WX_LUM_LOW_RE, 1)
+ var lumHigh = parseWxElement(comment, WX_LUM_HIGH_RE, 1)
+
+ if(lumHigh != None)
+ lumHigh = Some(lumHigh.get + 1000.0)
+
+ var lum = if(lumLow != None) lumLow else if(lumHigh != None) lumHigh else None
+
+ // unit conversions
+ windSpeed = if (windSpeed != None) Some(kt2mps(windSpeed.get)) else None
+ windGust = if (windGust != None) Some(kt2mps(windGust.get)) else None
+ ambTemp = if (ambTemp != None) Some(f2c(ambTemp.get)) else None
+ rainHour = if(rainHour != None) Some(hi2mm(rainHour.get)) else None
+ rainDay = if(rainDay != None) Some(hi2mm(rainDay.get)) else None
+ rainToday = if(rainToday != None) Some(hi2mm(rainToday.get)) else None
+ snow = if(snow != None) Some(hi2mm(snow.get)) else None
+ var compWind = if (windDir != None) getBearing(windDir.get) else None
+
+ val wxFinal = new scala.collection.mutable.ListBuffer[String]
+ if(ambTemp != None) wxFinal += "%1.1f°C".format(ambTemp.get)
+ if(humidity != None) wxFinal += "%1.0f%%".format(humidity.get)
+ val windBuf = new scala.collection.mutable.ListBuffer[String]
+ if(windSpeed != None) windBuf += "Wind %1.1fm/s".format(windSpeed.get)
+ if(windGust != None) windBuf += "Gust %1.1fm/s".format(windGust.get)
+ if(windDir != None) windBuf += "%s (%1.0f°)".format(compWind, windDir.get)
+ if(windBuf.length > 0) wxFinal += windBuf.toList.mkString(" ")
+ if(qnh != None) wxFinal += "%1.0fmbar".format(qnh.get)
+ val rainBuf = new scala.collection.mutable.ListBuffer[String]
+ if(rainHour != None) rainBuf += "%1.1f".format(rainHour.get)
+ if(rainDay != None) rainBuf += "%1.1f".format(rainHour.get)
+ if(rainToday != None) rainBuf += "%1.1f".format(rainHour.get)
+ if(rainBuf.length > 0) wxFinal += "Rain " + rainBuf.toList.mkString("/") + "mm"
+ if(snow != None) wxFinal += "Snow %1.1fmm".format(snow.get)
+ if(lum != None) wxFinal += "%1.0fW/m2".format(lum.get)
+ wxFinal.toList.mkString(" ")
+ }
+
def parseHostPort(hostport : String, defaultport : Int) : (String, Int) = {
val splits = hostport.trim().split(":")
try {
diff --git a/src/StationListAdapter.scala b/src/StationListAdapter.scala
index 72662a6..f74dfbd 100644
--- a/src/StationListAdapter.scala
+++ b/src/StationListAdapter.scala
@@ -12,12 +12,15 @@ import _root_.android.widget.FilterQueryProvider
object StationListAdapter {
import StorageDatabase.Station._
- val LIST_FROM = Array(CALL, COMMENT, QRG)
- val LIST_TO = Array(R.id.station_call, R.id.listmessage, R.id.station_qrg)
+ val LIST_FROM = Array(CALL, COMMENT, QRG, WX)
+ val LIST_TO = Array(R.id.station_call, R.id.listmessage, R.id.station_qrg, R.id.extendedinfo)
val SINGLE = 0
val NEIGHBORS = 1
val SSIDS = 2
+ // return compass bearing for a given value
+ private val LETTERS = Array("N", "NE", "E", "SE", "S", "SW", "W", "NW")
+ def getBearing(b : Double) = LETTERS(((b.toInt + 22 + 720) % 360) / 45)
}
class StationListAdapter(context : Context, prefs : PrefsWrapper,
@@ -52,10 +55,6 @@ class StationListAdapter(context : Context, prefs : PrefsWrapper,
mix.reduceLeft(_*256 + _)
}
- // return compass bearing for a given value
- private val LETTERS = Array("N", "NE", "E", "SE", "S", "SW", "W", "NW")
- def getBearing(b : Double) = LETTERS(((b.toInt + 22 + 720) % 360) / 45)
-
override def bindView(view : View, context : Context, cursor : Cursor) {
import StorageDatabase.Station._
@@ -67,9 +66,9 @@ class StationListAdapter(context : Context, prefs : PrefsWrapper,
val lat = cursor.getInt(COLUMN_LAT)
val lon = cursor.getInt(COLUMN_LON)
val qrg = cursor.getString(COLUMN_QRG)
+ var wx = cursor.getString(COLUMN_WX)
val symbol = cursor.getString(COLUMN_SYMBOL)
val dist = Array[Float](0, 0)
-
if (call == mycall) {
view.setBackgroundColor(0x4020ff20)
} else if (call == targetcall) {
@@ -85,8 +84,10 @@ class StationListAdapter(context : Context, prefs : PrefsWrapper,
val MCD = 1000000.
android.location.Location.distanceBetween(my_lat/MCD, my_lon/MCD,
lat/MCD, lon/MCD, dist)
- distage.setText("%1.1f km %s\n%s".format(dist(0)/1000., getBearing(dist(1)), age))
+ distage.setText("%1.1f km %s\n%s".format(dist(0)/1000., StationListAdapter.getBearing(dist(1)), age))
view.findViewById(R.id.station_symbol).asInstanceOf[SymbolView].setSymbol(symbol)
+ val wx_visible = if(wx != null && wx != "") View.VISIBLE else View.GONE
+ view.findViewById(R.id.extendedinfo).setVisibility(wx_visible)
super.bindView(view, context, cursor)
}
diff --git a/src/StorageDatabase.scala b/src/StorageDatabase.scala
index 5624339..fab6351 100644
--- a/src/StorageDatabase.scala
+++ b/src/StorageDatabase.scala
@@ -14,7 +14,7 @@ import _root_.scala.math.{cos, Pi}
object StorageDatabase {
val TAG = "APRSdroid.Storage"
- val DB_VERSION = 3
+ val DB_VERSION = 4
val DB_NAME = "storage.db"
val TSS_COL = "DATETIME(TS/1000, 'unixepoch', 'localtime') as TSS"
@@ -57,30 +57,32 @@ object StorageDatabase {
val ORIGIN = "origin" // originator call for object/item
val QRG = "qrg" // voice frequency
val FLAGS = "flags" // bitmask for attributes like "messaging capable"
+ val WX = "wx" // weather data
lazy val TABLE_CREATE = """CREATE TABLE %s (%s INTEGER PRIMARY KEY AUTOINCREMENT, %s LONG,
%s TEXT UNIQUE, %s INTEGER, %s INTEGER,
%s INTEGER, %s INTEGER, %s INTEGER,
- %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s INTEGER)"""
+ %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s INTEGER, %s TEXT)"""
.format(TABLE, _ID, TS,
CALL, LAT, LON,
SPEED, COURSE, ALT,
- SYMBOL, COMMENT, ORIGIN, QRG, FLAGS)
+ SYMBOL, COMMENT, ORIGIN, QRG, FLAGS, WX)
lazy val TABLE_DROP = "DROP TABLE %s".format(TABLE)
- lazy val COLUMNS = Array(_ID, TS, CALL, LAT, LON, SYMBOL, COMMENT, SPEED, COURSE, ALT, ORIGIN, QRG)
+ lazy val COLUMNS = Array(_ID, TS, CALL, LAT, LON, SPEED, COURSE, ALT, SYMBOL, COMMENT, ORIGIN, QRG, FLAGS, WX)
lazy val COL_DIST = "((lat - %d)*(lat - %d) + (lon - %d)*(lon - %d)*%d/100) as dist"
val COLUMN_TS = 1
val COLUMN_CALL = 2
val COLUMN_LAT = 3
val COLUMN_LON = 4
- val COLUMN_SYMBOL = 5
- val COLUMN_COMMENT = 6
- val COLUMN_SPEED = 7
- val COLUMN_COURSE = 8
- val COLUMN_ALT = 9
+ val COLUMN_SPEED = 5
+ val COLUMN_COURSE = 6
+ val COLUMN_ALT = 7
+ val COLUMN_SYMBOL = 8
+ val COLUMN_COMMENT = 9
val COLUMN_ORIGIN = 10
val COLUMN_QRG = 11
val COLUMN_FLAGS = 12
+ val COLUMN_WX = 13
lazy val COLUMNS_MAP = Array(_ID, CALL, LAT, LON, SYMBOL, ORIGIN)
val COLUMN_MAP_CALL = 1
@@ -199,6 +201,9 @@ class StorageDatabase(context : Context) extends
db.execSQL(Station.TABLE_CREATE)
db.execSQL(Position.TABLE_CREATE)
}
+ if(from <= 3 && to <= 4) {
+ db.execSQL("ALTER TABLE stations ADD COLUMN wx TEXT")
+ }
}
def trimPosts(ts : Long) = Benchmark("trimPosts") {
@@ -225,6 +230,8 @@ class StorageDatabase(context : Context) extends
val sym = "%s%s".format(pos.getSymbolTable(), pos.getSymbolCode())
val comment = ap.getAprsInformation().getComment()
val qrg = AprsPacket.parseQrg(comment)
+ val wx = if(pos.getSymbolCode() == '_') AprsPacket.parseWx(comment) else if (pos.getSymbolCode() == '$') AprsPacket.parseWxPeet(comment) else ""
+
cv.put(TS, ts.asInstanceOf[java.lang.Long])
cv.put(CALL, if (objectname != null) objectname else call)
cv.put(LAT, lat.asInstanceOf[java.lang.Integer])
@@ -237,13 +244,15 @@ class StorageDatabase(context : Context) extends
cv.put(SYMBOL, sym)
cv.put(COMMENT, comment)
cv.put(QRG, qrg)
+ cv.put(WX, wx)
+
if (cse != null) {
cv.put(SPEED, cse.getSpeed().asInstanceOf[java.lang.Integer])
cv.put(COURSE, cse.getCourse().asInstanceOf[java.lang.Integer])
}
- Log.d(TAG, "got %s(%d, %d)%s -> %s".format(call, lat, lon, sym, comment))
+ Log.d(TAG, "got %s(%d, %d)%s -> %s %s".format(call, lat, lon, sym, comment, wx))
// replace the full station info in stations table
- getWritableDatabase().replaceOrThrow(TABLE, CALL, cv)
+ getWritableDatabase().replaceOrThrow(Station.TABLE, CALL, cv)
}
def isMessageDuplicate(call : String, msgid : String, text : String) : Boolean = {