+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright © 2012 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.altusmetrum.AltosDroid"
- android:versionCode="6"
- android:versionName="1.5">
- <uses-sdk android:targetSdkVersion="10" android:minSdkVersion="10"/>
- <!-- Google Maps -->
- <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
-
- <!-- Permissions needed to access bluetooth -->
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
- <uses-permission android:name="android.permission.BLUETOOTH" />
- <!-- Permissions needed to save Telemetry logs to SD card -->
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <!-- Permissions needed for GoogleMaps -->
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-
- <permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"
- android:protectionLevel="signature"/>
- <uses-permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"/>
-
-
- <application android:label="@string/app_name"
- android:icon="@drawable/app_icon"
- android:allowBackup="true" >
- <activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
- android:label="@string/app_name"
- android:configChanges="orientation|keyboardHidden" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <activity android:name=".DeviceListActivity"
- android:label="@string/select_device"
- android:theme="@android:style/Theme.Dialog"
- android:configChanges="orientation|keyboardHidden" />
-
- <service android:name=".TelemetryService" />
-
- <meta-data android:name="com.google.android.maps.v2.API_KEY"
- android:value="AIzaSyDSr6u4i9TJmVGhgGk4g0wUUhTy9FGyn0s"/>
- <meta-data android:name="com.google.android.gms.version"
- android:value="@integer/google_play_services_version" />
- </application>
-</manifest>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.altusmetrum.AltosDroid"
+ android:versionCode="@ANDROID_VERSION@"
+ android:versionName="@VERSION@">
+ <uses-sdk android:targetSdkVersion="12" android:minSdkVersion="12"/>
+ <!-- Google Maps -->
+ <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
+
+ <!-- Permissions needed to access bluetooth -->
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <!-- Permissions needed to save Telemetry logs to SD card -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <!-- Permissions needed for GoogleMaps -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
+ <permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"
+ android:protectionLevel="signature"/>
+ <uses-permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"/>
+
+ <!-- Permissions needed to access USB OTG -->
+ <uses-feature android:name="android.hardware.usb.host" />
+
+ <application android:label="@string/app_name"
+ android:icon="@drawable/app_icon"
+ android:allowBackup="true"
+ android:theme="@style/CustomTheme">
+ <activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
+ android:label="@string/app_name"
+ android:configChanges="orientation|keyboardHidden"
+ android:launchMode="singleTop">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
+ android:configChanges="orientation|keyboardHidden"
+ android:launchMode="singleTop">
+ <intent-filter>
+ <action
+ android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
+ </intent-filter>
+ <meta-data
+ android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
+ android:resource="@xml/device_filter" />
+ </activity>
+
+ <activity android:name=".DeviceListActivity"
+ android:label="@string/select_device"
+ android:theme="@android:style/Theme.Dialog"
+ android:configChanges="orientation|keyboardHidden" />
+
+ <activity android:name=".PreloadMapActivity"
+ android:label="@string/preload_maps"
+ android:theme="@android:style/Theme.Dialog"
+ android:configChanges="orientation|keyboardHidden" />
+
+ <activity android:name=".MapTypeActivity"
+ android:label="@string/map_type"
+ android:theme="@android:style/Theme.Dialog"
+ android:configChanges="orientation|keyboardHidden" />
+
+ <service android:name=".TelemetryService" />
+
+ <meta-data android:name="com.google.android.maps.v2.API_KEY"
+ android:value="AIzaSyDSr6u4i9TJmVGhgGk4g0wUUhTy9FGyn0s"/>
+ <meta-data android:name="com.google.android.gms.version"
+ android:value="@integer/google_play_services_version" />
+ </application>
+</manifest>
ADB=$(SDK)/platform-tools/adb
AAPT=$(SDK)/platform-tools/aapt
APKBUILDER=$(SDK)/tools/apkbuilder
-ZIPALIGN=$(SDK)/tools/zipalign
+ZIPALIGN_A=$(SDK)/tools/zipalign
+ZIPALIGN_B=$(SDK)/build-tools/*/zipalign
JAVA_SRC_DIR=src/org/altusmetrum/AltosDroid
EXT_LIBDIR=libs
DRAWABLE_DIR=res/drawable
+LAYOUT_DIR=res/layout
+MENU_DIR=res/menu
+VALUES_DIR=res/values
+XML_DIR=res/xml
ALTOSLIB_SRCDIR=../altoslib
ALTOSLIB_JAR=altoslib_$(ALTOSLIB_VERSION).jar
$(DRAWABLE_DIR)/greenled.png \
$(DRAWABLE_DIR)/grayled.png
-SRC=$(JAVA_SRC) $(DRAWABLES)
+LAYOUTS=$(LAYOUT_DIR)/*.xml
+MENUS=$(MENU_DIR)/*.xml
+VALUES=$(VALUES_DIR)/*.xml
+XMLS=$(XML_DIR)/*.xml
+
+RES=$(LAYOUTS) $(MENUS) $(VALUES) $(XMLS)
+
+SRC=$(JAVA_SRC) $(DRAWABLES) $(RES)
all: $(all_target)
.NOTPARALLEL:
-$(EXT_LIBDIR):
+$(ALTOSLIB): $(ALTOSLIB_SRCDIR)/$(ALTOSLIB_JAR)
mkdir -p $(EXT_LIBDIR)
-
-$(ALTOSLIB): $(EXT_LIBDIR) $(ALTOSLIB_SRCDIR)/$(ALTOSLIB_JAR)
cd $(EXT_LIBDIR) && ln -sf $(shell echo $(EXT_LIBDIR) | sed 's|[^/]\+|..|g')/$(ALTOSLIB_SRCDIR)/$(ALTOSLIB_JAR) .
-$(SUPPORT_V4): $(EXT_LIBDIR) $(SUPPORT_V4_SRCDIR)/$(SUPPORT_V4_JAR)
+$(SUPPORT_V4): $(SUPPORT_V4_SRCDIR)/$(SUPPORT_V4_JAR)
+ mkdir -p $(EXT_LIBDIR)
cd $(EXT_LIBDIR) && ln -sf $(SUPPORT_V4_SRCDIR)/$(SUPPORT_V4_JAR) .
$(GOOGLE_PLAY_SERVICES_LIB): $(GOOGLE_PLAY_SERVICES_LIB_SRCDIR)/$(GOOGLE_PLAY_SERVICES_LIB)
-storepass:file ~/altusmetrumllc/google-play-passphrase \
-signedjar bin/AltosDroid-release-signed.apk \
bin/AltosDroid-release-unsigned.apk AltosDroid
- $(ZIPALIGN) -f 4 \
- bin/AltosDroid-release-signed.apk \
- bin/AltosDroid-release.apk
+ if [ -f $(ZIPALIGN_A) ]; then \
+ $(ZIPALIGN_A) -f 4 \
+ bin/AltosDroid-release-signed.apk \
+ bin/AltosDroid-release.apk; \
+ else \
+ $(ZIPALIGN_B) -f 4 \
+ bin/AltosDroid-release-signed.apk \
+ bin/AltosDroid-release.apk; \
+ fi
release: bin/AltosDroid-release.apk
*) Monitor-idle mode
- *) Frequency scanning
+ *) Online maps comes up tracking object at 0,0
- *) Select satellite imaging mode
-
- *) TeleBT battery voltage
-
- *) Deal with long bluetooth list. Currently, a list longer than
- the screen makes it impossible to use entries off the bottom.
-
- *) Pickle/unpickle state instead of reloading entire history from
- file. Current restart time is lengthy.
+ *) Have names for each serial number, default to callsign
Completed features
Done
+ *) Select satellite imaging mode
+
+ Done
+
+ *) Deal with long bluetooth list. Currently, a list longer than
+ the screen makes it impossible to use entries off the bottom.
+
+ Done
+
+ *) Pickle/unpickle state instead of reloading entire history from
+ file. Current restart time is lengthy.
+
+ Done
+
+ *) Offline maps
+
+ Done
+
+ *) Multi-tracker management
+
+ Done
+
+ *) Provide units for age field, turn red if old
+
+ Done
+
+ *) TeleBT battery voltage
+
+ Done
+
+ *) Evaluate performance issues
+
+ Done. Offline maps were duplicating tabs at every redisplay.
+
+ *) Merge offline/online maps into single tab with mode
+
+ Done.
+
+ *) Auto select tracker after long delay
+
+ Done.
+
+ *) Select tracker by clicking map
+
+ Done.
+
+ *) Convert to four tab design:
+
+ Done.
+
+ *) Make voice responses depend on selected tab
+
+ Done.
+
+ *) Monitor TeleMega igniters
+
+ Done. Visible only in Pad tab
+
+ *) Make it harder to switch trackers in map view. Too easy to touch
+ the screen and switch on accident.
+
+ Done. A menu pops up with trackers within a small radius of
+ the touch point, letting you cancel if that wasn't your intent.
+
+ *) Make sure it keeps talking with the screen blanked
+
+ Done. Don't shut down voice when stopping UI.
# project structure.
# Project target.
-target=android-10
+target=android-12
# project structure.
# Project target.
-target=android-10
+target=android-12
android.library.reference.1=google-play-services_lib/
android:textColor="#fff"
android:paddingLeft="5dp"
/>
+ <ListView android:id="@+id/new_devices"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:fadeScrollbars="false"
+ android:scrollbars="vertical"
+ />
<TextView android:id="@+id/title_paired_devices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
<ListView android:id="@+id/paired_devices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:stackFromBottom="true"
android:layout_weight="1"
- />
- <ListView android:id="@+id/new_devices"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:stackFromBottom="true"
- android:layout_weight="2"
+ android:fadeScrollbars="false"
+ android:scrollbars="vertical"
/>
</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright © 2015 Keith Packard <keithp@keithp.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+ <ScrollView android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <LinearLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView android:id="@+id/preload_site_label"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_site_label"
+ />
+ <Spinner android:id="@+id/preload_site_list"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:prompt="@string/preload_site_label"
+ android:spinnerMode="dropdown"
+ />
+ <TextView android:id="@+id/preload_latitude_label"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_latitude_label"
+ />
+ <EditText android:id="@+id/preload_latitude"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/preload_latitude_label"
+ android:inputType="number"/>
+ <TextView android:id="@+id/preload_longitude_label"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_longitude_label"
+ />
+ <EditText android:id="@+id/preload_longitude"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/preload_longitude_label"
+ android:inputType="number"/>
+ <TextView android:id="@+id/preload_types"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_types"
+ />
+ <CheckBox android:id="@+id/preload_hybrid"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_hybrid"
+ />
+ <CheckBox android:id="@+id/preload_satellite"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_satellite"
+ />
+ <CheckBox android:id="@+id/preload_roadmap"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_roadmap"
+ />
+ <CheckBox android:id="@+id/preload_terrain"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_terrain"
+ />
+ <TextView android:id="@+id/preload_min_zoom_label"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_min_zoom"
+ />
+ <Spinner android:id="@+id/preload_min_zoom"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:prompt="@string/preload_min_zoom"
+ android:spinnerMode="dropdown"
+ />
+ <TextView android:id="@+id/preload_max_zoom_label"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_max_zoom"
+ />
+ <Spinner android:id="@+id/preload_max_zoom"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:prompt="@string/preload_max_zoom"
+ android:spinnerMode="dropdown"
+ />
+ <TextView android:id="@+id/preload_radius_label"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_radius"
+ />
+ <Spinner android:id="@+id/preload_radius"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:prompt="@string/preload_radius"
+ android:spinnerMode="dropdown"
+ />
+ <Button android:id="@+id/preload_load"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_load"
+ />
+ <ProgressBar android:id="@+id/preload_progress"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/Widget.ProgressBar.Horizontal"
+ />
+ </LinearLayout>
+ </ScrollView>
+</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright © 2015 Keith Packard <keithp@keithp.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+ <Button android:id="@+id/map_type_hybrid"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_hybrid"
+ android:onClick="selectType"
+ />
+ <Button android:id="@+id/map_type_satellite"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_satellite"
+ android:onClick="selectType"
+ />
+ <Button android:id="@+id/map_type_roadmap"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_roadmap"
+ android:onClick="selectType"
+ />
+ <Button android:id="@+id/map_type_terrain"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/preload_terrain"
+ android:onClick="selectType"
+ />
+</LinearLayout>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright © 2013 Mike Beattie <mike@ethernal.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:baselineAligned="true"
- android:orientation="horizontal"
- android:paddingTop="5dp" >
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
-
- <TextView
- android:id="@+id/height_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/height_label" />
-
- <TextView
- android:id="@+id/height_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/height_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
-
- <TextView
- android:id="@+id/max_height_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/max_height_label" />
-
- <TextView
- android:id="@+id/max_height_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/max_height_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
- </LinearLayout>
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:baselineAligned="true"
- android:orientation="horizontal"
- android:paddingTop="5dp" >
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
-
- <TextView
- android:id="@+id/speed_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/speed_label" />
-
- <TextView
- android:id="@+id/speed_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/speed_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
-
- <TextView
- android:id="@+id/max_speed_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/max_speed_label" />
-
- <TextView
- android:id="@+id/max_speed_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/max_speed_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
- </LinearLayout>
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:baselineAligned="true"
- android:orientation="horizontal"
- android:paddingTop="5dp" >
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
-
- <TextView
- android:id="@+id/accel_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/accel_label" />
-
- <TextView
- android:id="@+id/accel_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/accel_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
-
- <TextView
- android:id="@+id/max_accel_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/max_accel_label" />
-
- <TextView
- android:id="@+id/max_accel_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/max_accel_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
- </LinearLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/lat_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/latitude_label" />
-
- <TextView
- android:id="@+id/lat_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/lat_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/lon_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/longitude_label" />
-
- <TextView
- android:id="@+id/lon_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/lon_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <ImageView
- android:id="@+id/apogee_redled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/apogee_voltage_label"
- android:src="@drawable/grayled" />
-
- <ImageView
- android:id="@+id/apogee_greenled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/apogee_redled"
- android:contentDescription="@string/apogee_voltage_label"
- android:paddingRight="5dp"
- android:src="@drawable/grayled" />
-
- <TextView
- android:id="@+id/apogee_voltage_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/apogee_greenled"
- android:text="@string/apogee_voltage_label" />
-
- <TextView
- android:id="@+id/apogee_voltage_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/apogee_voltage_label"
- android:layout_toRightOf="@id/apogee_greenled"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <ImageView
- android:id="@+id/main_redled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/main_voltage_label"
- android:src="@drawable/grayled" />
-
- <ImageView
- android:id="@+id/main_greenled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/main_redled"
- android:contentDescription="@string/main_voltage_label"
- android:paddingRight="5dp"
- android:src="@drawable/grayled" />
-
- <TextView
- android:id="@+id/main_voltage_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/main_greenled"
- android:text="@string/main_voltage_label" />
-
- <TextView
- android:id="@+id/main_voltage_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/main_voltage_label"
- android:layout_toRightOf="@id/main_greenled"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
-</LinearLayout>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright © 2013 Mike Beattie <mike@ethernal.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:baselineAligned="true"
- android:orientation="horizontal" >
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
-
- <TextView
- android:id="@+id/speed_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/speed_label" />
-
- <TextView
- android:id="@+id/speed_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/speed_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
-
- <TextView
- android:id="@+id/height_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/height_label" />
-
- <TextView
- android:id="@+id/height_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/height_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- </LinearLayout>
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:baselineAligned="true"
- android:orientation="horizontal"
- android:paddingTop="5dp" >
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
-
- <TextView
- android:id="@+id/elevation_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/elevation_label" />
-
- <TextView
- android:id="@+id/elevation_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/elevation_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
-
- <TextView
- android:id="@+id/range_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/range_label" />
-
- <TextView
- android:id="@+id/range_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/range_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- </LinearLayout>
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:baselineAligned="true"
- android:orientation="horizontal"
- android:paddingTop="5dp" >
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
-
- <TextView
- android:id="@+id/bearing_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/bearing_label" />
-
- <TextView
- android:id="@+id/bearing_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/bearing_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
-
- <TextView
- android:id="@+id/compass_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="" />
-
- <TextView
- android:id="@+id/compass_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/compass_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- </LinearLayout>
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:baselineAligned="true"
- android:orientation="horizontal"
- android:paddingTop="5dp" >
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
-
- <TextView
- android:id="@+id/distance_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/gnd_distance_label" />
-
- <TextView
- android:id="@+id/distance_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/distance_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <TextView
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" >
- </TextView>
-
- </LinearLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/lat_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/latitude_label" />
-
- <TextView
- android:id="@+id/lat_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/lat_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/lon_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/longitude_label" />
-
- <TextView
- android:id="@+id/lon_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/lon_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <ImageView
- android:id="@+id/apogee_redled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/apogee_voltage_label"
- android:src="@drawable/grayled" />
-
- <ImageView
- android:id="@+id/apogee_greenled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/apogee_redled"
- android:contentDescription="@string/apogee_voltage_label"
- android:paddingRight="5dp"
- android:src="@drawable/grayled" />
-
- <TextView
- android:id="@+id/apogee_voltage_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/apogee_greenled"
- android:text="@string/apogee_voltage_label" />
-
- <TextView
- android:id="@+id/apogee_voltage_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/apogee_voltage_label"
- android:layout_toRightOf="@id/apogee_greenled"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <ImageView
- android:id="@+id/main_redled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/main_voltage_label"
- android:src="@drawable/grayled" />
-
- <ImageView
- android:id="@+id/main_greenled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/main_redled"
- android:contentDescription="@string/main_voltage_label"
- android:paddingRight="5dp"
- android:src="@drawable/grayled" />
-
- <TextView
- android:id="@+id/main_voltage_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/main_greenled"
- android:text="@string/main_voltage_label" />
-
- <TextView
- android:id="@+id/main_voltage_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/main_voltage_label"
- android:layout_toRightOf="@id/main_greenled"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
-</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright © 2013 Mike Beattie <mike@ethernal.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_weight="0"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:baselineAligned="true"
+ android:orientation="horizontal" >
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" >
+
+ <TextView
+ android:id="@+id/speed_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/speed_label" />
+
+ <TextView
+ android:id="@+id/speed_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/speed_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" >
+
+ <TextView
+ android:id="@+id/height_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/height_label" />
+
+ <TextView
+ android:id="@+id/height_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/height_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:baselineAligned="true"
+ android:orientation="horizontal" >
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" >
+
+ <TextView
+ android:id="@+id/max_speed_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/max_speed_label" />
+
+ <TextView
+ android:id="@+id/max_speed_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/max_speed_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" >
+
+ <TextView
+ android:id="@+id/max_height_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/max_height_label" />
+
+ <TextView
+ android:id="@+id/max_height_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/max_height_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:baselineAligned="true"
+ android:orientation="horizontal"
+ android:paddingTop="5dp" >
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" >
+
+ <TextView
+ android:id="@+id/elevation_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/elevation_label" />
+
+ <TextView
+ android:id="@+id/elevation_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/elevation_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" >
+
+ <TextView
+ android:id="@+id/range_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/range_label" />
+
+ <TextView
+ android:id="@+id/range_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/range_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:baselineAligned="true"
+ android:orientation="horizontal"
+ android:paddingTop="5dp" >
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" >
+
+ <TextView
+ android:id="@+id/bearing_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/bearing_label" />
+
+ <TextView
+ android:id="@+id/bearing_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/bearing_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" >
+
+ <TextView
+ android:id="@+id/compass_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="" />
+
+ <TextView
+ android:id="@+id/compass_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/compass_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:baselineAligned="true"
+ android:orientation="horizontal"
+ android:paddingTop="5dp" >
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" >
+
+ <TextView
+ android:id="@+id/distance_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/gnd_distance_label" />
+
+ <TextView
+ android:id="@+id/distance_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/distance_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" >
+ </TextView>
+
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="5dp" >
+
+ <TextView
+ android:id="@+id/lat_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/latitude_label" />
+
+ <TextView
+ android:id="@+id/lat_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/lat_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="5dp" >
+
+ <TextView
+ android:id="@+id/lon_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longitude_label" />
+
+ <TextView
+ android:id="@+id/lon_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/lon_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:id="@+id/apogee_view"
+ android:visibility="gone"
+ android:layout_gravity="fill"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="5dp" >
+
+ <ImageView
+ android:id="@+id/apogee_redled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/apogee_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <ImageView
+ android:id="@+id/apogee_greenled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/apogee_redled"
+ android:contentDescription="@string/apogee_voltage_label"
+ android:paddingRight="5dp"
+ android:src="@drawable/grayled" />
+
+ <TextView
+ android:id="@+id/apogee_voltage_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/apogee_greenled"
+ android:text="@string/apogee_voltage_label" />
+
+ <TextView
+ android:id="@+id/apogee_voltage_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:id="@+id/main_view"
+ android:visibility="gone"
+ android:layout_gravity="fill"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="5dp" >
+
+ <ImageView
+ android:id="@+id/main_redled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/main_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <ImageView
+ android:id="@+id/main_greenled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/main_redled"
+ android:contentDescription="@string/main_voltage_label"
+ android:paddingRight="5dp"
+ android:src="@drawable/grayled" />
+
+ <TextView
+ android:id="@+id/main_voltage_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/main_greenled"
+ android:text="@string/main_voltage_label" />
+
+ <TextView
+ android:id="@+id/main_voltage_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+ </LinearLayout>
+</LinearLayout>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright © 2013 Mike Beattie <mike@ethernal.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" >
-
- <TextView
- android:id="@+id/bearing_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/bearing_label" />
-
- <TextView
- android:id="@+id/bearing_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@+id/bearing_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/distance_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/distance_label" />
-
- <TextView
- android:id="@+id/distance_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@+id/distance_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/target_lat_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/target_latitude_label" />
-
- <TextView
- android:id="@+id/target_lat_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/target_lat_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/target_lon_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/target_longitude_label" />
-
- <TextView
- android:id="@+id/target_lon_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/target_lon_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/receiver_lat_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/receiver_latitude_label" />
-
- <TextView
- android:id="@+id/receiver_lat_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/receiver_lat_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/receiver_lon_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/receiver_longitude_label" />
-
- <TextView
- android:id="@+id/receiver_lon_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/receiver_lon_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/max_height_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/max_height_label" />
-
- <TextView
- android:id="@+id/max_height_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/max_height_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/max_speed_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/max_speed_label" />
-
- <TextView
- android:id="@+id/max_speed_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/max_speed_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/max_accel_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/max_accel_label" />
-
- <TextView
- android:id="@+id/max_accel_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/max_accel_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
-</LinearLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/customTabLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView
+ android:id="@+id/tabLabel"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:textSize="13dp"
+ android:textColor="#ffffff"
+ android:gravity="center_horizontal"
+ android:background="#808080"
+ android:layout_centerVertical="true"
+ android:layout_centerHorizontal="true" />
+</RelativeLayout>
android:layout_height="match_parent"
android:orientation="vertical" >
- <LinearLayout
- android:id="@+id/map"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="0dp"
- android:layout_weight="1">
-
- </LinearLayout>
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:baselineAligned="true"
- android:orientation="horizontal" >
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/distance_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="4dp"
- android:text="@string/distance_label" />
-
- <TextView
- android:id="@+id/distance_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@+id/distance_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/bearing_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="4dp"
- android:text="@string/bearing_label" />
-
- <TextView
- android:id="@+id/bearing_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@+id/bearing_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- </LinearLayout>
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:baselineAligned="true"
- android:orientation="horizontal" >
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/target_lat_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="4dp"
- android:text="@string/target_latitude_label" />
-
- <TextView
- android:id="@+id/target_lat_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/target_lat_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/target_lon_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="4dp"
- android:text="@string/target_longitude_label" />
-
- <TextView
- android:id="@+id/target_lon_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/target_lon_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
- </LinearLayout>
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:baselineAligned="true"
- android:orientation="horizontal" >
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/receiver_lat_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="4dp"
- android:text="@string/receiver_latitude_label" />
-
- <TextView
- android:id="@+id/receiver_lat_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/receiver_lat_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingTop="5dp" >
-
- <TextView
- android:id="@+id/receiver_lon_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="4dp"
- android:text="@string/receiver_longitude_label" />
-
- <TextView
- android:id="@+id/receiver_lon_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/receiver_lon_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
- </LinearLayout>
-</LinearLayout>
\ No newline at end of file
+ <FrameLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:baselineAligned="true"
+ android:orientation="horizontal"
+ android:layout_weight="1">
+
+ <LinearLayout
+ android:id="@+id/map_online"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1">
+ </LinearLayout>
+
+ <org.altusmetrum.AltosDroid.AltosMapOffline
+ android:id="@+id/map_offline"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ </FrameLayout>
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:baselineAligned="true"
+ android:orientation="horizontal" >
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="5dp" >
+
+ <TextView
+ android:id="@+id/distance_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:text="@string/distance_label" />
+
+ <TextView
+ android:id="@+id/distance_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/distance_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="5dp" >
+
+ <TextView
+ android:id="@+id/bearing_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:text="@string/bearing_label" />
+
+ <TextView
+ android:id="@+id/bearing_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/bearing_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:baselineAligned="true"
+ android:orientation="horizontal" >
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="5dp" >
+
+ <TextView
+ android:id="@+id/target_lat_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:text="@string/target_latitude_label" />
+
+ <TextView
+ android:id="@+id/target_lat_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/target_lat_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="5dp" >
+
+ <TextView
+ android:id="@+id/target_lon_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:text="@string/target_longitude_label" />
+
+ <TextView
+ android:id="@+id/target_lon_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/target_lon_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:baselineAligned="true"
+ android:orientation="horizontal" >
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="5dp" >
+
+ <TextView
+ android:id="@+id/receiver_lat_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:text="@string/receiver_latitude_label" />
+
+ <TextView
+ android:id="@+id/receiver_lat_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/receiver_lat_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="5dp" >
+
+ <TextView
+ android:id="@+id/receiver_lon_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:text="@string/receiver_longitude_label" />
+
+ <TextView
+ android:id="@+id/receiver_lon_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/receiver_lon_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+ </LinearLayout>
+</LinearLayout>
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:orientation="vertical" >
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" >
-
- <ImageView
- android:id="@+id/battery_redled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/battery_voltage_label"
- android:src="@drawable/grayled" />
-
- <ImageView
- android:id="@+id/battery_greenled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/battery_redled"
- android:contentDescription="@string/battery_voltage_label"
- android:paddingRight="5dp"
- android:src="@drawable/grayled" />
-
- <TextView
- android:id="@+id/battery_voltage_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/battery_greenled"
- android:text="@string/battery_voltage_label" />
-
- <TextView
- android:id="@+id/battery_voltage_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/battery_voltage_label"
- android:layout_toRightOf="@id/battery_greenled"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <ImageView
- android:id="@+id/apogee_redled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/apogee_voltage_label"
- android:src="@drawable/grayled" />
-
- <ImageView
- android:id="@+id/apogee_greenled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/apogee_redled"
- android:contentDescription="@string/apogee_voltage_label"
- android:paddingRight="5dp"
- android:src="@drawable/grayled" />
-
- <TextView
- android:id="@+id/apogee_voltage_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/apogee_greenled"
- android:text="@string/apogee_voltage_label" />
-
- <TextView
- android:id="@+id/apogee_voltage_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/apogee_voltage_label"
- android:layout_toRightOf="@id/apogee_greenled"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <ImageView
- android:id="@+id/main_redled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/main_voltage_label"
- android:src="@drawable/grayled" />
-
- <ImageView
- android:id="@+id/main_greenled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/main_redled"
- android:contentDescription="@string/main_voltage_label"
- android:paddingRight="5dp"
- android:src="@drawable/grayled" />
-
- <TextView
- android:id="@+id/main_voltage_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/main_greenled"
- android:text="@string/main_voltage_label" />
-
- <TextView
- android:id="@+id/main_voltage_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/main_voltage_label"
- android:layout_toRightOf="@id/main_greenled"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <ImageView
- android:id="@+id/logging_redled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/logging_label"
- android:src="@drawable/grayled" />
-
- <ImageView
- android:id="@+id/logging_greenled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/logging_redled"
- android:contentDescription="@string/logging_label"
- android:paddingRight="5dp"
- android:src="@drawable/grayled" />
-
- <TextView
- android:id="@+id/logging_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/logging_greenled"
- android:text="@string/logging_label" />
-
- <TextView
- android:id="@+id/logging_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/logging_label"
- android:layout_toRightOf="@id/logging_greenled"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <ImageView
- android:id="@+id/gps_locked_redled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/gps_locked_label"
- android:src="@drawable/grayled" />
-
- <ImageView
- android:id="@+id/gps_locked_greenled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/gps_locked_redled"
- android:contentDescription="@string/gps_locked_label"
- android:paddingRight="5dp"
- android:src="@drawable/grayled" />
-
- <TextView
- android:id="@+id/gps_locked_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/gps_locked_greenled"
- android:text="@string/gps_locked_label" />
-
- <TextView
- android:id="@+id/gps_locked_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/gps_locked_label"
- android:layout_toRightOf="@id/gps_locked_greenled"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="5dp" >
-
- <ImageView
- android:id="@+id/gps_ready_redled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/gps_ready_label"
- android:src="@drawable/grayled" />
-
- <ImageView
- android:id="@+id/gps_ready_greenled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/gps_ready_redled"
- android:contentDescription="@string/gps_ready_label"
- android:paddingRight="5dp"
- android:src="@drawable/grayled" />
-
- <TextView
- android:id="@+id/gps_ready_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/gps_ready_greenled"
- android:text="@string/gps_ready_label" />
-
- <TextView
- android:id="@+id/gps_ready_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/gps_ready_label"
- android:layout_toRightOf="@id/gps_ready_greenled"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="69dp">
-
- <TextView
- android:id="@+id/pad_lat_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:width="100sp"
- android:paddingRight="10sp"
- android:layout_toRightOf="@id/gps_ready_greenled"
- android:text="@string/pad_lat_label" />
-
- <TextView
- android:id="@+id/pad_lat_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/pad_lat_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="69dp">
-
- <TextView
- android:id="@+id/pad_lon_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:width="100sp"
- android:paddingRight="10sp"
- android:layout_toRightOf="@id/gps_ready_greenled"
- android:text="@string/pad_lon_label" />
-
- <TextView
- android:id="@+id/pad_lon_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/pad_lon_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="69dp">
-
- <TextView
- android:id="@+id/pad_alt_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:width="100sp"
- android:paddingRight="10sp"
- android:layout_toRightOf="@id/gps_ready_greenled"
- android:text="@string/pad_alt_label" />
-
- <TextView
- android:id="@+id/pad_alt_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/pad_alt_label"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
-
-</LinearLayout>
\ No newline at end of file
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TableLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:stretchColumns="2,3"
+ android:layout_weight="0"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TableRow
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/battery_redled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/battery_voltage_label"
+ android:src="@drawable/grayled"
+ />
+
+ <ImageView
+ android:id="@+id/battery_greenled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/battery_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <TextView
+ android:id="@+id/battery_voltage_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/battery_voltage_label" />
+
+ <TextView
+ android:id="@+id/battery_voltage_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+ </TableRow>
+
+ <TableRow
+ android:id="@+id/receiver_row"
+ android:visibility="gone"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/receiver_redled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/receiver_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <ImageView
+ android:id="@+id/receiver_greenled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/receiver_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <TextView
+ android:id="@+id/receiver_voltage_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/receiver_voltage_label" />
+
+ <TextView
+ android:id="@+id/receiver_voltage_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </TableRow>
+
+ <TableRow
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <ImageView
+ android:id="@+id/logging_redled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/logging_label"
+ android:src="@drawable/grayled" />
+
+ <ImageView
+ android:id="@+id/logging_greenled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/logging_label"
+ android:src="@drawable/grayled" />
+
+ <TextView
+ android:id="@+id/logging_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/logging_label" />
+
+ <TextView
+ android:id="@+id/logging_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/logging_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </TableRow>
+
+ <TableRow
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <ImageView
+ android:id="@+id/gps_locked_redled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/gps_locked_label"
+ android:src="@drawable/grayled" />
+
+ <ImageView
+ android:id="@+id/gps_locked_greenled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/gps_locked_label"
+ android:src="@drawable/grayled" />
+
+ <TextView
+ android:id="@+id/gps_locked_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/gps_locked_label" />
+
+ <TextView
+ android:id="@+id/gps_locked_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/gps_locked_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </TableRow>
+
+ <TableRow
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <ImageView
+ android:id="@+id/gps_ready_redled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/gps_ready_label"
+ android:src="@drawable/grayled" />
+
+ <ImageView
+ android:id="@+id/gps_ready_greenled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/gps_ready_label"
+ android:src="@drawable/grayled" />
+
+ <TextView
+ android:id="@+id/gps_ready_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/gps_ready_label" />
+
+ <TextView
+ android:id="@+id/gps_ready_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/gps_ready_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </TableRow>
+
+ <TableRow
+ android:id="@+id/apogee_row"
+ android:visibility="gone"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/apogee_redled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/apogee_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <ImageView
+ android:id="@+id/apogee_greenled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/apogee_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <TextView
+ android:id="@+id/apogee_voltage_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/apogee_voltage_label" />
+
+ <TextView
+ android:id="@+id/apogee_voltage_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </TableRow>
+
+ <TableRow
+ android:id="@+id/main_row"
+ android:visibility="gone"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <ImageView
+ android:id="@+id/main_redled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/main_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <ImageView
+ android:id="@+id/main_greenled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/main_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <TextView
+ android:id="@+id/main_voltage_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/main_voltage_label" />
+
+ <TextView
+ android:id="@+id/main_voltage_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </TableRow>
+
+ <TableRow
+ android:id="@+id/ignite_a_row"
+ android:visibility="gone"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <ImageView
+ android:id="@+id/ignite_a_redled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/ignite_a_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <ImageView
+ android:id="@+id/ignite_a_greenled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/ignite_a_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <TextView
+ android:id="@+id/ignite_a_voltage_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/ignite_a_voltage_label" />
+
+ <TextView
+ android:id="@+id/ignite_a_voltage_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </TableRow>
+
+ <TableRow
+ android:id="@+id/ignite_b_row"
+ android:visibility="gone"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <ImageView
+ android:id="@+id/ignite_b_redled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/ignite_b_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <ImageView
+ android:id="@+id/ignite_b_greenled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/ignite_b_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <TextView
+ android:id="@+id/ignite_b_voltage_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/ignite_b_voltage_label" />
+
+ <TextView
+ android:id="@+id/ignite_b_voltage_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </TableRow>
+
+ <TableRow
+ android:id="@+id/ignite_c_row"
+ android:visibility="gone"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <ImageView
+ android:id="@+id/ignite_c_redled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/ignite_c_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <ImageView
+ android:id="@+id/ignite_c_greenled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/ignite_c_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <TextView
+ android:id="@+id/ignite_c_voltage_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/ignite_c_voltage_label" />
+
+ <TextView
+ android:id="@+id/ignite_c_voltage_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </TableRow>
+
+ <TableRow
+ android:id="@+id/ignite_d_row"
+ android:visibility="gone"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <ImageView
+ android:id="@+id/ignite_d_redled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/ignite_d_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <ImageView
+ android:id="@+id/ignite_d_greenled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/ignite_d_voltage_label"
+ android:src="@drawable/grayled" />
+
+ <TextView
+ android:id="@+id/ignite_d_voltage_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/ignite_d_voltage_label" />
+
+ <TextView
+ android:id="@+id/ignite_d_voltage_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </TableRow>
+
+ <TableRow
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/receiver_lat_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_column="2"
+ android:text="@string/receiver_latitude_label" />
+
+ <TextView
+ android:id="@+id/receiver_lat_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </TableRow>
+
+ <TableRow
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/receiver_lon_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_column="2"
+ android:text="@string/receiver_longitude_label" />
+
+ <TextView
+ android:id="@+id/receiver_lon_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </TableRow>
+
+ <TableRow
+ android:padding="2dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/receiver_alt_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_column="2"
+ android:text="@string/receiver_altitude_label" />
+
+ <TextView
+ android:id="@+id/receiver_alt_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </TableRow>
+ </TableLayout>
+</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright © 2013 Mike Beattie <mike@ethernal.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_weight="0"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <RelativeLayout
+ android:layout_gravity="fill"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:id="@+id/bearing_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/bearing_label" />
+
+ <TextView
+ android:id="@+id/bearing_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/bearing_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_gravity="fill"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:id="@+id/direction_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/direction_label" />
+
+ <TextView
+ android:id="@+id/direction_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/direction_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_gravity="fill"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/distance_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/distance_label" />
+
+ <TextView
+ android:id="@+id/distance_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/distance_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_gravity="fill"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/target_lat_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/target_latitude_label" />
+
+ <TextView
+ android:id="@+id/target_lat_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/target_lat_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_gravity="fill"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/target_lon_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/target_longitude_label" />
+
+ <TextView
+ android:id="@+id/target_lon_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/target_lon_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_gravity="fill"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/receiver_lat_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/receiver_latitude_label" />
+
+ <TextView
+ android:id="@+id/receiver_lat_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/receiver_lat_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_gravity="fill"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/receiver_lon_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/receiver_longitude_label" />
+
+ <TextView
+ android:id="@+id/receiver_lon_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/receiver_lon_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_gravity="fill"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/max_height_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/max_height_label" />
+
+ <TextView
+ android:id="@+id/max_height_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/max_height_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_gravity="fill"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/max_speed_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/max_speed_label" />
+
+ <TextView
+ android:id="@+id/max_speed_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/max_speed_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_gravity="fill"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/max_accel_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/max_accel_label" />
+
+ <TextView
+ android:id="@+id/max_accel_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/max_accel_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+ </LinearLayout>
+</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+ Copyright © 2015 Keith Packard <keithp@keithp.com>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
- http://www.apache.org/licenses/LICENSE-2.0
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/connect_scan"
<item android:id="@+id/disconnect"
android:icon="@android:drawable/ic_notification_clear_all"
android:title="@string/disconnect_device" />
- <item android:id="@+id/quit"
- android:icon="@android:drawable/ic_menu_close_clear_cancel"
- android:title="@string/quit" />
<item android:id="@+id/select_freq"
android:icon="@android:drawable/ic_menu_preferences"
android:title="@string/select_freq" />
<item android:id="@+id/change_units"
android:icon="@android:drawable/ic_menu_view"
android:title="@string/change_units" />
+ <item android:id="@+id/preload_maps"
+ android:icon="@android:drawable/ic_menu_mapmode"
+ android:title="@string/preload_maps" />
+ <item android:id="@+id/map_type"
+ android:icon="@android:drawable/ic_menu_mapmode"
+ android:title="@string/map_type" />
+ <item android:id="@+id/map_source"
+ android:icon="@android:drawable/ic_menu_mapmode"
+ android:title="@string/map_source" />
+ <item android:id="@+id/select_tracker"
+ android:icon="@android:drawable/ic_menu_view"
+ android:title="@string/select_tracker"/>
+ <item android:id="@+id/delete_track"
+ android:icon="@android:drawable/ic_notification_clear_all"
+ android:title="@string/delete_track"/>
+ <item android:id="@+id/quit"
+ android:icon="@android:drawable/ic_menu_close_clear_cancel"
+ android:title="@string/quit" />
</menu>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright © 2015 Keith Packard <keithp@keithp.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<resources>
+ <color name="old_color">#ffff4040</color>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+ <style name="CustomTheme" parent="android:Theme.Holo">
+ </style>
+</resources>
<string name="select_freq">Select radio frequency</string>
<string name="select_rate">Select data rate</string>
<string name="change_units">Change units</string>
+ <string name="preload_maps">Load Maps</string>
+ <string name="select_tracker">Select Tracker</string>
+ <string name="delete_track">Delete Track</string>
+ <string name="map_type">Map Type</string>
+ <string name="map_source">Toggle Online/Offline maps</string>
+
+ <!-- MapTypeActivity -->
+ <string name="map_type">Map Type</string>
<!-- DeviceListActivity -->
<string name="scanning">scanning for devices…</string>
<string name="speed_label">Speed</string>
<string name="accel_label">Acceleration</string>
<string name="bearing_label">Bearing</string>
+ <string name="direction_label">Direction</string>
<string name="elevation_label">Elevation</string>
<string name="range_label">Range</string>
<string name="distance_label">Distance</string>
<string name="max_height_label">Max Height</string>
<string name="max_speed_label">Max Speed</string>
<string name="max_accel_label">Max Accel</string>
- <string name="battery_voltage_label">Battery Voltage</string>
- <string name="apogee_voltage_label">Apogee Igniter Voltage</string>
- <string name="main_voltage_label">Main Igniter Voltage</string>
- <string name="logging_label">On-board Data Logging</string>
+ <string name="battery_voltage_label">Battery</string>
+ <string name="receiver_voltage_label">Receiver Battery</string>
+ <string name="apogee_voltage_label">Apogee Igniter</string>
+ <string name="main_voltage_label">Main Igniter</string>
+ <string name="ignite_a_voltage_label">Igniter A</string>
+ <string name="ignite_b_voltage_label">Igniter B</string>
+ <string name="ignite_c_voltage_label">Igniter C</string>
+ <string name="ignite_d_voltage_label">Igniter D</string>
+ <string name="logging_label">Data Logging</string>
<string name="gps_locked_label">GPS Locked</string>
<string name="gps_ready_label">GPS Ready</string>
<string name="latitude_label">Latitude</string>
<string name="target_longitude_label">Tar Lon</string>
<string name="receiver_latitude_label">My Lat</string>
<string name="receiver_longitude_label">My Lon</string>
- <string name="pad_lat_label">Pad Lat</string>
- <string name="pad_lon_label">Pad Lon</string>
- <string name="pad_alt_label">Pad Alt</string>
+ <string name="receiver_altitude_label">My Alt</string>
+
+ <!-- Map preload -->
+ <string name="preload_site_label">Known Launch Sites</string>
+ <string name="preload_latitude_label">Latitude</string>
+ <string name="preload_longitude_label">Longitude</string>
+ <string name="preload_types">Map Types</string>
+ <string name="preload_hybrid">Hybrid</string>
+ <string name="preload_satellite">Satellite</string>
+ <string name="preload_roadmap">Roadmap</string>
+ <string name="preload_terrain">Terrain</string>
+ <string name="preload_min_zoom">Minimum Zoom</string>
+ <string name="preload_max_zoom">Maximum Zoom</string>
+ <string name="preload_radius">Radius</string>
+
+ <string name="preload_load">Load Map</string>
</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+ <usb-device vendor-id="65534" />
+ <usb-device vendor-id="1027" />
+</resources>
//import android.os.Bundle;
import android.os.Handler;
//import android.os.Message;
-import android.util.Log;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
-public class AltosBluetooth extends AltosLink {
-
- // Debugging
- private static final String TAG = "AltosBluetooth";
- private static final boolean D = true;
+public class AltosBluetooth extends AltosDroidLink {
private ConnectThread connect_thread = null;
- private Thread input_thread = null;
-
- private Handler handler;
- private BluetoothAdapter adapter;
- private BluetoothDevice device;
+ private BluetoothDevice device;
private BluetoothSocket socket;
private InputStream input;
private OutputStream output;
+ private boolean pause;
// Constructor
- public AltosBluetooth(BluetoothDevice in_device, Handler in_handler) {
-// set_debug(D);
- adapter = BluetoothAdapter.getDefaultAdapter();
- device = in_device;
- handler = in_handler;
+ public AltosBluetooth(BluetoothDevice device, Handler handler, boolean pause) {
+ super(handler);
+ this.device = device;
+ this.handler = handler;
+ this.pause = pause;
- connect_thread = new ConnectThread(device);
+ connect_thread = new ConnectThread();
connect_thread.start();
-
}
- private void connected() {
+ void connected() {
+ if (closed()) {
+ AltosDebug.debug("connected after closed");
+ return;
+ }
+
+ AltosDebug.check_ui("connected\n");
try {
synchronized(this) {
if (socket != null) {
input = socket.getInputStream();
output = socket.getOutputStream();
-
- input_thread = new Thread(this);
- input_thread.start();
-
- // Configure the newly connected device for telemetry
- print("~\nE 0\n");
- set_monitor(false);
- if (D) Log.d(TAG, "ConnectThread: connected");
-
- /* Let TelemetryService know we're connected
- */
- handler.obtainMessage(TelemetryService.MSG_CONNECTED).sendToTarget();
-
- /* Notify other waiting threads that we're connected now
- */
- notifyAll();
+ super.connected();
}
}
+ } catch (InterruptedException ie) {
+ connect_failed();
} catch (IOException io) {
connect_failed();
}
}
private void connect_failed() {
- synchronized (this) {
- if (socket != null) {
- try {
- socket.close();
- } catch (IOException e2) {
- if (D) Log.e(TAG, "ConnectThread: Failed to close() socket after failed connection");
- }
- socket = null;
- }
- input = null;
- output = null;
- handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED).sendToTarget();
- if (D) Log.e(TAG, "ConnectThread: Failed to establish connection");
+ if (closed()) {
+ AltosDebug.debug("connect_failed after closed");
+ return;
}
+
+ close_device();
+ input = null;
+ output = null;
+ handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED, this).sendToTarget();
+ AltosDebug.error("ConnectThread: Failed to establish connection");
}
- private Object closing_lock = new Object();
- private boolean closing = false;
+ void close_device() {
+ BluetoothSocket tmp_socket;
+
+ synchronized(this) {
+ tmp_socket = socket;
+ socket = null;
+ }
- private void disconnected() {
- synchronized(closing_lock) {
- if (D) Log.e(TAG, String.format("Connection lost during I/O. Closing %b", closing));
- if (!closing) {
- if (D) Log.d(TAG, "Sending disconnected message");
- handler.obtainMessage(TelemetryService.MSG_DISCONNECTED).sendToTarget();
+ if (tmp_socket != null) {
+ try {
+ tmp_socket.close();
+ } catch (IOException e) {
+ AltosDebug.error("close_socket failed");
}
}
}
- private class ConnectThread extends Thread {
- private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
+ public void close() {
+ super.close();
+ input = null;
+ output = null;
+ }
- public ConnectThread(BluetoothDevice device) {
- BluetoothSocket tmp_socket = null;
+ private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
- try {
- tmp_socket = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
- } catch (IOException e) {
- e.printStackTrace();
- }
+ private void create_socket(BluetoothDevice device) {
+
+ BluetoothSocket tmp_socket = null;
+
+ AltosDebug.check_ui("create_socket\n");
+ try {
+ tmp_socket = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ if (socket != null) {
+ AltosDebug.debug("Socket already allocated %s", socket.toString());
+ close_device();
+ }
+ synchronized (this) {
socket = tmp_socket;
}
+ }
+
+ private class ConnectThread extends Thread {
public void run() {
- if (D) Log.d(TAG, "ConnectThread: BEGIN");
+ AltosDebug.debug("ConnectThread: BEGIN (pause %b)", pause);
setName("ConnectThread");
+ if (pause) {
+ try {
+ Thread.sleep(4000);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ create_socket(device);
// Always cancel discovery because it will slow down a connection
- adapter.cancelDiscovery();
+ try {
+ BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
+ } catch (Exception e) {
+ AltosDebug.debug("cancelDiscovery exception %s", e.toString());
+ }
- BluetoothSocket local_socket;
+ BluetoothSocket local_socket = null;
- try {
- synchronized (AltosBluetooth.this) {
+ synchronized (AltosBluetooth.this) {
+ if (!closed())
local_socket = socket;
- }
+ }
- if (local_socket != null) {
+ if (local_socket != null) {
+ try {
// Make a connection to the BluetoothSocket
// This is a blocking call and will only return on a
// successful connection or an exception
local_socket.connect();
+ } catch (Exception e) {
+ AltosDebug.debug("Connect exception %s", e.toString());
+ try {
+ local_socket.close();
+ } catch (Exception ce) {
+ AltosDebug.debug("Close exception %s", ce.toString());
+ }
+ local_socket = null;
}
+ }
+ if (local_socket != null) {
connected();
-
- } catch (IOException e) {
+ } else {
connect_failed();
}
- synchronized (AltosBluetooth.this) {
- /* Reset the ConnectThread because we're done
- */
- connect_thread = null;
- }
- if (D) Log.d(TAG, "ConnectThread: Connect completed");
- }
-
- public void cancel() {
- try {
- BluetoothSocket local_socket;
- synchronized(AltosBluetooth.this) {
- local_socket = socket;
- socket = null;
- }
- if (local_socket != null)
- local_socket.close();
-
- } catch (IOException e) {
- if (D) Log.e(TAG, "ConnectThread: close() of connect socket failed", e);
- }
+ AltosDebug.debug("ConnectThread: completed");
}
}
- public double frequency() {
- return frequency;
- }
-
- public int telemetry_rate() {
- return telemetry_rate;
- }
-
- public void save_frequency() {
- AltosPreferences.set_frequency(0, frequency);
- }
-
- public void save_telemetry_rate() {
- AltosPreferences.set_telemetry_rate(0, telemetry_rate);
- }
-
private synchronized void wait_connected() throws InterruptedException, IOException {
+ AltosDebug.check_ui("wait_connected\n");
if (input == null && socket != null) {
- if (D) Log.d(TAG, "wait_connected...");
+ AltosDebug.debug("wait_connected...");
wait();
- if (D) Log.d(TAG, "wait_connected done");
+ AltosDebug.debug("wait_connected done");
}
if (socket == null)
throw new IOException();
}
- public void print(String data) {
- byte[] bytes = data.getBytes();
- if (D) Log.d(TAG, "print(): begin");
+ int write(byte[] buffer, int len) {
try {
- wait_connected();
- output.write(bytes);
- if (D) Log.d(TAG, "print(): Wrote bytes: '" + data.replace('\n', '\\') + "'");
- } catch (IOException e) {
- disconnected();
- } catch (InterruptedException e) {
- disconnected();
+ output.write(buffer, 0, len);
+ } catch (IOException ie) {
+ return -1;
}
+ return len;
}
- public void putchar(byte c) {
- byte[] bytes = { c };
- if (D) Log.d(TAG, "print(): begin");
+ int read(byte[] buffer, int len) {
try {
- wait_connected();
- output.write(bytes);
- if (D) Log.d(TAG, "print(): Wrote byte: '" + c + "'");
- } catch (IOException e) {
- disconnected();
- } catch (InterruptedException e) {
- disconnected();
+ return input.read(buffer, 0, len);
+ } catch (IOException ie) {
+ return -1;
}
}
- private static final int buffer_size = 1024;
-
- private byte[] buffer = new byte[buffer_size];
- private int buffer_len = 0;
- private int buffer_off = 0;
-
- public int getchar() {
- while (buffer_off == buffer_len) {
- try {
- wait_connected();
- buffer_len = input.read(buffer);
- buffer_off = 0;
- } catch (IOException e) {
- if (D) Log.d(TAG, "getchar IOException");
- disconnected();
- return AltosLink.ERROR;
- } catch (java.lang.InterruptedException e) {
- if (D) Log.d(TAG, "getchar Interrupted");
- disconnected();
- return AltosLink.ERROR;
- }
- }
- return buffer[buffer_off++];
- }
-
- public void closing() {
- synchronized(closing_lock) {
- if (D) Log.d(TAG, "Marked closing true");
- closing = true;
- }
- }
-
-
- public void close() {
- if (D) Log.d(TAG, "close(): begin");
-
- closing();
-
- synchronized(this) {
- if (D) Log.d(TAG, "close(): synched");
-
- if (socket != null) {
- if (D) Log.d(TAG, "close(): Closing socket");
- try {
- socket.close();
- } catch (IOException e) {
- if (D) Log.e(TAG, "close(): unable to close() socket");
- }
- socket = null;
- }
- connect_thread = null;
- if (input_thread != null) {
- if (D) Log.d(TAG, "close(): stopping input_thread");
- try {
- if (D) Log.d(TAG, "close(): input_thread.interrupt().....");
- input_thread.interrupt();
- if (D) Log.d(TAG, "close(): input_thread.join().....");
- input_thread.join();
- } catch (Exception e) {}
- input_thread = null;
- }
- input = null;
- output = null;
- notifyAll();
- }
- }
-
-
- // We override this method so that we can add some debugging. Not 100% elegant, but more useful
- // than debugging one char at a time above in getchar()!
- public void add_reply(AltosLine line) throws InterruptedException {
- if (D) Log.d(TAG, String.format("Got REPLY: %s", line.line));
- super.add_reply(line);
- }
-
- //public void flush_output() { super.flush_output(); }
-
// Stubs of required methods when extending AltosLink
public boolean can_cancel_reply() { return false; }
public boolean show_reply_timeout() { return true; }
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+package org.altusmetrum.AltosDroid;
+
+import java.util.Arrays;
+import java.io.*;
+import java.lang.*;
+
+import org.altusmetrum.altoslib_7.*;
+
+import android.app.Activity;
+import android.graphics.*;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.view.*;
+import android.widget.*;
+import android.location.Location;
+import android.content.*;
+import android.util.Log;
+import android.os.*;
+import android.content.pm.*;
+
+public class AltosDebug {
+ // Debugging
+ static final String TAG = "AltosDroid";
+
+ static boolean D = true;
+
+ static void init(Context context) {
+ ApplicationInfo app_info = context.getApplicationInfo();
+
+ if ((app_info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+ Log.d(TAG, "Enable debugging\n");
+ D = true;
+ } else {
+ Log.d(TAG, "Disable debugging\n");
+ D = false;
+ }
+ }
+
+
+ static void info(String format, Object ... arguments) {
+ Log.i(TAG, String.format(format, arguments));
+ }
+
+ static void debug(String format, Object ... arguments) {
+ if (D)
+ Log.d(TAG, String.format(format, arguments));
+ }
+
+ static void error(String format, Object ... arguments) {
+ Log.e(TAG, String.format(format, arguments));
+ }
+
+ static void check_ui(String format, Object ... arguments) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ Log.e(TAG, String.format("ON UI THREAD " + format, arguments));
+ for (StackTraceElement el : Thread.currentThread().getStackTrace())
+ Log.e(TAG, "\t" + el.toString() + "\n");
+ }
+ }
+}
package org.altusmetrum.AltosDroid;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Timer;
-import java.util.TimerTask;
+import java.text.*;
+import java.util.*;
+import java.io.*;
import android.app.Activity;
+import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
+import android.content.res.Resources;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.Window;
-import android.view.View;
-import android.widget.TabHost;
-import android.widget.TextView;
-import android.widget.RelativeLayout;
-import android.widget.Toast;
+import android.view.*;
+import android.widget.*;
import android.app.AlertDialog;
import android.location.Location;
+import android.hardware.usb.*;
+import android.graphics.*;
+import android.graphics.drawable.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
- // Debugging
- static final String TAG = "AltosDroid";
- static final boolean D = true;
+
+ // Actions sent to the telemetry server at startup time
+
+ public static final String ACTION_BLUETOOTH = "org.altusmetrum.AltosDroid.BLUETOOTH";
+ public static final String ACTION_USB = "org.altusmetrum.AltosDroid.USB";
// Message types received by our Handler
// Intent request codes
public static final int REQUEST_CONNECT_DEVICE = 1;
public static final int REQUEST_ENABLE_BT = 2;
+ public static final int REQUEST_PRELOAD_MAPS = 3;
+ public static final int REQUEST_MAP_TYPE = 4;
+
+ public int map_type = AltosMap.maptype_hybrid;
public static FragmentManager fm;
private BluetoothAdapter mBluetoothAdapter = null;
- // Layout Views
- private TextView mTitle;
-
// Flight state values
private TextView mCallsignView;
private TextView mRSSIView;
private RelativeLayout mStateLayout;
private TextView mStateView;
private TextView mAgeView;
+ private boolean mAgeViewOld;
+ private int mAgeNewColor;
+ private int mAgeOldColor;
+
+ public static final String tab_pad_name = "pad";
+ public static final String tab_flight_name = "flight";
+ public static final String tab_recover_name = "recover";
+ public static final String tab_map_name = "map";
// field to display the version at the bottom of the screen
private TextView mVersion;
// Timer and Saved flight state for Age calculation
private Timer timer;
AltosState saved_state;
+ TelemetryState telemetry_state;
+ Integer[] serials;
+
+ UsbDevice pending_usb_device;
+ boolean start_with_usb;
// Service
private boolean mIsBound = false;
switch (msg.what) {
case MSG_STATE:
- if(D) Log.d(TAG, "MSG_STATE");
- TelemetryState telemetry_state = (TelemetryState) msg.obj;
- if (telemetry_state == null) {
- Log.d(TAG, "telemetry_state null!");
+ if (msg.obj == null) {
+ AltosDebug.debug("telemetry_state null!");
return;
}
-
- ad.update_state(telemetry_state);
+ ad.update_state((TelemetryState) msg.obj);
break;
case MSG_UPDATE_AGE:
- if(D) Log.d(TAG, "MSG_UPDATE_AGE");
ad.update_age();
break;
}
} catch (RemoteException e) {
// In this case the service has crashed before we could even do anything with it
}
+ if (pending_usb_device != null) {
+ try {
+ mService.send(Message.obtain(null, TelemetryService.MSG_OPEN_USB, pending_usb_device));
+ pending_usb_device = null;
+ } catch (RemoteException e) {
+ }
+ }
}
public void onServiceDisconnected(ComponentName className) {
if (telemetry_state.telemetry_rate != AltosLib.ao_telemetry_rate_38400)
str = str.concat(String.format(" %d bps",
AltosLib.ao_telemetry_rate_values[telemetry_state.telemetry_rate]));
- mTitle.setText(str);
+ setTitle(str);
} else {
- mTitle.setText(R.string.title_connected_to);
+ setTitle(R.string.title_connected_to);
}
break;
case TelemetryState.CONNECT_CONNECTING:
if (telemetry_state.address != null)
- mTitle.setText(String.format("Connecting to %s...", telemetry_state.address.name));
+ setTitle(String.format("Connecting to %s...", telemetry_state.address.name));
else
- mTitle.setText("Connecting to something...");
+ setTitle("Connecting to something...");
break;
case TelemetryState.CONNECT_DISCONNECTED:
case TelemetryState.CONNECT_NONE:
- mTitle.setText(R.string.title_not_connected);
+ setTitle(R.string.title_not_connected);
break;
}
}
}
}
+ int selected_serial = 0;
+ int current_serial;
+ long switch_time;
+
+ void set_switch_time() {
+ switch_time = System.currentTimeMillis();
+ selected_serial = 0;
+ }
+
boolean registered_units_listener;
- void update_state(TelemetryState telemetry_state) {
+ void update_state(TelemetryState new_telemetry_state) {
+
+ if (new_telemetry_state != null)
+ telemetry_state = new_telemetry_state;
+
+ if (selected_serial != 0)
+ current_serial = selected_serial;
+
+ if (current_serial == 0)
+ current_serial = telemetry_state.latest_serial;
if (!registered_units_listener) {
registered_units_listener = true;
AltosPreferences.register_units_listener(this);
}
+ serials = telemetry_state.states.keySet().toArray(new Integer[0]);
+ Arrays.sort(serials);
+
update_title(telemetry_state);
- update_ui(telemetry_state.state, telemetry_state.location);
- if (telemetry_state.connect == TelemetryState.CONNECT_CONNECTED)
- start_timer();
+
+ AltosState state = null;
+ boolean aged = true;
+
+ if (telemetry_state.states.containsKey(current_serial)) {
+ state = telemetry_state.states.get(current_serial);
+ int age = state_age(state);
+ if (age < 20)
+ aged = false;
+ if (current_serial == selected_serial)
+ aged = false;
+ else if (switch_time != 0 && (switch_time - state.received_time) > 0)
+ aged = true;
+ }
+
+ if (aged) {
+ AltosState newest_state = null;
+ int newest_age = 0;
+
+ for (int serial : telemetry_state.states.keySet()) {
+ AltosState existing = telemetry_state.states.get(serial);
+ int existing_age = state_age(existing);
+
+ if (newest_state == null || existing_age < newest_age) {
+ newest_state = existing;
+ newest_age = existing_age;
+ }
+ }
+
+ if (newest_state != null)
+ state = newest_state;
+ }
+
+ update_ui(telemetry_state, state, telemetry_state.location);
+
+ start_timer();
}
boolean same_string(String a, String b) {
}
}
+
+ private int blend_component(int a, int b, double r, int shift, int mask) {
+ return ((int) (((a >> shift) & mask) * r + ((b >> shift) & mask) * (1 - r)) & mask) << shift;
+ }
+ private int blend_color(int a, int b, double r) {
+ return (blend_component(a, b, r, 0, 0xff) |
+ blend_component(a, b, r, 8, 0xff) |
+ blend_component(a, b, r, 16, 0xff) |
+ blend_component(a, b, r, 24, 0xff));
+ }
+
+ int state_age(AltosState state) {
+ return (int) ((System.currentTimeMillis() - state.received_time + 500) / 1000);
+ }
+
+ void set_screen_on(int age) {
+ if (age < 60)
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ else
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+
void update_age() {
- if (saved_state != null)
- mAgeView.setText(String.format("%d", (System.currentTimeMillis() - saved_state.received_time + 500) / 1000));
+ if (saved_state != null) {
+ int age = state_age(saved_state);
+
+ double age_scale = age / 100.0;
+
+ if (age_scale > 1.0)
+ age_scale = 1.0;
+
+ mAgeView.setTextColor(blend_color(mAgeOldColor, mAgeNewColor, age_scale));
+
+ set_screen_on(age);
+
+ String text;
+ if (age < 60)
+ text = String.format("%ds", age);
+ else if (age < 60 * 60)
+ text = String.format("%dm", age / 60);
+ else if (age < 60 * 60 * 24)
+ text = String.format("%dh", age / (60 * 60));
+ else
+ text = String.format("%dd", age / (24 * 60 * 60));
+ mAgeView.setText(text);
+ }
}
- void update_ui(AltosState state, Location location) {
+ void update_ui(TelemetryState telem_state, AltosState state, Location location) {
int prev_state = AltosLib.ao_flight_invalid;
prev_state = saved_state.state;
if (state != null) {
+ set_screen_on(state_age(state));
+
if (state.state == AltosLib.ao_flight_stateless) {
boolean prev_locked = false;
boolean locked = false;
if (prev_locked != locked) {
String currentTab = mTabHost.getCurrentTabTag();
if (locked) {
- if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent");
+ if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name);
} else {
- if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("pad");
+ if (currentTab.equals(tab_flight_name)) mTabHost.setCurrentTabByTag(tab_pad_name);
}
}
} else {
String currentTab = mTabHost.getCurrentTabTag();
switch (state.state) {
case AltosLib.ao_flight_boost:
- if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("ascent");
- break;
- case AltosLib.ao_flight_drogue:
- if (currentTab.equals("ascent")) mTabHost.setCurrentTabByTag("descent");
+ if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name);
break;
case AltosLib.ao_flight_landed:
- if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("landed");
+ if (currentTab.equals(tab_flight_name)) mTabHost.setCurrentTabByTag(tab_recover_name);
break;
case AltosLib.ao_flight_stateless:
- if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent");
+ if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name);
break;
}
}
}
for (AltosDroidTab mTab : mTabs)
- mTab.update_ui(state, from_receiver, location, mTab == mTabsAdapter.currentItem());
+ mTab.update_ui(telem_state, state, from_receiver, location, mTab == mTabsAdapter.currentItem());
- if (state != null && mAltosVoice != null)
- mAltosVoice.tell(state, from_receiver);
+ if (mAltosVoice != null)
+ mAltosVoice.tell(telem_state, state, from_receiver, location, (AltosDroidTab) mTabsAdapter.currentItem());
saved_state = state;
}
return String.format(format, value);
}
+ private View create_tab_view(String label) {
+ LayoutInflater inflater = (LayoutInflater) this.getLayoutInflater();
+ View tab_view = inflater.inflate(R.layout.tab_layout, null);
+ TextView text_view = (TextView) tab_view.findViewById (R.id.tabLabel);
+ text_view.setText(label);
+ return tab_view;
+ }
+
+ public void set_map_source(int source) {
+ for (AltosDroidTab mTab : mTabs)
+ mTab.set_map_source(source);
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if(D) Log.e(TAG, "+++ ON CREATE +++");
-
- // Get local Bluetooth adapter
- mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ AltosDebug.init(this);
+ AltosDebug.debug("+++ ON CREATE +++");
- // If the adapter is null, then Bluetooth is not supported
- if (mBluetoothAdapter == null) {
- Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
- finish();
- }
+ // Initialise preferences
+ AltosDroidPreferences.init(this);
fm = getSupportFragmentManager();
// Set up the window layout
- requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.altosdroid);
- getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);
// Create the Tabs and ViewPager
mTabHost = (TabHost)findViewById(android.R.id.tabhost);
mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
- mTabsAdapter.addTab(mTabHost.newTabSpec("pad").setIndicator("Pad"), TabPad.class, null);
- mTabsAdapter.addTab(mTabHost.newTabSpec("ascent").setIndicator("Ascent"), TabAscent.class, null);
- mTabsAdapter.addTab(mTabHost.newTabSpec("descent").setIndicator("Descent"), TabDescent.class, null);
- mTabsAdapter.addTab(mTabHost.newTabSpec("landed").setIndicator("Landed"), TabLanded.class, null);
- mTabsAdapter.addTab(mTabHost.newTabSpec("map").setIndicator("Map"), TabMap.class, null);
-
-
- // Scale the size of the Tab bar for different screen densities
- // This probably won't be needed when we start supporting ICS+ tabs.
- DisplayMetrics metrics = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getMetrics(metrics);
- int density = metrics.densityDpi;
-
- if (density==DisplayMetrics.DENSITY_XHIGH)
- tabHeight = 65;
- else if (density==DisplayMetrics.DENSITY_HIGH)
- tabHeight = 45;
- else if (density==DisplayMetrics.DENSITY_MEDIUM)
- tabHeight = 35;
- else if (density==DisplayMetrics.DENSITY_LOW)
- tabHeight = 25;
- else
- tabHeight = 65;
-
- for (int i = 0; i < 5; i++)
- mTabHost.getTabWidget().getChildAt(i).getLayoutParams().height = tabHeight;
-
- // Set up the custom title
- mTitle = (TextView) findViewById(R.id.title_left_text);
- mTitle.setText(R.string.app_name);
- mTitle = (TextView) findViewById(R.id.title_right_text);
+ mTabsAdapter.addTab(mTabHost.newTabSpec(tab_pad_name).setIndicator(create_tab_view("Pad")), TabPad.class, null);
+ mTabsAdapter.addTab(mTabHost.newTabSpec(tab_flight_name).setIndicator(create_tab_view("Flight")), TabFlight.class, null);
+ mTabsAdapter.addTab(mTabHost.newTabSpec(tab_recover_name).setIndicator(create_tab_view("Recover")), TabRecover.class, null);
+ mTabsAdapter.addTab(mTabHost.newTabSpec(tab_map_name).setIndicator(create_tab_view("Map")), TabMap.class, null);
// Display the Version
mVersion = (TextView) findViewById(R.id.version);
mStateLayout = (RelativeLayout) findViewById(R.id.state_container);
mStateView = (TextView) findViewById(R.id.state_value);
mAgeView = (TextView) findViewById(R.id.age_value);
+ mAgeNewColor = mAgeView.getTextColors().getDefaultColor();
+ mAgeOldColor = getResources().getColor(R.color.old_color);
}
- @Override
- public void onStart() {
- super.onStart();
- if(D) Log.e(TAG, "++ ON START ++");
+ private boolean ensureBluetooth() {
+ // Get local Bluetooth adapter
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- // Start Telemetry Service
- startService(new Intent(AltosDroid.this, TelemetryService.class));
+ // If the adapter is null, then Bluetooth is not supported
+ if (mBluetoothAdapter == null) {
+ Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
+ return false;
+ }
if (!mBluetoothAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, AltosDroid.REQUEST_ENABLE_BT);
}
+ return true;
+ }
+
+ private boolean check_usb() {
+ UsbDevice device = AltosUsb.find_device(this, AltosLib.product_basestation);
+
+ if (device != null) {
+ Intent i = new Intent(this, AltosDroid.class);
+ PendingIntent pi = PendingIntent.getActivity(this, 0, new Intent("hello world", null, this, AltosDroid.class), 0);
+
+ if (AltosUsb.request_permission(this, device, pi)) {
+ connectUsb(device);
+ }
+ start_with_usb = true;
+ return true;
+ }
+
+ start_with_usb = false;
+
+ return false;
+ }
+
+ private void noticeIntent(Intent intent) {
+
+ /* Ok, this is pretty convenient.
+ *
+ * When a USB device is plugged in, and our 'hotplug'
+ * intent registration fires, we get an Intent with
+ * EXTRA_DEVICE set.
+ *
+ * When we start up and see a usb device and request
+ * permission to access it, that queues a
+ * PendingIntent, which has the EXTRA_DEVICE added in,
+ * along with the EXTRA_PERMISSION_GRANTED field as
+ * well.
+ *
+ * So, in both cases, we get the device name using the
+ * same call. We check to see if access was granted,
+ * in which case we ignore the device field and do our
+ * usual startup thing.
+ */
+
+ UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ boolean granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+
+ AltosDebug.debug("intent %s device %s granted %s", intent, device, granted);
+
+ if (!granted)
+ device = null;
+
+ if (device != null) {
+ AltosDebug.debug("intent has usb device " + device.toString());
+ connectUsb(device);
+ } else {
+
+ /* 'granted' is only false if this intent came
+ * from the request_permission call and
+ * permission was denied. In which case, we
+ * don't want to loop forever...
+ */
+ if (granted) {
+ AltosDebug.debug("check for a USB device at startup");
+ if (check_usb())
+ return;
+ }
+ AltosDebug.debug("Starting by looking for bluetooth devices");
+ if (ensureBluetooth())
+ return;
+ finish();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ AltosDebug.debug("++ ON START ++");
+
+ set_switch_time();
+
+ noticeIntent(getIntent());
+
+ // Start Telemetry Service
+ String action = start_with_usb ? ACTION_USB : ACTION_BLUETOOTH;
+
+ startService(new Intent(action, null, AltosDroid.this, TelemetryService.class));
+
doBindService();
if (mAltosVoice == null)
mAltosVoice = new AltosVoice(this);
+
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ AltosDebug.debug("onNewIntent");
+ noticeIntent(intent);
}
@Override
public void onResume() {
super.onResume();
- if(D) Log.e(TAG, "+ ON RESUME +");
+ AltosDebug.debug("+ ON RESUME +");
}
@Override
public void onPause() {
super.onPause();
- if(D) Log.e(TAG, "- ON PAUSE -");
+ AltosDebug.debug("- ON PAUSE -");
}
@Override
public void onStop() {
super.onStop();
- if(D) Log.e(TAG, "-- ON STOP --");
-
- doUnbindService();
- if (mAltosVoice != null) {
- mAltosVoice.stop();
- mAltosVoice = null;
- }
+ AltosDebug.debug("-- ON STOP --");
}
@Override
public void onDestroy() {
super.onDestroy();
- if(D) Log.e(TAG, "--- ON DESTROY ---");
+ AltosDebug.debug("--- ON DESTROY ---");
- if (mAltosVoice != null) mAltosVoice.stop();
+ doUnbindService();
+ if (mAltosVoice != null) {
+ mAltosVoice.stop();
+ mAltosVoice = null;
+ }
stop_timer();
}
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if(D) Log.d(TAG, "onActivityResult " + resultCode);
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ AltosDebug.debug("onActivityResult " + resultCode);
switch (requestCode) {
case REQUEST_CONNECT_DEVICE:
// When DeviceListActivity returns with a device to connect to
//setupChat();
} else {
// User did not enable Bluetooth or an error occured
- Log.e(TAG, "BT not enabled");
+ AltosDebug.error("BT not enabled");
stopService(new Intent(AltosDroid.this, TelemetryService.class));
Toast.makeText(this, R.string.bt_not_enabled, Toast.LENGTH_SHORT).show();
finish();
}
break;
+ case REQUEST_MAP_TYPE:
+ if (resultCode == Activity.RESULT_OK)
+ set_map_type(data);
+ break;
+ }
+ }
+
+ private void connectUsb(UsbDevice device) {
+ if (mService == null)
+ pending_usb_device = device;
+ else {
+ // Attempt to connect to the device
+ try {
+ mService.send(Message.obtain(null, TelemetryService.MSG_OPEN_USB, device));
+ AltosDebug.debug("Sent OPEN_USB message");
+ } catch (RemoteException e) {
+ AltosDebug.debug("connect device message failed");
+ }
}
}
String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
String name = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_NAME);
- if (D) Log.d(TAG, "Connecting to " + address + name);
+ AltosDebug.debug("Connecting to " + address + " " + name);
DeviceAddress a = new DeviceAddress(address, name);
mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, a));
+ AltosDebug.debug("Sent connecting message");
} catch (RemoteException e) {
+ AltosDebug.debug("connect device message failed");
}
}
}
}
+ private void set_map_type(Intent data) {
+ int type = data.getIntExtra(MapTypeActivity.EXTRA_MAP_TYPE, -1);
+
+ AltosDebug.debug("intent set_map_type %d\n", type);
+ if (type != -1) {
+ map_type = type;
+ for (AltosDroidTab mTab : mTabs)
+ mTab.set_map_type(map_type);
+ }
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
void setFrequency(double freq) {
try {
mService.send(Message.obtain(null, TelemetryService.MSG_SETFREQUENCY, freq));
+ set_switch_time();
} catch (RemoteException e) {
}
}
void setFrequency(String freq) {
try {
- setFrequency (Double.parseDouble(freq.substring(11, 17)));
- } catch (NumberFormatException e) {
+ setFrequency (AltosParse.parse_double_net(freq.substring(11, 17)));
+ } catch (ParseException e) {
}
}
void setBaud(int baud) {
try {
mService.send(Message.obtain(null, TelemetryService.MSG_SETBAUD, baud));
+ set_switch_time();
} catch (RemoteException e) {
}
}
}
}
+ void select_tracker(int serial) {
+ int i;
+
+ AltosDebug.debug("select tracker %d\n", serial);
+
+ if (serial == selected_serial) {
+ AltosDebug.debug("%d already selected\n", serial);
+ return;
+ }
+
+ if (serial != 0) {
+ for (i = 0; i < serials.length; i++)
+ if (serials[i] == serial)
+ break;
+
+ if (i == serials.length) {
+ AltosDebug.debug("attempt to select unknown tracker %d\n", serial);
+ return;
+ }
+ }
+
+ current_serial = selected_serial = serial;
+ update_state(null);
+ }
+
+ void touch_trackers(Integer[] serials) {
+ AlertDialog.Builder builder_tracker = new AlertDialog.Builder(this);
+ builder_tracker.setTitle("Select Tracker");
+ final String[] trackers = new String[serials.length + 1];
+ trackers[0] = "Auto";
+ for (int i = 0; i < serials.length; i++)
+ trackers[i+1] = String.format("%d", serials[i]);
+ builder_tracker.setItems(trackers,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ if (item == 0)
+ select_tracker(0);
+ else
+ select_tracker(Integer.parseInt(trackers[item]));
+ }
+ });
+ AlertDialog alert_tracker = builder_tracker.create();
+ alert_tracker.show();
+ }
+
+ void delete_track(int serial) {
+ try {
+ mService.send(Message.obtain(null, TelemetryService.MSG_DELETE_SERIAL, (Integer) serial));
+ } catch (Exception ex) {
+ }
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Intent serverIntent = null;
switch (item.getItemId()) {
case R.id.connect_scan:
- // Launch the DeviceListActivity to see devices and do scan
- serverIntent = new Intent(this, DeviceListActivity.class);
- startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
+ if (ensureBluetooth()) {
+ // Launch the DeviceListActivity to see devices and do scan
+ serverIntent = new Intent(this, DeviceListActivity.class);
+ startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
+ }
return true;
case R.id.disconnect:
- /* Disconnect the bluetooth device
+ /* Disconnect the device
*/
disconnectDevice();
return true;
case R.id.quit:
- Log.d(TAG, "R.id.quit");
+ AltosDebug.debug("R.id.quit");
disconnectDevice();
finish();
return true;
boolean imperial = AltosPreferences.imperial_units();
AltosPreferences.set_imperial_units(!imperial);
return true;
+ case R.id.preload_maps:
+ serverIntent = new Intent(this, PreloadMapActivity.class);
+ startActivityForResult(serverIntent, REQUEST_PRELOAD_MAPS);
+ return true;
+ case R.id.map_type:
+ serverIntent = new Intent(this, MapTypeActivity.class);
+ startActivityForResult(serverIntent, REQUEST_MAP_TYPE);
+ return true;
+ case R.id.map_source:
+ int source = AltosDroidPreferences.map_source();
+ int new_source = source == AltosDroidPreferences.MAP_SOURCE_ONLINE ? AltosDroidPreferences.MAP_SOURCE_OFFLINE : AltosDroidPreferences.MAP_SOURCE_ONLINE;
+ AltosDroidPreferences.set_map_source(new_source);
+ set_map_source(new_source);
+ return true;
+ case R.id.select_tracker:
+ if (serials != null) {
+ String[] trackers = new String[serials.length+1];
+ trackers[0] = "Auto";
+ for (int i = 0; i < serials.length; i++)
+ trackers[i+1] = String.format("%d", serials[i]);
+ AlertDialog.Builder builder_serial = new AlertDialog.Builder(this);
+ builder_serial.setTitle("Select a tracker");
+ builder_serial.setItems(trackers,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ if (item == 0)
+ select_tracker(0);
+ else
+ select_tracker(serials[item-1]);
+ }
+ });
+ AlertDialog alert_serial = builder_serial.create();
+ alert_serial.show();
+
+ }
+ return true;
+ case R.id.delete_track:
+ if (serials != null) {
+ String[] trackers = new String[serials.length];
+ for (int i = 0; i < serials.length; i++)
+ trackers[i] = String.format("%d", serials[i]);
+ AlertDialog.Builder builder_serial = new AlertDialog.Builder(this);
+ builder_serial.setTitle("Delete a track");
+ builder_serial.setItems(trackers,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ delete_track(serials[item]);
+ }
+ });
+ AlertDialog alert_serial = builder_serial.create();
+ alert_serial.show();
+
+ }
+ return true;
}
return false;
}
+ static String direction(AltosGreatCircle from_receiver,
+ Location receiver) {
+ if (from_receiver == null)
+ return null;
+
+ if (receiver == null)
+ return null;
+
+ if (!receiver.hasBearing())
+ return null;
+
+ float bearing = receiver.getBearing();
+ float heading = (float) from_receiver.bearing - bearing;
+
+ while (heading <= -180.0f)
+ heading += 360.0f;
+ while (heading > 180.0f)
+ heading -= 360.0f;
+
+ int iheading = (int) (heading + 0.5f);
+
+ if (-1 < iheading && iheading < 1)
+ return "ahead";
+ else if (iheading < -179 || 179 < iheading)
+ return "backwards";
+ else if (iheading < 0)
+ return String.format("left %d°", -iheading);
+ else
+ return String.format("right %d°", iheading);
+ }
}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.UUID;
+
+import android.os.Handler;
+
+import org.altusmetrum.altoslib_7.*;
+
+public abstract class AltosDroidLink extends AltosLink {
+
+ Handler handler;
+
+ Thread input_thread = null;
+
+ public double frequency() {
+ return frequency;
+ }
+
+ public int telemetry_rate() {
+ return telemetry_rate;
+ }
+
+ public void save_frequency() {
+ AltosPreferences.set_frequency(0, frequency);
+ }
+
+ public void save_telemetry_rate() {
+ AltosPreferences.set_telemetry_rate(0, telemetry_rate);
+ }
+
+ Object closed_lock = new Object();
+ boolean closing = false;
+ boolean closed = false;
+
+ public boolean closed() {
+ synchronized(closed_lock) {
+ return closing;
+ }
+ }
+
+ void connected() throws InterruptedException {
+ input_thread = new Thread(this);
+ input_thread.start();
+
+ // Configure the newly connected device for telemetry
+ print("~\nE 0\n");
+ set_monitor(false);
+ AltosDebug.debug("ConnectThread: connected");
+
+ /* Let TelemetryService know we're connected
+ */
+ handler.obtainMessage(TelemetryService.MSG_CONNECTED, this).sendToTarget();
+
+ /* Notify other waiting threads that we're connected now
+ */
+ notifyAll();
+ }
+
+ public void closing() {
+ synchronized(closed_lock) {
+ AltosDebug.debug("Marked closing true");
+ closing = true;
+ }
+ }
+
+ private boolean actually_closed() {
+ synchronized(closed_lock) {
+ return closed;
+ }
+ }
+
+ abstract void close_device();
+
+ public void close() {
+ AltosDebug.debug("close(): begin");
+
+ closing();
+
+ flush_output();
+
+ synchronized (closed_lock) {
+ AltosDebug.debug("Marked closed true");
+ closed = true;
+ }
+
+ close_device();
+
+ synchronized(this) {
+
+ if (input_thread != null) {
+ AltosDebug.debug("close(): stopping input_thread");
+ try {
+ AltosDebug.debug("close(): input_thread.interrupt().....");
+ input_thread.interrupt();
+ AltosDebug.debug("close(): input_thread.join().....");
+ input_thread.join();
+ } catch (Exception e) {}
+ input_thread = null;
+ }
+ notifyAll();
+ }
+ }
+
+ abstract int write(byte[] buffer, int len);
+
+ abstract int read(byte[] buffer, int len);
+
+ private static final int buffer_size = 64;
+
+ private byte[] in_buffer = new byte[buffer_size];
+ private byte[] out_buffer = new byte[buffer_size];
+ private int buffer_len = 0;
+ private int buffer_off = 0;
+ private int out_buffer_off = 0;
+
+ private byte[] debug_chars = new byte[buffer_size];
+ private int debug_off;
+
+ private void debug_input(byte b) {
+ if (b == '\n') {
+ AltosDebug.debug(" " + new String(debug_chars, 0, debug_off));
+ debug_off = 0;
+ } else {
+ if (debug_off < buffer_size)
+ debug_chars[debug_off++] = b;
+ }
+ }
+
+ private void disconnected() {
+ if (closed()) {
+ AltosDebug.debug("disconnected after closed");
+ return;
+ }
+
+ AltosDebug.debug("Sending disconnected message");
+ handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
+ }
+
+ public int getchar() {
+
+ if (actually_closed())
+ return ERROR;
+
+ while (buffer_off == buffer_len) {
+ buffer_len = read(in_buffer, buffer_size);
+ if (buffer_len < 0) {
+ AltosDebug.debug("ERROR returned from getchar()");
+ disconnected();
+ return ERROR;
+ }
+ buffer_off = 0;
+ }
+ if (AltosDebug.D)
+ debug_input(in_buffer[buffer_off]);
+ return in_buffer[buffer_off++];
+ }
+
+ public void flush_output() {
+ super.flush_output();
+
+ if (actually_closed()) {
+ out_buffer_off = 0;
+ return;
+ }
+
+ while (out_buffer_off != 0) {
+ int sent = write(out_buffer, out_buffer_off);
+
+ if (sent <= 0) {
+ AltosDebug.debug("flush_output() failed");
+ out_buffer_off = 0;
+ break;
+ }
+
+ if (sent < out_buffer_off)
+ System.arraycopy(out_buffer, 0, out_buffer, sent, out_buffer_off - sent);
+
+ out_buffer_off -= sent;
+ }
+ }
+
+ public void putchar(byte c) {
+ out_buffer[out_buffer_off++] = c;
+ if (out_buffer_off == buffer_size)
+ flush_output();
+ }
+
+ public void print(String data) {
+ byte[] bytes = data.getBytes();
+ AltosDebug.debug("print(): begin");
+ for (byte b : bytes)
+ putchar(b);
+ AltosDebug.debug("print(): Wrote bytes: '" + data.replace('\n', '\\') + "'");
+ }
+
+ public AltosDroidLink(Handler handler) {
+ this.handler = handler;
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+import java.io.*;
+import android.location.Location;
+import org.altusmetrum.altoslib_7.*;
+
+public interface AltosDroidMapInterface {
+ public void onCreateView(AltosDroid altos_droid);
+
+ public void set_visible(boolean visible);
+
+ public void center(double lat, double lon, double accuracy);
+
+ public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
+}
package org.altusmetrum.AltosDroid;
import android.content.Context;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosDroidPreferences extends AltosPreferences {
static DeviceAddress active_device_address;
+ /* Map source preference name */
+ final static String mapSourcePreference = "MAP-SOURCE";
+
+ static final int MAP_SOURCE_OFFLINE = 0;
+ static final int MAP_SOURCE_ONLINE = 1;
+
+ static int map_source;
+
public static void init(Context context) {
if (backend != null)
return;
if (address != null && name != null)
active_device_address = new DeviceAddress (address, name);
+
+ map_source = backend.getInt(mapSourcePreference, MAP_SOURCE_ONLINE);
}
public static void set_active_device(DeviceAddress address) {
return active_device_address;
}
}
+
+ public static void set_map_source(int map_source) {
+ synchronized(backend) {
+ AltosDroidPreferences.map_source = map_source;
+ backend.putInt(mapSourcePreference, map_source);
+ flush_preferences();
+ }
+ }
+
+ public static int map_source() {
+ synchronized(backend) {
+ return map_source;
+ }
+ }
}
import android.os.Environment;
import android.util.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosDroidPreferencesBackend implements AltosPreferencesBackend {
public final static String NAME = "org.altusmetrum.AltosDroid";
public String[] keys() {
Map<String, ?> all = prefs.getAll();
- return (String[])all.keySet().toArray();
+ Object[] ao = all.keySet().toArray();
+
+ String[] as = new String[ao.length];
+ for (int i = 0; i < ao.length; i++)
+ as[i] = (String) ao[i];
+ return as;
}
public AltosPreferencesBackend node(String key) {
}
public void remove(String key) {
+ AltosDebug.debug("remove preference %s\n", key);
editor.remove(key);
}
package org.altusmetrum.AltosDroid;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
import android.location.Location;
import android.app.Activity;
import android.graphics.Color;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.FragmentManager;
import android.location.Location;
-import android.util.Log;
import android.widget.TextView;
public abstract class AltosDroidTab extends Fragment implements AltosUnitsListener {
+ TelemetryState last_telem_state;
AltosState last_state;
AltosGreatCircle last_from_receiver;
Location last_receiver;
+ AltosDroid altos_droid;
- public abstract void show(AltosState state, AltosGreatCircle from_receiver, Location receiver);
+ public abstract void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
public abstract String tab_name();
+ public void set_map_type(int map_type) {
+ }
+
+ public void set_map_source(int map_source) {
+ }
+
public void units_changed(boolean imperial_units) {
- if (!isHidden() && last_state != null)
- show(last_state, last_from_receiver, last_receiver);
+ if (!isHidden())
+ show(last_telem_state, last_state, last_from_receiver, last_receiver);
}
public void set_value(TextView text_view,
public void set_visible(boolean visible) {
FragmentTransaction ft = AltosDroid.fm.beginTransaction();
+ AltosDebug.debug("set visible %b %s\n", visible, tab_name());
if (visible) {
- AltosState state = last_state;
- AltosGreatCircle from_receiver = last_from_receiver;
- Location receiver = last_receiver;
-
- show(state, from_receiver, receiver);
ft.show(this);
+ show(last_telem_state, last_state, last_from_receiver, last_receiver);
} else
ft.hide(this);
ft.commitAllowingStateLoss();
}
- public void update_ui(AltosState state, AltosGreatCircle from_receiver, Location receiver, boolean is_current) {
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ altos_droid = (AltosDroid) activity;
+ altos_droid.registerTab(this);
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ altos_droid.unregisterTab(this);
+ altos_droid = null;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ AltosDebug.debug("onResume tab %s\n", tab_name());
+ set_visible(true);
+ }
+
+ public void update_ui(TelemetryState telem_state, AltosState state,
+ AltosGreatCircle from_receiver, Location receiver, boolean is_current)
+ {
+ last_telem_state = telem_state;
last_state = state;
last_from_receiver = from_receiver;
last_receiver = receiver;
if (is_current)
- show(state, from_receiver, receiver);
+ show(telem_state, state, from_receiver, receiver);
else
return;
}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+import java.io.*;
+
+import org.altusmetrum.altoslib_7.*;
+
+import android.app.Activity;
+import android.graphics.*;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.view.*;
+import android.widget.*;
+import android.location.Location;
+import android.content.*;
+import android.util.*;
+
+class Rocket implements Comparable {
+ AltosLatLon position;
+ String name;
+ int serial;
+ long last_packet;
+ boolean active;
+ AltosMapOffline map_offline;
+
+ void paint() {
+ map_offline.draw_bitmap(position, map_offline.rocket_bitmap, map_offline.rocket_off_x, map_offline.rocket_off_y);
+ map_offline.draw_text(position, name, 0, 3*map_offline.rocket_bitmap.getHeight()/4);
+ }
+
+ void set_position(AltosLatLon position, long last_packet) {
+ this.position = position;
+ this.last_packet = last_packet;
+ }
+
+ void set_active(boolean active) {
+ this.active = active;
+ }
+
+ public int compareTo(Object o) {
+ Rocket other = (Rocket) o;
+
+ if (active && !other.active)
+ return 1;
+ if (other.active && !active)
+ return -1;
+
+ long diff = last_packet - other.last_packet;
+
+ if (diff > 0)
+ return 1;
+ if (diff < 0)
+ return -1;
+ return 0;
+ }
+
+ Rocket(int serial, AltosMapOffline map_offline) {
+ this.serial = serial;
+ this.name = String.format("%d", serial);
+ this.map_offline = map_offline;
+ }
+}
+
+public class AltosMapOffline extends View implements ScaleGestureDetector.OnScaleGestureListener, AltosMapInterface, AltosDroidMapInterface {
+ ScaleGestureDetector scale_detector;
+ boolean scaling;
+ AltosMap map;
+ AltosDroid altos_droid;
+
+ AltosLatLon here;
+ AltosLatLon there;
+ AltosLatLon pad;
+
+ Canvas canvas;
+ Paint paint;
+
+ Bitmap pad_bitmap;
+ int pad_off_x, pad_off_y;
+ Bitmap rocket_bitmap;
+ int rocket_off_x, rocket_off_y;
+ Bitmap here_bitmap;
+ int here_off_x, here_off_y;
+
+ static final int WHITE = 0xffffffff;
+ static final int RED = 0xffff0000;
+ static final int PINK = 0xffff8080;
+ static final int YELLOW= 0xffffff00;
+ static final int CYAN = 0xff00ffff;
+ static final int BLUE = 0xff0000ff;
+ static final int BLACK = 0xff000000;
+
+ public static final int stateColors[] = {
+ WHITE, // startup
+ WHITE, // idle
+ WHITE, // pad
+ RED, // boost
+ PINK, // fast
+ YELLOW, // coast
+ CYAN, // drogue
+ BLUE, // main
+ BLACK, // landed
+ BLACK, // invalid
+ CYAN, // stateless
+ };
+
+ /* AltosMapInterface */
+ public void debug(String format, Object ... arguments) {
+ AltosDebug.debug(format, arguments);
+ }
+
+ class MapTile extends AltosMapTile {
+ public void paint(AltosMapTransform t) {
+ AltosPointInt pt = new AltosPointInt(t.screen(upper_left));
+
+ if (canvas.quickReject(pt.x, pt.y, pt.x + px_size, pt.y + px_size, Canvas.EdgeType.AA))
+ return;
+
+ AltosImage altos_image = cache.get(this, store, px_size, px_size);
+
+ MapImage map_image = (MapImage) altos_image;
+
+ Bitmap bitmap = null;
+
+ if (map_image != null)
+ bitmap = map_image.bitmap;
+
+ if (bitmap != null) {
+ canvas.drawBitmap(bitmap, pt.x, pt.y, paint);
+ } else {
+ paint.setColor(0xff808080);
+ canvas.drawRect(pt.x, pt.y, pt.x + px_size, pt.y + px_size, paint);
+ if (t.has_location()) {
+ String message = null;
+ switch (status) {
+ case AltosMapTile.loading:
+ message = "Loading...";
+ break;
+ case AltosMapTile.bad_request:
+ message = "Internal error";
+ break;
+ case AltosMapTile.failed:
+ message = "Network error, check connection";
+ break;
+ case AltosMapTile.forbidden:
+ message = "Too many requests, try later";
+ break;
+ }
+ if (message != null) {
+ Rect bounds = new Rect();
+ paint.getTextBounds(message, 0, message.length(), bounds);
+
+ int width = bounds.right - bounds.left;
+ int height = bounds.bottom - bounds.top;
+
+ float x = pt.x + px_size / 2.0f;
+ float y = pt.y + px_size / 2.0f;
+ x = x - width / 2.0f;
+ y = y + height / 2.0f;
+ paint.setColor(0xff000000);
+ canvas.drawText(message, 0, message.length(), x, y, paint);
+ }
+ }
+ }
+ }
+
+ public MapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+ super(listener, upper_left, center, zoom, maptype, px_size, 2);
+ }
+
+ }
+
+ public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+ return new MapTile(listener, upper_left, center, zoom, maptype, px_size);
+ }
+
+ public AltosMapPath new_path() {
+ return null;
+ }
+
+ public AltosMapLine new_line() {
+ return null;
+ }
+
+ class MapImage implements AltosImage {
+ public Bitmap bitmap;
+
+ public void flush() {
+ if (bitmap != null) {
+ bitmap.recycle();
+ bitmap = null;
+ }
+ }
+
+ public MapImage(File file) {
+ bitmap = BitmapFactory.decodeFile(file.getPath());
+ }
+ }
+
+ public AltosImage load_image(File file) throws Exception {
+ return new MapImage(file);
+ }
+
+ class MapMark extends AltosMapMark {
+ public void paint(AltosMapTransform t) {
+ }
+
+ MapMark(double lat, double lon, int state) {
+ super(lat, lon, state);
+ }
+ }
+
+ public AltosMapMark new_mark(double lat, double lon, int state) {
+ return new MapMark(lat, lon, state);
+ }
+
+ public int width() {
+ return getWidth();
+ }
+
+ public int height() {
+ return getHeight();
+ }
+
+ public void repaint() {
+ postInvalidate();
+ }
+
+ public void repaint(AltosRectangle damage) {
+ postInvalidate(damage.x, damage.y, damage.x + damage.width, damage.y + damage.height);
+ }
+
+ public void set_zoom_label(String label) {
+ }
+
+ public void select_object(AltosLatLon latlon) {
+ if (map.transform == null)
+ return;
+ ArrayList<Integer> near = new ArrayList<Integer>();
+
+ for (Rocket rocket : sorted_rockets()) {
+ if (rocket.position == null) {
+ debug("rocket %d has no position\n", rocket.serial);
+ continue;
+ }
+ double distance = map.transform.hypot(latlon, rocket.position);
+ debug("check select %d distance %g width %d\n", rocket.serial, distance, rocket_bitmap.getWidth());
+ if (distance < rocket_bitmap.getWidth() * 2.0) {
+ debug("selecting %d\n", rocket.serial);
+ near.add(rocket.serial);
+ }
+ }
+ if (near.size() != 0)
+ altos_droid.touch_trackers(near.toArray(new Integer[0]));
+ }
+
+ class Line {
+ AltosLatLon a, b;
+
+ void paint() {
+ if (a != null && b != null) {
+ AltosPointDouble a_screen = map.transform.screen(a);
+ AltosPointDouble b_screen = map.transform.screen(b);
+ paint.setColor(0xff8080ff);
+ canvas.drawLine((float) a_screen.x, (float) a_screen.y,
+ (float) b_screen.x, (float) b_screen.y,
+ paint);
+ }
+ }
+
+ void set_a(AltosLatLon a) {
+ this.a = a;
+ }
+
+ void set_b(AltosLatLon b) {
+ this.b = b;
+ }
+
+ Line() {
+ }
+ }
+
+ Line line = new Line();
+
+ int stroke_width = 20;
+
+ void draw_text(AltosLatLon lat_lon, String text, int off_x, int off_y) {
+ if (lat_lon != null && map != null && map.transform != null) {
+ AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
+
+ Rect bounds = new Rect();
+ paint.getTextBounds(text, 0, text.length(), bounds);
+
+ int width = bounds.right - bounds.left;
+ int height = bounds.bottom - bounds.top;
+
+ float x = pt.x;
+ float y = pt.y;
+ x = x - width / 2.0f - off_x;
+ y = y + height / 2.0f - off_y;
+ paint.setColor(0xff000000);
+ canvas.drawText(text, 0, text.length(), x, y, paint);
+ }
+ }
+
+ HashMap<Integer,Rocket> rockets = new HashMap<Integer,Rocket>();
+
+ void draw_bitmap(AltosLatLon lat_lon, Bitmap bitmap, int off_x, int off_y) {
+ if (lat_lon != null && map != null && map.transform != null) {
+ AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
+
+ canvas.drawBitmap(bitmap, pt.x - off_x, pt.y - off_y, paint);
+ }
+ }
+
+ private Rocket[] sorted_rockets() {
+ Rocket[] rocket_array = rockets.values().toArray(new Rocket[0]);
+
+ Arrays.sort(rocket_array);
+ return rocket_array;
+ }
+
+ private void draw_positions() {
+ line.set_a(there);
+ line.set_b(here);
+ line.paint();
+ draw_bitmap(pad, pad_bitmap, pad_off_x, pad_off_y);
+
+ for (Rocket rocket : sorted_rockets())
+ rocket.paint();
+ draw_bitmap(here, here_bitmap, here_off_x, here_off_y);
+ }
+
+ @Override public void invalidate() {
+ Rect r = new Rect();
+ getDrawingRect(r);
+ super.invalidate();
+ }
+
+ @Override public void invalidate(int l, int t, int r, int b) {
+ Rect rect = new Rect();
+ getDrawingRect(rect);
+ super.invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas view_canvas) {
+ if (map == null) {
+ debug("MapView draw without map\n");
+ return;
+ }
+ canvas = view_canvas;
+ paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ paint.setStrokeWidth(stroke_width);
+ paint.setStrokeCap(Paint.Cap.ROUND);
+ paint.setStrokeJoin(Paint.Join.ROUND);
+ paint.setTextSize(40);
+ map.paint();
+ draw_positions();
+ canvas = null;
+ }
+
+ public boolean onScale(ScaleGestureDetector detector) {
+ float f = detector.getScaleFactor();
+
+ if (f <= 0.8) {
+ map.set_zoom_centre(map.get_zoom() - 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY()));
+ return true;
+ }
+ if (f >= 1.2) {
+ map.set_zoom_centre(map.get_zoom() + 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY()));
+ return true;
+ }
+ return false;
+ }
+
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ return true;
+ }
+
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ scale_detector.onTouchEvent(event);
+
+ if (scale_detector.isInProgress()) {
+ scaling = true;
+ }
+
+ if (scaling) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ scaling = false;
+ }
+ return true;
+ }
+
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ map.touch_start((int) event.getX(), (int) event.getY(), true);
+ } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ map.touch_continue((int) event.getX(), (int) event.getY(), true);
+ } else if (event.getAction() == MotionEvent.ACTION_UP) {
+ map.touch_stop((int) event.getX(), (int) event.getY(), true);
+ }
+ return true;
+ }
+
+ double mapAccuracy;
+
+ public void center(double lat, double lon, double accuracy) {
+ if (mapAccuracy <= 0 || accuracy < mapAccuracy/10 || (map != null && !map.has_centre())) {
+ if (map != null)
+ map.maybe_centre(lat, lon);
+ mapAccuracy = accuracy;
+ }
+ }
+
+ public void set_visible(boolean visible) {
+ if (visible)
+ setVisibility(VISIBLE);
+ else
+ setVisibility(GONE);
+ }
+
+ public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+ if (state != null) {
+ map.show(state, null);
+ if (state.pad_lat != AltosLib.MISSING && pad == null)
+ pad = new AltosLatLon(state.pad_lat, state.pad_lon);
+ }
+
+ if (telem_state != null) {
+ Integer[] old_serial = rockets.keySet().toArray(new Integer[0]);
+ Integer[] new_serial = telem_state.states.keySet().toArray(new Integer[0]);
+
+ /* remove deleted keys */
+ for (int serial : old_serial) {
+ if (!telem_state.states.containsKey(serial))
+ rockets.remove(serial);
+ }
+
+ /* set remaining keys */
+
+ for (int serial : new_serial) {
+ Rocket rocket;
+ AltosState t_state = telem_state.states.get(serial);
+ if (rockets.containsKey(serial))
+ rocket = rockets.get(serial);
+ else {
+ rocket = new Rocket(serial, this);
+ rockets.put(serial, rocket);
+ }
+ if (t_state.gps != null) {
+ AltosLatLon latlon = new AltosLatLon(t_state.gps.lat, t_state.gps.lon);
+ rocket.set_position(latlon, t_state.received_time);
+ if (state.serial == serial)
+ there = latlon;
+ }
+ if (state != null)
+ rocket.set_active(state.serial == serial);
+ }
+ }
+ if (receiver != null) {
+ here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
+ }
+ }
+
+ public void onCreateView(AltosDroid altos_droid) {
+ this.altos_droid = altos_droid;
+ map = new AltosMap(this);
+ map.set_maptype(altos_droid.map_type);
+
+ pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad);
+ /* arrow at the bottom of the launchpad image */
+ pad_off_x = pad_bitmap.getWidth() / 2;
+ pad_off_y = pad_bitmap.getHeight();
+
+ rocket_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket);
+ /* arrow at the bottom of the rocket image */
+ rocket_off_x = rocket_bitmap.getWidth() / 2;
+ rocket_off_y = rocket_bitmap.getHeight();
+
+ here_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_maps_indicator_current_position);
+ /* Center of the dot */
+ here_off_x = here_bitmap.getWidth() / 2;
+ here_off_y = here_bitmap.getHeight() / 2;
+ }
+
+ public void set_map_type(int map_type) {
+ if (map != null)
+ map.set_maptype(map_type);
+ }
+
+ public AltosMapOffline(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ this.altos_droid = altos_droid;
+ scale_detector = new ScaleGestureDetector(context, this);
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2013 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+
+import org.altusmetrum.altoslib_7.*;
+
+import com.google.android.gms.maps.*;
+import com.google.android.gms.maps.model.*;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.*;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+//import android.support.v4.app.FragmentTransaction;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.location.Location;
+import android.content.*;
+
+class RocketOnline implements Comparable {
+ Marker marker;
+ int serial;
+ long last_packet;
+ int size;
+
+ void set_position(AltosLatLon position, long last_packet) {
+ marker.setPosition(new LatLng(position.lat, position.lon));
+ this.last_packet = last_packet;
+ }
+
+ private Bitmap rocket_bitmap(Context context, String text) {
+
+ /* From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
+ */
+ Bitmap orig_bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.rocket);
+ Bitmap bitmap = orig_bitmap.copy(Bitmap.Config.ARGB_8888, true);
+
+ Canvas canvas = new Canvas(bitmap);
+ Paint paint = new Paint();
+ paint.setTextSize(40);
+ paint.setColor(0xff000000);
+
+ Rect bounds = new Rect();
+ paint.getTextBounds(text, 0, text.length(), bounds);
+
+ int width = bounds.right - bounds.left;
+ int height = bounds.bottom - bounds.top;
+
+ float x = bitmap.getWidth() / 2.0f - width / 2.0f;
+ float y = bitmap.getHeight() / 2.0f - height / 2.0f;
+
+ size = bitmap.getWidth();
+
+ canvas.drawText(text, 0, text.length(), x, y, paint);
+ return bitmap;
+ }
+
+ public void remove() {
+ marker.remove();
+ }
+
+ public int compareTo(Object o) {
+ RocketOnline other = (RocketOnline) o;
+
+ long diff = last_packet - other.last_packet;
+
+ if (diff > 0)
+ return 1;
+ if (diff < 0)
+ return -1;
+ return 0;
+ }
+
+ RocketOnline(Context context, int serial, GoogleMap map, double lat, double lon, long last_packet) {
+ this.serial = serial;
+ String name = String.format("%d", serial);
+ this.marker = map.addMarker(new MarkerOptions()
+ .icon(BitmapDescriptorFactory.fromBitmap(rocket_bitmap(context, name)))
+ .position(new LatLng(lat, lon))
+ .visible(true));
+ this.last_packet = last_packet;
+ }
+}
+
+public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarkerClickListener, GoogleMap.OnMapClickListener {
+ public SupportMapFragment mMapFragment;
+ private GoogleMap mMap;
+ private boolean mapLoaded = false;
+ Context context;
+
+ private HashMap<Integer,RocketOnline> rockets = new HashMap<Integer,RocketOnline>();
+ private Marker mPadMarker;
+ private boolean pad_set;
+ private Polyline mPolyline;
+
+ private View map_view;
+
+ private double mapAccuracy = -1;
+
+ private AltosLatLon my_position = null;
+ private AltosLatLon target_position = null;
+
+ private AltosDroid altos_droid;
+
+ public void onCreateView(AltosDroid altos_droid) {
+ this.altos_droid = altos_droid;
+ final int map_type = altos_droid.map_type;
+ mMapFragment = new SupportMapFragment() {
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ setupMap(map_type);
+ }
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ map_view = super.onCreateView(inflater, container, savedInstanceState);
+ return map_view;
+ }
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ map_view = null;
+ }
+ };
+ }
+
+// public void onActivityCreated() {
+// getChildFragmentManager().beginTransaction().add(R.id.map, mMapFragment).commit();
+// }
+
+ private double pixel_distance(LatLng a, LatLng b) {
+ Projection projection = mMap.getProjection();
+
+ Point a_pt = projection.toScreenLocation(a);
+ Point b_pt = projection.toScreenLocation(b);
+
+ return Math.hypot((double) (a_pt.x - b_pt.x), (double) (a_pt.y - b_pt.y));
+ }
+
+ private RocketOnline[] sorted_rockets() {
+ RocketOnline[] rocket_array = rockets.values().toArray(new RocketOnline[0]);
+
+ Arrays.sort(rocket_array);
+ return rocket_array;
+ }
+
+ public void onMapClick(LatLng lat_lng) {
+ ArrayList<Integer> near = new ArrayList<Integer>();
+
+ for (RocketOnline rocket : sorted_rockets()) {
+ LatLng pos = rocket.marker.getPosition();
+
+ if (pos == null)
+ continue;
+
+ double distance = pixel_distance(lat_lng, pos);
+ if (distance < rocket.size * 2)
+ near.add(rocket.serial);
+ }
+
+ if (near.size() != 0)
+ altos_droid.touch_trackers(near.toArray(new Integer[0]));
+ }
+
+ public boolean onMarkerClick(Marker marker) {
+ onMapClick(marker.getPosition());
+ return true;
+ }
+
+ public void setupMap(int map_type) {
+ mMap = mMapFragment.getMap();
+ if (mMap != null) {
+ set_map_type(map_type);
+ mMap.setMyLocationEnabled(true);
+ mMap.getUiSettings().setTiltGesturesEnabled(false);
+ mMap.getUiSettings().setZoomControlsEnabled(false);
+ mMap.setOnMarkerClickListener(this);
+ mMap.setOnMapClickListener(this);
+
+ mPadMarker = mMap.addMarker(
+ new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad))
+ .position(new LatLng(0,0))
+ .visible(false)
+ );
+
+ mPolyline = mMap.addPolyline(
+ new PolylineOptions().add(new LatLng(0,0), new LatLng(0,0))
+ .width(20)
+ .color(Color.BLUE)
+ .visible(false)
+ );
+
+ mapLoaded = true;
+ }
+ }
+
+ public void center(double lat, double lon, double accuracy) {
+ if (mMap == null)
+ return;
+
+ if (mapAccuracy < 0 || accuracy < mapAccuracy/10) {
+ mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lon),14));
+ mapAccuracy = accuracy;
+ }
+ }
+
+ private void set_rocket(int serial, AltosState state) {
+ RocketOnline rocket;
+
+ if (state.gps == null || state.gps.lat == AltosLib.MISSING)
+ return;
+
+ if (mMap == null)
+ return;
+
+ if (rockets.containsKey(serial)) {
+ rocket = rockets.get(serial);
+ rocket.set_position(new AltosLatLon(state.gps.lat, state.gps.lon), state.received_time);
+ } else {
+ rocket = new RocketOnline(context,
+ serial,
+ mMap, state.gps.lat, state.gps.lon,
+ state.received_time);
+ rockets.put(serial, rocket);
+ }
+ }
+
+ private void remove_rocket(int serial) {
+ RocketOnline rocket = rockets.get(serial);
+ rocket.remove();
+ rockets.remove(serial);
+ }
+
+ public void set_visible(boolean visible) {
+ if (map_view == null)
+ return;
+ if (visible)
+ map_view.setVisibility(View.VISIBLE);
+ else
+ map_view.setVisibility(View.GONE);
+ }
+
+ public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+
+ if (telem_state != null) {
+ for (int serial : rockets.keySet()) {
+ if (!telem_state.states.containsKey(serial))
+ remove_rocket(serial);
+ }
+
+ for (int serial : telem_state.states.keySet()) {
+ set_rocket(serial, telem_state.states.get(serial));
+ }
+ }
+
+ if (state != null) {
+ if (mapLoaded) {
+ if (!pad_set && state.pad_lat != AltosLib.MISSING) {
+ pad_set = true;
+ mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon));
+ mPadMarker.setVisible(true);
+ }
+ }
+ if (state.gps != null) {
+
+ target_position = new AltosLatLon(state.gps.lat, state.gps.lon);
+ if (state.gps.locked && state.gps.nsat >= 4)
+ center (state.gps.lat, state.gps.lon, 10);
+ }
+ }
+
+ if (receiver != null) {
+ double accuracy;
+
+ if (receiver.hasAccuracy())
+ accuracy = receiver.getAccuracy();
+ else
+ accuracy = 1000;
+
+ my_position = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
+ center (my_position.lat, my_position.lon, accuracy);
+ }
+
+ if (my_position != null && target_position != null && mPolyline != null) {
+ mPolyline.setPoints(Arrays.asList(new LatLng(my_position.lat, my_position.lon), new LatLng(target_position.lat, target_position.lon)));
+ mPolyline.setVisible(true);
+ }
+
+ }
+
+ public void set_map_type(int map_type) {
+ if (mMap != null) {
+ if (map_type == AltosMap.maptype_hybrid)
+ mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
+ else if (map_type == AltosMap.maptype_satellite)
+ mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
+ else if (map_type == AltosMap.maptype_terrain)
+ mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
+ else
+ mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
+ }
+ }
+
+ public AltosMapOnline(Context context) {
+ this.context = context;
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.UUID;
+import java.util.HashMap;
+
+import android.content.Context;
+import android.hardware.usb.*;
+import android.app.*;
+import android.os.Handler;
+
+import org.altusmetrum.altoslib_7.*;
+
+public class AltosUsb extends AltosDroidLink {
+
+ private Thread input_thread = null;
+
+ private Handler handler;
+
+ private UsbManager manager;
+ private UsbDevice device;
+ private UsbDeviceConnection connection;
+ private UsbInterface iface;
+ private UsbEndpoint in, out;
+
+ private InputStream input;
+ private OutputStream output;
+
+ // Constructor
+ public AltosUsb(Context context, UsbDevice device, Handler handler) {
+ super(handler);
+// set_debug(D);
+ this.handler = handler;
+
+ iface = null;
+ in = null;
+ out = null;
+
+ int niface = device.getInterfaceCount();
+
+ for (int i = 0; i < niface; i++) {
+
+ iface = device.getInterface(i);
+
+ in = null;
+ out = null;
+
+ int nendpoints = iface.getEndpointCount();
+
+ for (int e = 0; e < nendpoints; e++) {
+ UsbEndpoint endpoint = iface.getEndpoint(e);
+
+ if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
+ switch (endpoint.getDirection()) {
+ case UsbConstants.USB_DIR_OUT:
+ out = endpoint;
+ break;
+ case UsbConstants.USB_DIR_IN:
+ in = endpoint;
+ break;
+ }
+ }
+ }
+
+ if (in != null && out != null)
+ break;
+ }
+
+ if (in != null && out != null) {
+ AltosDebug.debug("\tin %s out %s\n", in.toString(), out.toString());
+
+ manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+
+ if (manager == null) {
+ AltosDebug.debug("USB_SERVICE failed");
+ return;
+ }
+
+ connection = manager.openDevice(device);
+
+ if (connection == null) {
+ AltosDebug.debug("openDevice failed");
+ return;
+ }
+
+ connection.claimInterface(iface, true);
+
+ input_thread = new Thread(this);
+ input_thread.start();
+
+ // Configure the newly connected device for telemetry
+ print("~\nE 0\n");
+ set_monitor(false);
+ }
+ }
+
+ static private boolean isAltusMetrum(UsbDevice device) {
+ if (device.getVendorId() != AltosLib.vendor_altusmetrum)
+ return false;
+ if (device.getProductId() < AltosLib.product_altusmetrum_min)
+ return false;
+ if (device.getProductId() > AltosLib.product_altusmetrum_max)
+ return false;
+ return true;
+ }
+
+ static boolean matchProduct(int want_product, UsbDevice device) {
+
+ if (!isAltusMetrum(device))
+ return false;
+
+ if (want_product == AltosLib.product_any)
+ return true;
+
+ int have_product = device.getProductId();
+
+ if (want_product == AltosLib.product_basestation)
+ return have_product == AltosLib.product_teledongle ||
+ have_product == AltosLib.product_teleterra ||
+ have_product == AltosLib.product_telebt ||
+ have_product == AltosLib.product_megadongle;
+
+ if (want_product == AltosLib.product_altimeter)
+ return have_product == AltosLib.product_telemetrum ||
+ have_product == AltosLib.product_telemega ||
+ have_product == AltosLib.product_easymega ||
+ have_product == AltosLib.product_telegps ||
+ have_product == AltosLib.product_easymini ||
+ have_product == AltosLib.product_telemini;
+
+ if (have_product == AltosLib.product_altusmetrum) /* old devices match any request */
+ return true;
+
+ if (want_product == have_product)
+ return true;
+
+ return false;
+ }
+
+ static public boolean request_permission(Context context, UsbDevice device, PendingIntent pi) {
+ UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+
+// if (manager.hasPermission(device))
+// return true;
+
+ AltosDebug.debug("request permission for USB device " + device.toString());
+
+ manager.requestPermission(device, pi);
+ return false;
+ }
+
+ static public UsbDevice find_device(Context context, int match_product) {
+ UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+
+ HashMap<String,UsbDevice> devices = manager.getDeviceList();
+
+ for (UsbDevice device : devices.values()) {
+ int vendor = device.getVendorId();
+ int product = device.getProductId();
+
+ if (matchProduct(match_product, device)) {
+ AltosDebug.debug("found USB device " + device.toString());
+ return device;
+ }
+ }
+
+ return null;
+ }
+
+ private void disconnected() {
+ if (closed()) {
+ AltosDebug.debug("disconnected after closed");
+ return;
+ }
+
+ AltosDebug.debug("Sending disconnected message");
+ handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
+ }
+
+ void close_device() {
+ UsbDeviceConnection tmp_connection;
+
+ synchronized(this) {
+ tmp_connection = connection;
+ connection = null;
+ }
+
+ if (tmp_connection != null) {
+ AltosDebug.debug("Closing USB device");
+ tmp_connection.close();
+ }
+ }
+
+ int read(byte[] buffer, int len) {
+ int ret = connection.bulkTransfer(in, buffer, len, -1);
+ AltosDebug.debug("read(%d) = %d\n", len, ret);
+ return ret;
+ }
+
+ int write(byte[] buffer, int len) {
+ int ret = connection.bulkTransfer(out, buffer, len, -1);
+ AltosDebug.debug("write(%d) = %d\n", len, ret);
+ return ret;
+ }
+
+ // Stubs of required methods when extending AltosLink
+ public boolean can_cancel_reply() { return false; }
+ public boolean show_reply_timeout() { return true; }
+ public void hide_reply_timeout() { }
+
+}
@Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
- if(v.getClass() != null &&
- v.getClass().getPackage() != null &&
- v.getClass().getPackage().getName() != null &&
- v.getClass().getPackage().getName().startsWith("maps."))
- {
- return true;
- }
- return super.canScroll(v, checkV, dx, x, y);
+
+ if (v.getClass() != null &&
+ v.getClass().getName() != null &&
+ v.getClass().getName().endsWith("MapOffline"))
+ return true;
+
+ if(v.getClass() != null &&
+ v.getClass().getPackage() != null &&
+ v.getClass().getPackage().getName() != null &&
+ v.getClass().getPackage().getName().startsWith("maps."))
+ return true;
+
+ return super.canScroll(v, checkV, dx, x, y);
}
}
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
+import android.location.Location;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosVoice {
private TextToSpeech tts = null;
private boolean tts_enabled = false;
- private IdleThread idle_thread = null;
+ static final int TELL_MODE_NONE = 0;
+ static final int TELL_MODE_PAD = 1;
+ static final int TELL_MODE_FLIGHT = 2;
+ static final int TELL_MODE_RECOVER = 3;
- private AltosState old_state = null;
+ static final int TELL_FLIGHT_NONE = 0;
+ static final int TELL_FLIGHT_STATE = 1;
+ static final int TELL_FLIGHT_SPEED = 2;
+ static final int TELL_FLIGHT_HEIGHT = 3;
+ static final int TELL_FLIGHT_TRACK = 4;
- public AltosVoice(AltosDroid a) {
+ private int last_tell_mode;
+ private int last_tell_serial = AltosLib.MISSING;
+ private int last_state;
+ private AltosGPS last_gps;
+ private double last_height = AltosLib.MISSING;
+ private Location last_receiver;
+ private long last_speak_time;
+ private int last_flight_tell = TELL_FLIGHT_NONE;
+
+ private long now() {
+ return System.currentTimeMillis();
+ }
+
+ private void reset_last() {
+ last_tell_mode = TELL_MODE_NONE;
+ last_speak_time = now() - 100 * 1000;
+ last_gps = null;
+ last_height = AltosLib.MISSING;
+ last_receiver = null;
+ last_state = AltosLib.ao_flight_invalid;
+ last_flight_tell = TELL_FLIGHT_NONE;
+ }
+ public AltosVoice(AltosDroid a) {
tts = new TextToSpeech(a, new OnInitListener() {
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) tts_enabled = true;
- if (tts_enabled) {
- idle_thread = new IdleThread();
- }
}
});
+ reset_last();
+ }
+ public synchronized void set_enable(boolean enable) {
+ tts_enabled = enable;
}
public synchronized void speak(String s) {
if (!tts_enabled) return;
+ last_speak_time = now();
tts.speak(s, TextToSpeech.QUEUE_ADD, null);
}
+ public synchronized long time_since_speak() {
+ return now() - last_speak_time;
+ }
+
+ public synchronized void speak(String format, Object ... arguments) {
+ speak(String.format(format, arguments));
+ }
+
+ public synchronized boolean is_speaking() {
+ return tts.isSpeaking();
+ }
+
public void stop() {
- if (tts != null) tts.shutdown();
- if (idle_thread != null) {
- idle_thread.interrupt();
- idle_thread = null;
+ if (tts != null) {
+ tts.stop();
+ tts.shutdown();
}
}
- public void tell(AltosState state, AltosGreatCircle from_receiver) {
- if (!tts_enabled) return;
+ private boolean last_apogee_good;
+ private boolean last_main_good;
+ private boolean last_gps_good;
- boolean spoke = false;
- if (old_state == null || old_state.state != state.state) {
- if (state.state != AltosLib.ao_flight_stateless)
- speak(state.state_name());
- if ((old_state == null || old_state.state <= AltosLib.ao_flight_boost) &&
- state.state > AltosLib.ao_flight_boost) {
- if (state.max_speed() != AltosLib.MISSING)
- speak(String.format("Max speed: %s.",
- AltosConvert.speed.say_units(state.max_speed())));
- spoke = true;
- } else if ((old_state == null || old_state.state < AltosLib.ao_flight_drogue) &&
- state.state >= AltosLib.ao_flight_drogue) {
- if (state.max_height() != AltosLib.MISSING)
- speak(String.format("Max height: %s.",
- AltosConvert.height.say_units(state.max_height())));
- spoke = true;
- }
+ private boolean tell_gonogo(String name,
+ boolean current,
+ boolean previous,
+ boolean new_mode) {
+ if (current != previous || new_mode)
+ speak("%s %s.", name, current ? "ready" : "not ready");
+ return current;
+ }
+
+ private boolean tell_pad(TelemetryState telem_state, AltosState state,
+ AltosGreatCircle from_receiver, Location receiver) {
+
+ if (state == null)
+ return false;
+
+ if (state.apogee_voltage != AltosLib.MISSING)
+ last_apogee_good = tell_gonogo("apogee",
+ state.apogee_voltage >= AltosLib.ao_igniter_good,
+ last_apogee_good,
+ last_tell_mode != TELL_MODE_PAD);
+
+ if (state.main_voltage != AltosLib.MISSING)
+ last_main_good = tell_gonogo("main",
+ state.main_voltage >= AltosLib.ao_igniter_good,
+ last_main_good,
+ last_tell_mode != TELL_MODE_PAD);
+
+ if (state.gps != null)
+ last_gps_good = tell_gonogo("G P S",
+ state.gps_ready,
+ last_gps_good,
+ last_tell_mode != TELL_MODE_PAD);
+ return true;
+ }
+
+
+ private boolean descending(int state) {
+ return AltosLib.ao_flight_drogue <= state && state <= AltosLib.ao_flight_landed;
+ }
+
+ private boolean target_moved(AltosState state) {
+ if (last_gps != null && state != null && state.gps != null) {
+ AltosGreatCircle moved = new AltosGreatCircle(last_gps.lat, last_gps.lon, last_gps.alt,
+ state.gps.lat, state.gps.lon, state.gps.alt);
+ double height_change = 0;
+ double height = state.height();
+
+ if (height != AltosLib.MISSING && last_height != AltosLib.MISSING)
+ height_change = Math.abs(last_height - height);
+
+ if (moved.range < 10 && height_change < 10)
+ return false;
}
- if (old_state == null || old_state.gps_ready != state.gps_ready) {
- if (state.gps_ready) {
- speak("GPS ready");
- spoke = true;
- } else if (old_state != null) {
- speak("GPS lost");
- spoke = true;
- }
+ return true;
+ }
+
+ private boolean receiver_moved(Location receiver) {
+ if (last_receiver != null && receiver != null) {
+ AltosGreatCircle moved = new AltosGreatCircle(last_receiver.getLatitude(),
+ last_receiver.getLongitude(),
+ last_receiver.getAltitude(),
+ receiver.getLatitude(),
+ receiver.getLongitude(),
+ receiver.getAltitude());
+ if (moved.range < 10)
+ return false;
}
- old_state = state;
- if (idle_thread != null)
- idle_thread.notice(state, from_receiver, spoke);
+ return true;
}
+ private boolean tell_flight(TelemetryState telem_state, AltosState state,
+ AltosGreatCircle from_receiver, Location receiver) {
- class IdleThread extends Thread {
- boolean started;
- private AltosState state;
- private AltosGreatCircle from_receiver;
- int reported_landing;
- int report_interval;
- long report_time;
+ boolean spoken = false;
- public synchronized void report(boolean last) {
- if (state == null)
- return;
+ if (state == null)
+ return false;
- /* reset the landing count once we hear about a new flight */
- if (state.state < AltosLib.ao_flight_drogue)
- reported_landing = 0;
+ if (last_tell_mode != TELL_MODE_FLIGHT)
+ last_flight_tell = TELL_FLIGHT_NONE;
- /* Shut up once the rocket is on the ground */
- if (reported_landing > 2) {
- return;
+ if (state.state != last_state && AltosLib.ao_flight_boost <= state.state && state.state <= AltosLib.ao_flight_landed) {
+ speak(state.state_name());
+ if (descending(state.state) && !descending(last_state)) {
+ if (state.max_height() != AltosLib.MISSING) {
+ speak("max height: %s.",
+ AltosConvert.height.say_units(state.max_height()));
+ }
}
+ last_flight_tell = TELL_FLIGHT_STATE;
+ return true;
+ }
- /* If the rocket isn't on the pad, then report location */
- if ((AltosLib.ao_flight_drogue <= state.state &&
- state.state < AltosLib.ao_flight_landed) ||
- state.state == AltosLib.ao_flight_stateless)
- {
- AltosGreatCircle position;
-
- if (from_receiver != null)
- position = from_receiver;
- else
- position = state.from_pad;
-
- if (position != null) {
- speak(String.format("Height %s, bearing %s %d, elevation %d, range %s.\n",
- AltosConvert.height.say_units(state.height()),
- position.bearing_words(
- AltosGreatCircle.BEARING_VOICE),
- (int) (position.bearing + 0.5),
- (int) (position.elevation + 0.5),
- AltosConvert.distance.say_units(position.range)));
- }
- } else if (state.state > AltosLib.ao_flight_pad) {
- if (state.height() != AltosLib.MISSING)
- speak(AltosConvert.height.say_units(state.height()));
+ if (last_tell_mode == TELL_MODE_FLIGHT && last_flight_tell == TELL_FLIGHT_TRACK) {
+ if (time_since_speak() < 10 * 1000)
+ return false;
+ if (!target_moved(state) && !receiver_moved(receiver))
+ return false;
+ }
+
+ double speed;
+ double height;
+
+ if (last_flight_tell == TELL_FLIGHT_NONE || last_flight_tell == TELL_FLIGHT_STATE || last_flight_tell == TELL_FLIGHT_TRACK) {
+ last_flight_tell = TELL_FLIGHT_SPEED;
+
+ if (state.state <= AltosLib.ao_flight_coast) {
+ speed = state.speed();
} else {
- reported_landing = 0;
+ speed = state.gps_speed();
+ if (speed == AltosLib.MISSING)
+ speed = state.speed();
}
- /* If the rocket is coming down, check to see if it has landed;
- * either we've got a landed report or we haven't heard from it in
- * a long time
- */
- if (state.state >= AltosLib.ao_flight_drogue &&
- (last ||
- System.currentTimeMillis() - state.received_time >= 15000 ||
- state.state == AltosLib.ao_flight_landed))
- {
- if (Math.abs(state.speed()) < 20 && state.height() < 100)
- speak("rocket landed safely");
- else
- speak("rocket may have crashed");
- if (state.from_pad != null)
- speak(String.format("Bearing %d degrees, range %s.",
- (int) (state.from_pad.bearing + 0.5),
- AltosConvert.distance.say_units(state.from_pad.distance)));
- ++reported_landing;
+ if (speed != AltosLib.MISSING) {
+ speak("speed: %s.", AltosConvert.speed.say_units(speed));
+ return true;
}
}
- long now () {
- return System.currentTimeMillis();
- }
+ if (last_flight_tell == TELL_FLIGHT_SPEED) {
+ last_flight_tell = TELL_FLIGHT_HEIGHT;
+ height = state.height();
- void set_report_time() {
- report_time = now() + report_interval;
+ if (height != AltosLib.MISSING) {
+ speak("height: %s.", AltosConvert.height.say_units(height));
+ return true;
+ }
}
- public void run () {
- try {
- for (;;) {
- set_report_time();
- for (;;) {
- synchronized (this) {
- long sleep_time = report_time - now();
- if (sleep_time <= 0)
- break;
- wait(sleep_time);
- }
- }
- report(false);
- }
- } catch (InterruptedException ie) {
+ if (last_flight_tell == TELL_FLIGHT_HEIGHT) {
+ last_flight_tell = TELL_FLIGHT_TRACK;
+ if (from_receiver != null) {
+ speak("bearing %s %d, elevation %d, range %s.",
+ from_receiver.bearing_words(
+ AltosGreatCircle.BEARING_VOICE),
+ (int) (from_receiver.bearing + 0.5),
+ (int) (from_receiver.elevation + 0.5),
+ AltosConvert.distance.say(from_receiver.range));
+ return true;
}
}
- public synchronized void notice(AltosState new_state, AltosGreatCircle new_from_receiver, boolean spoken) {
- AltosState old_state = state;
- state = new_state;
- from_receiver = new_from_receiver;
- if (!started && state.state > AltosLib.ao_flight_pad) {
- started = true;
- start();
- }
+ return spoken;
+ }
- if (state.state < AltosLib.ao_flight_drogue)
- report_interval = 10000;
- else
- report_interval = 20000;
- if (old_state != null && old_state.state != state.state) {
- report_time = now();
- this.notify();
- } else if (spoken)
- set_report_time();
- }
+ private boolean tell_recover(TelemetryState telem_state, AltosState state,
+ AltosGreatCircle from_receiver, Location receiver) {
+
+ if (from_receiver == null)
+ return false;
- public IdleThread() {
- state = null;
- reported_landing = 0;
- report_interval = 10000;
+ if (last_tell_mode == TELL_MODE_RECOVER) {
+ if (!target_moved(state) && !receiver_moved(receiver))
+ return false;
+ if (time_since_speak() <= 10 * 1000)
+ return false;
}
+
+ String direction = AltosDroid.direction(from_receiver, receiver);
+ if (direction == null)
+ direction = String.format("Bearing %d", (int) (from_receiver.bearing + 0.5));
+
+ speak("%s, range %s.", direction,
+ AltosConvert.distance.say_units(from_receiver.distance));
+
+ return true;
}
+ public void tell(TelemetryState telem_state, AltosState state,
+ AltosGreatCircle from_receiver, Location receiver,
+ AltosDroidTab tab) {
+
+ boolean spoken = false;
+
+ if (!tts_enabled) return;
+
+ if (is_speaking()) return;
+
+ int tell_serial = last_tell_serial;
+
+ if (state != null)
+ tell_serial = state.serial;
+
+ if (tell_serial != last_tell_serial)
+ reset_last();
+
+ int tell_mode = TELL_MODE_NONE;
+
+ if (tab.tab_name().equals(AltosDroid.tab_pad_name))
+ tell_mode = TELL_MODE_PAD;
+ else if (tab.tab_name().equals(AltosDroid.tab_flight_name))
+ tell_mode = TELL_MODE_FLIGHT;
+ else
+ tell_mode = TELL_MODE_RECOVER;
+
+ if (tell_mode == TELL_MODE_PAD)
+ spoken = tell_pad(telem_state, state, from_receiver, receiver);
+ else if (tell_mode == TELL_MODE_FLIGHT)
+ spoken = tell_flight(telem_state, state, from_receiver, receiver);
+ else
+ spoken = tell_recover(telem_state, state, from_receiver, receiver);
+
+ if (spoken) {
+ last_tell_mode = tell_mode;
+ last_tell_serial = tell_serial;
+ if (state != null) {
+ last_state = state.state;
+ last_height = state.height();
+ if (state.gps != null)
+ last_gps = state.gps;
+ }
+ if (receiver != null)
+ last_receiver = receiver;
+ }
+ }
}
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
-import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
* Activity in the result Intent.
*/
public class DeviceListActivity extends Activity {
- // Debugging
- private static final String TAG = "DeviceListActivity";
- private static final boolean D = true;
// Return Intent extra
public static final String EXTRA_DEVICE_ADDRESS = "device_address";
* Start device discover with the BluetoothAdapter
*/
private void doDiscovery() {
- if (D) Log.d(TAG, "doDiscovery()");
+ AltosDebug.debug("doDiscovery()");
// Indicate scanning in the title
setProgressBarIndeterminateVisibility(true);
else
name = info;
- if (D) Log.d(TAG, String.format("******* selected item '%s'", info));
+ AltosDebug.debug("******* selected item '%s'", info);
// Create the result Intent and include the MAC address
Intent intent = new Intent();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
- // Get the BluetoothDevice object from the Intent
+
+ /* Get the BluetoothDevice object from the Intent
+ */
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- // If it's already paired, skip it, because it's been listed already
- if ( device.getBondState() != BluetoothDevice.BOND_BONDED
- && device.getName().startsWith("TeleBT") ) {
- mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
+
+ /* If it's already paired, skip it, because it's been listed already
+ */
+ if (device != null && device.getBondState() != BluetoothDevice.BOND_BONDED)
+ {
+ String name = device.getName();
+ if (name != null && name.startsWith("TeleBT"))
+ mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
- // When discovery is finished, change the Activity title
+
+ /* When discovery is finished, change the Activity title
+ */
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
setProgressBarIndeterminateVisibility(false);
setTitle(R.string.select_device);
missing = m;
set = true;
if (missing) {
- hide();
red.setImageDrawable(dGray);
green.setImageDrawable(dGray);
} else if (state) {
red.setImageDrawable(dGray);
green.setImageDrawable(dGreen);
- show();
} else {
red.setImageDrawable(dRed);
green.setImageDrawable(dGray);
- show();
}
}
-
- public void show() {
- red.setVisibility(View.VISIBLE);
- green.setVisibility(View.VISIBLE);
- }
-
- public void hide() {
- red.setVisibility(View.GONE);
- green.setVisibility(View.GONE);
- }
}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+import org.altusmetrum.AltosDroid.R;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.view.View.OnClickListener;
+import android.widget.*;
+import android.widget.AdapterView.*;
+
+import org.altusmetrum.altoslib_7.*;
+
+public class MapTypeActivity extends Activity {
+ private Button hybrid;
+ private Button satellite;
+ private Button roadmap;
+ private Button terrain;
+ private int selected_type;
+
+ public static final String EXTRA_MAP_TYPE = "map_type";
+
+ private void done(int type) {
+
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_MAP_TYPE, type);
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ }
+
+ public void selectType(View view) {
+ AltosDebug.debug("selectType %s", view.toString());
+ if (view == hybrid)
+ done(AltosMap.maptype_hybrid);
+ if (view == satellite)
+ done(AltosMap.maptype_satellite);
+ if (view == roadmap)
+ done(AltosMap.maptype_roadmap);
+ if (view == terrain)
+ done(AltosMap.maptype_terrain);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Setup the window
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ setContentView(R.layout.map_type);
+
+ hybrid = (Button) findViewById(R.id.map_type_hybrid);
+ satellite = (Button) findViewById(R.id.map_type_satellite);
+ roadmap = (Button) findViewById(R.id.map_type_roadmap);
+ terrain = (Button) findViewById(R.id.map_type_terrain);
+
+ // Set result CANCELED incase the user backs out
+ setResult(Activity.RESULT_CANCELED);
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.*;
+import java.io.*;
+import java.text.*;
+
+import org.altusmetrum.AltosDroid.R;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.view.View.OnClickListener;
+import android.widget.*;
+import android.widget.AdapterView.*;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationListener;
+import android.location.Criteria;
+
+import org.altusmetrum.altoslib_7.*;
+
+/**
+ * This Activity appears as a dialog. It lists any paired devices and
+ * devices detected in the area after discovery. When a device is chosen
+ * by the user, the MAC address of the device is sent back to the parent
+ * Activity in the result Intent.
+ */
+public class PreloadMapActivity extends Activity implements AltosLaunchSiteListener, AltosMapInterface, AltosMapLoaderListener, LocationListener {
+
+ private ArrayAdapter<AltosLaunchSite> known_sites_adapter;
+
+ private CheckBox hybrid;
+ private CheckBox satellite;
+ private CheckBox roadmap;
+ private CheckBox terrain;
+
+ private Spinner known_sites_spinner;
+ private Spinner min_zoom;
+ private Spinner max_zoom;
+ private TextView radius_label;
+ private Spinner radius;
+
+ private EditText latitude;
+ private EditText longitude;
+
+ private ProgressBar progress;
+
+ /* AltosMapLoaderListener interfaces */
+ public void loader_start(final int max) {
+ this.runOnUiThread(new Runnable() {
+ public void run() {
+ progress.setMax(max);
+ progress.setProgress(0);
+ }
+ });
+ }
+
+ public void loader_notify(final int cur, final int max, final String name) {
+ this.runOnUiThread(new Runnable() {
+ public void run() {
+ progress.setProgress(cur);
+ }
+ });
+ }
+
+ public void loader_done(int max) {
+ this.runOnUiThread(new Runnable() {
+ public void run() {
+ progress.setProgress(0);
+ finish();
+ }
+ });
+ }
+
+ /* AltosLaunchSiteListener interface */
+ public void notify_launch_sites(final List<AltosLaunchSite> sites) {
+ this.runOnUiThread(new Runnable() {
+ public void run() {
+ for (AltosLaunchSite site : sites)
+ known_sites_adapter.add(site);
+ }
+ });
+ }
+
+ AltosMap map;
+ AltosMapLoader loader;
+
+ class PreloadMapImage implements AltosImage {
+ public void flush() {
+ }
+
+ public PreloadMapImage(File file) {
+ }
+ }
+
+ public AltosMapPath new_path() {
+ return null;
+ }
+
+ public AltosMapLine new_line() {
+ return null;
+ }
+
+ public AltosImage load_image(File file) throws Exception {
+ return new PreloadMapImage(file);
+ }
+
+ public AltosMapMark new_mark(double lat, double lon, int state) {
+ return null;
+ }
+
+ class PreloadMapTile extends AltosMapTile {
+ public void paint(AltosMapTransform t) {
+ }
+
+ public PreloadMapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+ super(listener, upper_left, center, zoom, maptype, px_size, 2);
+ }
+
+ }
+
+ public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+ return new PreloadMapTile(listener, upper_left, center, zoom, maptype, px_size);
+ }
+
+ public int width() {
+ return AltosMap.px_size;
+ }
+
+ public int height() {
+ return AltosMap.px_size;
+ }
+
+ public void repaint() {
+ }
+
+ public void repaint(AltosRectangle damage) {
+ }
+
+ public void set_zoom_label(String label) {
+ }
+
+ public void select_object(AltosLatLon latlon) {
+ }
+
+ public void debug(String format, Object ... arguments) {
+ AltosDebug.debug(format, arguments);
+ }
+
+ /* LocationProvider interface */
+
+ AltosLaunchSite current_location_site;
+
+ public void onLocationChanged(Location location) {
+ AltosDebug.debug("location changed");
+ if (current_location_site == null) {
+ AltosLaunchSite selected_item = (AltosLaunchSite) known_sites_spinner.getSelectedItem();
+
+ current_location_site = new AltosLaunchSite("Current Location", location.getLatitude(), location.getLongitude());
+ known_sites_adapter.insert(current_location_site, 0);
+
+ if (selected_item != null)
+ known_sites_spinner.setSelection(known_sites_adapter.getPosition(selected_item));
+ else {
+ latitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.latitude)));
+ longitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.longitude)));
+ }
+ } else {
+ current_location_site.latitude = location.getLatitude();
+ current_location_site.longitude = location.getLongitude();
+ }
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+
+ public void onProviderEnabled(String provider) {
+ }
+
+ public void onProviderDisabled(String provider) {
+ }
+
+ private double text(EditText view) throws ParseException {
+ return AltosParse.parse_double_locale(view.getEditableText().toString());
+ }
+
+ private double latitude() throws ParseException {
+ return text(latitude);
+ }
+
+ private double longitude() throws ParseException {
+ return text(longitude);
+ }
+
+ private int value(Spinner spinner) {
+ return (Integer) spinner.getSelectedItem();
+ }
+
+ private int min_z() {
+ return value(min_zoom);
+ }
+
+ private int max_z() {
+ return value(max_zoom);
+ }
+
+ private double value_distance(Spinner spinner) {
+ return (Double) spinner.getSelectedItem();
+ }
+
+ private double radius() {
+ double r = value_distance(radius);
+ if (AltosPreferences.imperial_units())
+ r = AltosConvert.distance.inverse(r);
+ else
+ r = r * 1000;
+ return r;
+ }
+
+ private int bit(CheckBox box, int value) {
+ if (box.isChecked())
+ return 1 << value;
+ return 0;
+ }
+
+ private int types() {
+ return (bit(hybrid, AltosMap.maptype_hybrid) |
+ bit(satellite, AltosMap.maptype_satellite) |
+ bit(roadmap, AltosMap.maptype_roadmap) |
+ bit(terrain, AltosMap.maptype_terrain));
+ }
+
+ private void load() {
+ try {
+ double lat = latitude();
+ double lon = longitude();
+ int min = min_z();
+ int max = max_z();
+ double r = radius();
+ int t = types();
+
+ AltosDebug.debug("PreloadMap load %f %f %d %d %f %d\n",
+ lat, lon, min, max, r, t);
+ loader.load(lat, lon, min, max, r, t);
+ } catch (ParseException e) {
+ AltosDebug.debug("PreloadMap load raised exception %s", e.toString());
+ }
+ }
+
+ private void add_numbers(Spinner spinner, int min, int max, int def) {
+
+ ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this, android.R.layout.simple_spinner_item);
+
+ int spinner_def = 0;
+ int pos = 0;
+
+ for (int i = min; i <= max; i++) {
+ adapter.add(new Integer(i));
+ if (i == def)
+ spinner_def = pos;
+ pos++;
+ }
+
+ spinner.setAdapter(adapter);
+ spinner.setSelection(spinner_def);
+ }
+
+
+ private void add_distance(Spinner spinner, double[] distances_km, double def_km, double[] distances_mi, double def_mi) {
+
+ ArrayAdapter<Double> adapter = new ArrayAdapter<Double>(this, android.R.layout.simple_spinner_item);
+
+ int spinner_def = 0;
+ int pos = 0;
+
+ double[] distances;
+ double def;
+ if (AltosPreferences.imperial_units()) {
+ distances = distances_mi;
+ def = def_mi;
+ } else {
+ distances = distances_km;
+ def = def_km;
+ }
+
+ for (int i = 0; i < distances.length; i++) {
+ adapter.add(distances[i]);
+ if (distances[i] == def)
+ spinner_def = pos;
+ pos++;
+ }
+
+ spinner.setAdapter(adapter);
+ spinner.setSelection(spinner_def);
+ }
+
+
+
+ class SiteListListener implements OnItemSelectedListener {
+ public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+ AltosLaunchSite site = (AltosLaunchSite) parent.getItemAtPosition(pos);
+ latitude.setText(new StringBuffer(String.format("%12.6f", site.latitude)));
+ longitude.setText(new StringBuffer(String.format("%12.6f", site.longitude)));
+ }
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+
+ public SiteListListener() {
+ }
+ }
+
+ double[] radius_mi = { 1, 2, 5, 10, 20 };
+ double radius_def_mi = 2;
+ double[] radius_km = { 1, 2, 5, 10, 20, 30 };
+ double radius_def_km = 2;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Setup the window
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ setContentView(R.layout.map_preload);
+
+ // Set result CANCELED incase the user backs out
+ setResult(Activity.RESULT_CANCELED);
+
+ // Initialize the button to perform device discovery
+ Button loadButton = (Button) findViewById(R.id.preload_load);
+ loadButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ load();
+ }
+ });
+
+ latitude = (EditText) findViewById(R.id.preload_latitude);
+ longitude = (EditText) findViewById(R.id.preload_longitude);
+
+ hybrid = (CheckBox) findViewById(R.id.preload_hybrid);
+ satellite = (CheckBox) findViewById(R.id.preload_satellite);
+ roadmap = (CheckBox) findViewById(R.id.preload_roadmap);
+ terrain = (CheckBox) findViewById(R.id.preload_terrain);
+
+ hybrid.setChecked(true);
+
+ min_zoom = (Spinner) findViewById(R.id.preload_min_zoom);
+ add_numbers(min_zoom,
+ AltosMap.min_zoom - AltosMap.default_zoom,
+ AltosMap.max_zoom - AltosMap.default_zoom, -2);
+ max_zoom = (Spinner) findViewById(R.id.preload_max_zoom);
+ add_numbers(max_zoom,
+ AltosMap.min_zoom - AltosMap.default_zoom,
+ AltosMap.max_zoom - AltosMap.default_zoom, 2);
+ radius_label = (TextView) findViewById(R.id.preload_radius_label);
+ radius = (Spinner) findViewById(R.id.preload_radius);
+ if (AltosPreferences.imperial_units())
+ radius_label.setText("Radius (miles)");
+ else
+ radius_label.setText("Radius (km)");
+ add_distance(radius, radius_km, radius_def_km, radius_mi, radius_def_mi);
+
+ progress = (ProgressBar) findViewById(R.id.preload_progress);
+
+ // Initialize array adapters. One for already paired devices and
+ // one for newly discovered devices
+ known_sites_spinner = (Spinner) findViewById(R.id.preload_site_list);
+
+ known_sites_adapter = new ArrayAdapter<AltosLaunchSite>(this, android.R.layout.simple_spinner_item);
+
+ known_sites_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+
+ known_sites_spinner.setAdapter(known_sites_adapter);
+ known_sites_spinner.setOnItemSelectedListener(new SiteListListener());
+
+ map = new AltosMap(this);
+
+ loader = new AltosMapLoader(map, this);
+
+ // Listen for GPS and Network position updates
+ LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
+
+ locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
+
+ new AltosLaunchSites(this);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ // Stop listening for location updates
+ ((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
+ }
+}
+++ /dev/null
-/*
- * Copyright © 2013 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import org.altusmetrum.altoslib_6.*;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.location.Location;
-
-public class TabAscent extends AltosDroidTab {
- AltosDroid mAltosDroid;
-
- private TextView mHeightView;
- private TextView mMaxHeightView;
- private TextView mSpeedView;
- private TextView mMaxSpeedView;
- private TextView mAccelView;
- private TextView mMaxAccelView;
- private TextView mLatitudeView;
- private TextView mLongitudeView;
- private TextView mApogeeVoltageView;
- private GoNoGoLights mApogeeLights;
- private TextView mMainVoltageView;
- private GoNoGoLights mMainLights;
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- mAltosDroid = (AltosDroid) activity;
- mAltosDroid.registerTab(this);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.tab_ascent, container, false);
-
- mHeightView = (TextView) v.findViewById(R.id.height_value);
- mMaxHeightView = (TextView) v.findViewById(R.id.max_height_value);
- mSpeedView = (TextView) v.findViewById(R.id.speed_value);
- mMaxSpeedView = (TextView) v.findViewById(R.id.max_speed_value);
- mAccelView = (TextView) v.findViewById(R.id.accel_value);
- mMaxAccelView = (TextView) v.findViewById(R.id.max_accel_value);
- mLatitudeView = (TextView) v.findViewById(R.id.lat_value);
- mLongitudeView = (TextView) v.findViewById(R.id.lon_value);
-
- mApogeeVoltageView = (TextView) v.findViewById(R.id.apogee_voltage_value);
- mApogeeLights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
- (ImageView) v.findViewById(R.id.apogee_greenled),
- getResources());
-
- mMainVoltageView = (TextView) v.findViewById(R.id.main_voltage_value);
- mMainLights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
- (ImageView) v.findViewById(R.id.main_greenled),
- getResources());
-
- return v;
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mAltosDroid.unregisterTab(this);
- mAltosDroid = null;
- }
-
- public String tab_name() {
- return "ascent";
- }
-
- public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
- if (state != null) {
- set_value(mHeightView, AltosConvert.height, 6, state.height());
- set_value(mHeightView, AltosConvert.height, 6, state.height());
- set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height());
- set_value(mSpeedView, AltosConvert.speed, 6, state.speed());
- set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed());
- set_value(mAccelView, AltosConvert.accel, 6, state.acceleration());
- set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration());
-
- if (state.gps != null) {
- mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
- mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
- } else {
- mLatitudeView.setText("");
- mLongitudeView.setText("");
- }
-
- mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
- mApogeeLights.set(state.apogee_voltage > 3.2, state.apogee_voltage == AltosLib.MISSING);
-
- mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage));
- mMainLights.set(state.main_voltage > 3.2, state.main_voltage == AltosLib.MISSING);
- }
- }
-}
+++ /dev/null
-/*
- * Copyright © 2013 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import org.altusmetrum.altoslib_6.*;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.location.Location;
-
-public class TabDescent extends AltosDroidTab {
- AltosDroid mAltosDroid;
-
- private TextView mSpeedView;
- private TextView mHeightView;
- private TextView mElevationView;
- private TextView mRangeView;
- private TextView mBearingView;
- private TextView mCompassView;
- private TextView mDistanceView;
- private TextView mLatitudeView;
- private TextView mLongitudeView;
- private TextView mApogeeVoltageView;
- private GoNoGoLights mApogeeLights;
- private TextView mMainVoltageView;
- private GoNoGoLights mMainLights;
-
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- mAltosDroid = (AltosDroid) activity;
- mAltosDroid.registerTab(this);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.tab_descent, container, false);
-
- mSpeedView = (TextView) v.findViewById(R.id.speed_value);
- mHeightView = (TextView) v.findViewById(R.id.height_value);
- mElevationView = (TextView) v.findViewById(R.id.elevation_value);
- mRangeView = (TextView) v.findViewById(R.id.range_value);
- mBearingView = (TextView) v.findViewById(R.id.bearing_value);
- mCompassView = (TextView) v.findViewById(R.id.compass_value);
- mDistanceView = (TextView) v.findViewById(R.id.distance_value);
- mLatitudeView = (TextView) v.findViewById(R.id.lat_value);
- mLongitudeView = (TextView) v.findViewById(R.id.lon_value);
-
- mApogeeVoltageView = (TextView) v.findViewById(R.id.apogee_voltage_value);
- mApogeeLights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
- (ImageView) v.findViewById(R.id.apogee_greenled),
- getResources());
-
- mMainVoltageView = (TextView) v.findViewById(R.id.main_voltage_value);
- mMainLights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
- (ImageView) v.findViewById(R.id.main_greenled),
- getResources());
-
- return v;
- }
-
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mAltosDroid.unregisterTab(this);
- mAltosDroid = null;
- }
-
- public String tab_name() { return "descent"; }
-
- public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
- if (state != null) {
- set_value(mSpeedView, AltosConvert.speed, 6, state.speed());
- set_value(mHeightView, AltosConvert.height, 6, state.height());
- if (from_receiver != null) {
- mElevationView.setText(AltosDroid.number("%3.0f°", from_receiver.elevation));
- set_value(mRangeView, AltosConvert.distance, 6, from_receiver.range);
- mBearingView.setText(AltosDroid.number("%3.0f°", from_receiver.bearing));
- mCompassView.setText(from_receiver.bearing_words(AltosGreatCircle.BEARING_LONG));
- set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
- } else {
- mElevationView.setText("<unknown>");
- mRangeView.setText("<unknown>");
- mBearingView.setText("<unknown>");
- mCompassView.setText("<unknown>");
- mDistanceView.setText("<unknown>");
- }
- if (state.gps != null) {
- mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
- mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
- }
-
- mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
- mApogeeLights.set(state.apogee_voltage > 3.2, state.apogee_voltage == AltosLib.MISSING);
-
- mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage));
- mMainLights.set(state.main_voltage > 3.2, state.main_voltage == AltosLib.MISSING);
- }
- }
-
-}
--- /dev/null
+/*
+ * Copyright © 2013 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import org.altusmetrum.altoslib_7.*;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.*;
+import android.widget.*;
+import android.location.Location;
+
+public class TabFlight extends AltosDroidTab {
+ private TextView speed_view;
+ private TextView height_view;
+ private TextView max_speed_view;
+ private TextView max_height_view;
+ private TextView elevation_view;
+ private TextView range_view;
+ private TextView bearing_view;
+ private TextView compass_view;
+ private TextView distance_view;
+ private TextView latitude_view;
+ private TextView longitude_view;
+ private View apogee_view;
+ private TextView apogee_voltage_view;
+ private TextView apogee_voltage_label;
+ private GoNoGoLights apogee_lights;
+ private View main_view;
+ private TextView main_voltage_view;
+ private TextView main_voltage_label;
+ private GoNoGoLights main_lights;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.tab_flight, container, false);
+
+ speed_view = (TextView) v.findViewById(R.id.speed_value);
+ height_view = (TextView) v.findViewById(R.id.height_value);
+ max_speed_view = (TextView) v.findViewById(R.id.max_speed_value);
+ max_height_view= (TextView) v.findViewById(R.id.max_height_value);
+ elevation_view = (TextView) v.findViewById(R.id.elevation_value);
+ range_view = (TextView) v.findViewById(R.id.range_value);
+ bearing_view = (TextView) v.findViewById(R.id.bearing_value);
+ compass_view = (TextView) v.findViewById(R.id.compass_value);
+ distance_view = (TextView) v.findViewById(R.id.distance_value);
+ latitude_view = (TextView) v.findViewById(R.id.lat_value);
+ longitude_view = (TextView) v.findViewById(R.id.lon_value);
+
+ apogee_view = v.findViewById(R.id.apogee_view);
+ apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value);
+ apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
+ (ImageView) v.findViewById(R.id.apogee_greenled),
+ getResources());
+ apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label);
+
+ main_view = v.findViewById(R.id.main_view);
+ main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value);
+ main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
+ (ImageView) v.findViewById(R.id.main_greenled),
+ getResources());
+ main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label);
+
+ return v;
+ }
+
+ public String tab_name() { return AltosDroid.tab_flight_name; }
+
+ public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+ if (state != null) {
+ set_value(speed_view, AltosConvert.speed, 6, state.speed());
+ set_value(height_view, AltosConvert.height, 6, state.height());
+ set_value(max_speed_view, AltosConvert.speed, 6, state.max_speed());
+ set_value(max_height_view, AltosConvert.speed, 6, state.max_height());
+ if (from_receiver != null) {
+ elevation_view.setText(AltosDroid.number("%3.0f°", from_receiver.elevation));
+ set_value(range_view, AltosConvert.distance, 6, from_receiver.range);
+ bearing_view.setText(AltosDroid.number("%3.0f°", from_receiver.bearing));
+ compass_view.setText(from_receiver.bearing_words(AltosGreatCircle.BEARING_LONG));
+ set_value(distance_view, AltosConvert.distance, 6, from_receiver.distance);
+ } else {
+ elevation_view.setText("<unknown>");
+ range_view.setText("<unknown>");
+ bearing_view.setText("<unknown>");
+ compass_view.setText("<unknown>");
+ distance_view.setText("<unknown>");
+ }
+ if (state.gps != null) {
+ latitude_view.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+ longitude_view.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
+ }
+
+ if (state.apogee_voltage == AltosLib.MISSING) {
+ apogee_view.setVisibility(View.GONE);
+ } else {
+ apogee_voltage_view.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
+ apogee_lights.set(state.apogee_voltage > 3.2, state.apogee_voltage == AltosLib.MISSING);
+ apogee_view.setVisibility(View.VISIBLE);
+ }
+
+ if (state.main_voltage == AltosLib.MISSING) {
+ main_view.setVisibility(View.GONE);
+ } else {
+ main_voltage_view.setText(AltosDroid.number("%4.2f V", state.main_voltage));
+ main_lights.set(state.main_voltage > 3.2, state.main_voltage == AltosLib.MISSING);
+ main_view.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+}
+++ /dev/null
-/*
- * Copyright © 2013 Mike Beattie <mike@ethernal.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.AltosDroid;
-
-import org.altusmetrum.altoslib_6.*;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-import android.location.Location;
-
-public class TabLanded extends AltosDroidTab {
- AltosDroid mAltosDroid;
-
- private TextView mBearingView;
- private TextView mDistanceView;
- private TextView mTargetLatitudeView;
- private TextView mTargetLongitudeView;
- private TextView mReceiverLatitudeView;
- private TextView mReceiverLongitudeView;
- private TextView mMaxHeightView;
- private TextView mMaxSpeedView;
- private TextView mMaxAccelView;
-
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- mAltosDroid = (AltosDroid) activity;
- mAltosDroid.registerTab(this);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.tab_landed, container, false);
-
- mBearingView = (TextView) v.findViewById(R.id.bearing_value);
- mDistanceView = (TextView) v.findViewById(R.id.distance_value);
- mTargetLatitudeView = (TextView) v.findViewById(R.id.target_lat_value);
- mTargetLongitudeView = (TextView) v.findViewById(R.id.target_lon_value);
- mReceiverLatitudeView = (TextView) v.findViewById(R.id.receiver_lat_value);
- mReceiverLongitudeView = (TextView) v.findViewById(R.id.receiver_lon_value);
- mMaxHeightView = (TextView) v.findViewById(R.id.max_height_value);
- mMaxSpeedView = (TextView) v.findViewById(R.id.max_speed_value);
- mMaxAccelView = (TextView) v.findViewById(R.id.max_accel_value);
-
- return v;
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mAltosDroid.unregisterTab(this);
- mAltosDroid = null;
- }
-
- public String tab_name() { return "landed"; }
-
- public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
- if (from_receiver != null) {
- mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
- set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
- }
- if (state != null && state.gps != null) {
- mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
- mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
- }
-
- if (receiver != null) {
- mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
- mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
- }
-
- if (state != null) {
- set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height());
- set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration());
- set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed());
- }
- }
-}
package org.altusmetrum.AltosDroid;
-import java.util.Arrays;
+import java.util.*;
+import java.io.*;
-import org.altusmetrum.altoslib_6.*;
-
-import com.google.android.gms.maps.CameraUpdateFactory;
-import com.google.android.gms.maps.GoogleMap;
-import com.google.android.gms.maps.SupportMapFragment;
-import com.google.android.gms.maps.model.BitmapDescriptorFactory;
-import com.google.android.gms.maps.model.LatLng;
-import com.google.android.gms.maps.model.Marker;
-import com.google.android.gms.maps.model.MarkerOptions;
-import com.google.android.gms.maps.model.Polyline;
-import com.google.android.gms.maps.model.PolylineOptions;
+import org.altusmetrum.altoslib_7.*;
import android.app.Activity;
-import android.graphics.Color;
+import android.graphics.*;
import android.os.Bundle;
import android.support.v4.app.Fragment;
-//import android.support.v4.app.FragmentTransaction;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
+import android.support.v4.app.FragmentTransaction;
+import android.view.*;
+import android.widget.*;
import android.location.Location;
+import android.content.*;
public class TabMap extends AltosDroidTab {
- AltosDroid mAltosDroid;
-
- private SupportMapFragment mMapFragment;
- private GoogleMap mMap;
- private boolean mapLoaded = false;
- private Marker mRocketMarker;
- private Marker mPadMarker;
- private boolean pad_set;
- private Polyline mPolyline;
+ AltosLatLon here;
private TextView mDistanceView;
+ private TextView mBearingLabel;
private TextView mBearingView;
private TextView mTargetLatitudeView;
private TextView mTargetLongitudeView;
private TextView mReceiverLatitudeView;
private TextView mReceiverLongitudeView;
-
- private double mapAccuracy = -1;
+ private AltosMapOffline map_offline;
+ private AltosMapOnline map_online;
+ private View view;
+ private int map_source;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
- mAltosDroid = (AltosDroid) activity;
- mAltosDroid.registerTab(this);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- mMapFragment = new SupportMapFragment() {
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- setupMap();
- }
- };
-
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.tab_map, container, false);
- mDistanceView = (TextView)v.findViewById(R.id.distance_value);
- mBearingView = (TextView)v.findViewById(R.id.bearing_value);
- mTargetLatitudeView = (TextView)v.findViewById(R.id.target_lat_value);
- mTargetLongitudeView = (TextView)v.findViewById(R.id.target_lon_value);
- mReceiverLatitudeView = (TextView)v.findViewById(R.id.receiver_lat_value);
- mReceiverLongitudeView = (TextView)v.findViewById(R.id.receiver_lon_value);
- return v;
+ view = inflater.inflate(R.layout.tab_map, container, false);
+ int map_source = AltosDroidPreferences.map_source();
+
+ mDistanceView = (TextView)view.findViewById(R.id.distance_value);
+ mBearingLabel = (TextView)view.findViewById(R.id.bearing_label);
+ mBearingView = (TextView)view.findViewById(R.id.bearing_value);
+ mTargetLatitudeView = (TextView)view.findViewById(R.id.target_lat_value);
+ mTargetLongitudeView = (TextView)view.findViewById(R.id.target_lon_value);
+ mReceiverLatitudeView = (TextView)view.findViewById(R.id.receiver_lat_value);
+ mReceiverLongitudeView = (TextView)view.findViewById(R.id.receiver_lon_value);
+ map_offline = (AltosMapOffline)view.findViewById(R.id.map_offline);
+ map_offline.onCreateView(altos_droid);
+ map_online = new AltosMapOnline(view.getContext());
+ map_online.onCreateView(altos_droid);
+ set_map_source(AltosDroidPreferences.map_source());
+ return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- getChildFragmentManager().beginTransaction().add(R.id.map, mMapFragment).commit();
+ if (map_online != null)
+ getChildFragmentManager().beginTransaction().add(R.id.map_online, map_online.mMapFragment).commit();
}
@Override
public void onDestroyView() {
super.onDestroyView();
-
- mAltosDroid.unregisterTab(this);
- mAltosDroid = null;
-
- //Fragment fragment = (getFragmentManager().findFragmentById(R.id.map));
- //FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
- //ft.remove(fragment);
- //ft.commit();
}
- private void setupMap() {
- mMap = mMapFragment.getMap();
- if (mMap != null) {
- mMap.setMyLocationEnabled(true);
- mMap.getUiSettings().setTiltGesturesEnabled(false);
- mMap.getUiSettings().setZoomControlsEnabled(false);
-
- mRocketMarker = mMap.addMarker(
- // From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
- new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.rocket))
- .position(new LatLng(0,0))
- .visible(false)
- );
-
- mPadMarker = mMap.addMarker(
- new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad))
- .position(new LatLng(0,0))
- .visible(false)
- );
-
- mPolyline = mMap.addPolyline(
- new PolylineOptions().add(new LatLng(0,0), new LatLng(0,0))
- .width(3)
- .color(Color.BLUE)
- .visible(false)
- );
-
- mapLoaded = true;
- }
- }
+ public String tab_name() { return AltosDroid.tab_map_name; }
private void center(double lat, double lon, double accuracy) {
- if (mapAccuracy < 0 || accuracy < mapAccuracy/10) {
- mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lon),14));
- mapAccuracy = accuracy;
- }
+ if (map_offline != null)
+ map_offline.center(lat, lon, accuracy);
+ if (map_online != null)
+ map_online.center(lat, lon, accuracy);
}
- public String tab_name() { return "map"; }
-
- public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+ public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
if (from_receiver != null) {
- mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
+ String direction = AltosDroid.direction(from_receiver, receiver);
+ if (direction != null) {
+ mBearingLabel.setText("Direction");
+ mBearingView.setText(direction);
+ } else {
+ mBearingLabel.setText("Bearing");
+ mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
+ }
set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
+ } else {
+ mBearingLabel.setText("Bearing");
+ mBearingView.setText("");
+ set_value(mDistanceView, AltosConvert.distance, 6, AltosLib.MISSING);
}
if (state != null) {
- if (mapLoaded) {
- if (state.gps != null) {
- mRocketMarker.setPosition(new LatLng(state.gps.lat, state.gps.lon));
- mRocketMarker.setVisible(true);
-
- mPolyline.setPoints(Arrays.asList(new LatLng(state.pad_lat, state.pad_lon), new LatLng(state.gps.lat, state.gps.lon)));
- mPolyline.setVisible(true);
- }
-
- if (!pad_set && state.pad_lat != AltosLib.MISSING) {
- pad_set = true;
- mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon));
- mPadMarker.setVisible(true);
- }
- }
if (state.gps != null) {
mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
- if (state.gps.locked && state.gps.nsat >= 4)
- center (state.gps.lat, state.gps.lon, 10);
}
}
if (receiver != null) {
double accuracy;
+ here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
if (receiver.hasAccuracy())
accuracy = receiver.getAccuracy();
else
accuracy = 1000;
- mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
- mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
- center (receiver.getLatitude(), receiver.getLongitude(), accuracy);
+ mReceiverLatitudeView.setText(AltosDroid.pos(here.lat, "N", "S"));
+ mReceiverLongitudeView.setText(AltosDroid.pos(here.lon, "E", "W"));
+ center (here.lat, here.lon, accuracy);
+ }
+ if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) {
+ if (map_offline != null)
+ map_offline.show(telem_state, state, from_receiver, receiver);
+ } else {
+ if (map_online != null)
+ map_online.show(telem_state, state, from_receiver, receiver);
}
+ }
+ @Override
+ public void set_map_type(int map_type) {
+ if (map_offline != null)
+ map_offline.set_map_type(map_type);
+ if (map_online != null)
+ map_online.set_map_type(map_type);
+ }
+
+ @Override
+ public void set_map_source(int map_source) {
+ this.map_source = map_source;
+ if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) {
+ if (map_online != null)
+ map_online.set_visible(false);
+ if (map_offline != null) {
+ map_offline.set_visible(true);
+ map_offline.show(last_telem_state, last_state, last_from_receiver, last_receiver);
+ }
+ } else {
+ if (map_offline != null)
+ map_offline.set_visible(false);
+ if (map_online != null) {
+ map_online.set_visible(true);
+ map_online.show(last_telem_state, last_state, last_from_receiver, last_receiver);
+ }
+ }
}
public TabMap() {
package org.altusmetrum.AltosDroid;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
+import android.view.*;
+import android.widget.*;
import android.location.Location;
public class TabPad extends AltosDroidTab {
- AltosDroid mAltosDroid;
-
- private TextView mBatteryVoltageView;
- private TextView mBatteryVoltageLabel;
- private GoNoGoLights mBatteryLights;
- private TextView mApogeeVoltageView;
- private TextView mApogeeVoltageLabel;
- private GoNoGoLights mApogeeLights;
- private TextView mMainVoltageView;
- private TextView mMainVoltageLabel;
- private GoNoGoLights mMainLights;
- private TextView mDataLoggingView;
- private GoNoGoLights mDataLoggingLights;
- private TextView mGPSLockedView;
- private GoNoGoLights mGPSLockedLights;
- private TextView mGPSReadyView;
- private GoNoGoLights mGPSReadyLights;
- private TextView mPadLatitudeView;
- private TextView mPadLongitudeView;
- private TextView mPadAltitudeView;
+ private TextView battery_voltage_view;
+ private GoNoGoLights battery_lights;
+
+ private TableRow receiver_row;
+ private TextView receiver_voltage_view;
+ private TextView receiver_voltage_label;
+ private GoNoGoLights receiver_voltage_lights;
+
+ private TableRow apogee_row;
+ private TextView apogee_voltage_view;
+ private TextView apogee_voltage_label;
+ private GoNoGoLights apogee_lights;
+
+ private TableRow main_row;
+ private TextView main_voltage_view;
+ private TextView main_voltage_label;
+ private GoNoGoLights main_lights;
+
+ private TextView data_logging_view;
+ private GoNoGoLights data_logging_lights;
+
+ private TextView gps_locked_view;
+ private GoNoGoLights gps_locked_lights;
+
+ private TextView gps_ready_view;
+ private GoNoGoLights gps_ready_lights;
+
+ private TextView receiver_latitude_view;
+ private TextView receiver_longitude_view;
+ private TextView receiver_altitude_view;
+
+ private TableRow[] ignite_row = new TableRow[4];
+ private TextView[] ignite_voltage_view = new TextView[4];
+ private TextView[] ignite_voltage_label = new TextView[4];
+ private GoNoGoLights[] ignite_lights = new GoNoGoLights[4];
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- mAltosDroid = (AltosDroid) activity;
- mAltosDroid.registerTab(this);
- }
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.tab_pad, container, false);
- mBatteryVoltageView = (TextView) v.findViewById(R.id.battery_voltage_value);
- mBatteryVoltageLabel = (TextView) v.findViewById(R.id.battery_voltage_label);
- mBatteryLights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled),
+ battery_voltage_view = (TextView) v.findViewById(R.id.battery_voltage_value);
+ battery_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled),
(ImageView) v.findViewById(R.id.battery_greenled),
getResources());
- mApogeeVoltageView = (TextView) v.findViewById(R.id.apogee_voltage_value);
- mApogeeVoltageLabel = (TextView) v.findViewById(R.id.apogee_voltage_label);
- mApogeeLights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
+ receiver_row = (TableRow) v.findViewById(R.id.receiver_row);
+ receiver_voltage_view = (TextView) v.findViewById(R.id.receiver_voltage_value);
+ receiver_voltage_label = (TextView) v.findViewById(R.id.receiver_voltage_label);
+ receiver_voltage_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.receiver_redled),
+ (ImageView) v.findViewById(R.id.receiver_greenled),
+ getResources());
+
+ apogee_row = (TableRow) v.findViewById(R.id.apogee_row);
+ apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value);
+ apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label);
+ apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
(ImageView) v.findViewById(R.id.apogee_greenled),
getResources());
- mMainVoltageView = (TextView) v.findViewById(R.id.main_voltage_value);
- mMainVoltageLabel = (TextView) v.findViewById(R.id.main_voltage_label);
- mMainLights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
+ main_row = (TableRow) v.findViewById(R.id.main_row);
+ main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value);
+ main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label);
+ main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
(ImageView) v.findViewById(R.id.main_greenled),
getResources());
- mDataLoggingView = (TextView) v.findViewById(R.id.logging_value);
- mDataLoggingLights = new GoNoGoLights((ImageView) v.findViewById(R.id.logging_redled),
+ data_logging_view = (TextView) v.findViewById(R.id.logging_value);
+ data_logging_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.logging_redled),
(ImageView) v.findViewById(R.id.logging_greenled),
getResources());
- mGPSLockedView = (TextView) v.findViewById(R.id.gps_locked_value);
- mGPSLockedLights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_locked_redled),
+ gps_locked_view = (TextView) v.findViewById(R.id.gps_locked_value);
+ gps_locked_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_locked_redled),
(ImageView) v.findViewById(R.id.gps_locked_greenled),
getResources());
- mGPSReadyView = (TextView) v.findViewById(R.id.gps_ready_value);
- mGPSReadyLights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_ready_redled),
+ gps_ready_view = (TextView) v.findViewById(R.id.gps_ready_value);
+ gps_ready_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_ready_redled),
(ImageView) v.findViewById(R.id.gps_ready_greenled),
getResources());
- mPadLatitudeView = (TextView) v.findViewById(R.id.pad_lat_value);
- mPadLongitudeView = (TextView) v.findViewById(R.id.pad_lon_value);
- mPadAltitudeView = (TextView) v.findViewById(R.id.pad_alt_value);
- return v;
- }
+ for (int i = 0; i < 4; i++) {
+ int row_id, view_id, label_id, lights_id;
+ int red_id, green_id;
+ switch (i) {
+ case 0:
+ default:
+ row_id = R.id.ignite_a_row;
+ view_id = R.id.ignite_a_voltage_value;
+ label_id = R.id.ignite_a_voltage_label;
+ red_id = R.id.ignite_a_redled;
+ green_id = R.id.ignite_a_greenled;
+ break;
+ case 1:
+ row_id = R.id.ignite_b_row;
+ view_id = R.id.ignite_b_voltage_value;
+ label_id = R.id.ignite_b_voltage_label;
+ red_id = R.id.ignite_b_redled;
+ green_id = R.id.ignite_b_greenled;
+ break;
+ case 2:
+ row_id = R.id.ignite_c_row;
+ view_id = R.id.ignite_c_voltage_value;
+ label_id = R.id.ignite_c_voltage_label;
+ red_id = R.id.ignite_c_redled;
+ green_id = R.id.ignite_c_greenled;
+ break;
+ case 3:
+ row_id = R.id.ignite_d_row;
+ view_id = R.id.ignite_d_voltage_value;
+ label_id = R.id.ignite_d_voltage_label;
+ red_id = R.id.ignite_d_redled;
+ green_id = R.id.ignite_d_greenled;
+ break;
+ }
+ ignite_row[i] = (TableRow) v.findViewById(row_id);
+ ignite_voltage_view[i] = (TextView) v.findViewById(view_id);
+ ignite_voltage_label[i] = (TextView) v.findViewById(label_id);
+ ignite_lights[i] = new GoNoGoLights((ImageView) v.findViewById(red_id),
+ (ImageView) v.findViewById(green_id),
+ getResources());
+ }
- @Override
- public void onDestroy() {
- super.onDestroy();
- mAltosDroid.unregisterTab(this);
- mAltosDroid = null;
+ receiver_latitude_view = (TextView) v.findViewById(R.id.receiver_lat_value);
+ receiver_longitude_view = (TextView) v.findViewById(R.id.receiver_lon_value);
+ receiver_altitude_view = (TextView) v.findViewById(R.id.receiver_alt_value);
+ return v;
}
- public String tab_name() { return "pad"; }
+ public String tab_name() { return AltosDroid.tab_pad_name; }
- public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+ public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
if (state != null) {
- mBatteryVoltageView.setText(AltosDroid.number("%4.2f V", state.battery_voltage));
- mBatteryLights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING);
+ battery_voltage_view.setText(AltosDroid.number(" %4.2f V", state.battery_voltage));
+ battery_lights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING);
if (state.apogee_voltage == AltosLib.MISSING) {
- mApogeeVoltageView.setVisibility(View.GONE);
- mApogeeVoltageLabel.setVisibility(View.GONE);
+ apogee_row.setVisibility(View.GONE);
} else {
- mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
- mApogeeVoltageView.setVisibility(View.VISIBLE);
- mApogeeVoltageLabel.setVisibility(View.VISIBLE);
+ apogee_voltage_view.setText(AltosDroid.number(" %4.2f V", state.apogee_voltage));
+ apogee_row.setVisibility(View.VISIBLE);
}
- mApogeeLights.set(state.apogee_voltage >= AltosLib.ao_igniter_good, state.apogee_voltage == AltosLib.MISSING);
+ apogee_lights.set(state.apogee_voltage >= AltosLib.ao_igniter_good, state.apogee_voltage == AltosLib.MISSING);
if (state.main_voltage == AltosLib.MISSING) {
- mMainVoltageView.setVisibility(View.GONE);
- mMainVoltageLabel.setVisibility(View.GONE);
+ main_row.setVisibility(View.GONE);
} else {
- mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage));
- mMainVoltageView.setVisibility(View.VISIBLE);
- mMainVoltageLabel.setVisibility(View.VISIBLE);
+ main_voltage_view.setText(AltosDroid.number(" %4.2f V", state.main_voltage));
+ main_row.setVisibility(View.VISIBLE);
+ }
+ main_lights.set(state.main_voltage >= AltosLib.ao_igniter_good, state.main_voltage == AltosLib.MISSING);
+
+ int num_igniter = state.ignitor_voltage == null ? 0 : state.ignitor_voltage.length;
+
+ for (int i = 0; i < 4; i++) {
+ double voltage = i >= num_igniter ? AltosLib.MISSING : state.ignitor_voltage[i];
+ if (voltage == AltosLib.MISSING) {
+ ignite_row[i].setVisibility(View.GONE);
+ } else {
+ ignite_voltage_view[i].setText(AltosDroid.number(" %4.2f V", voltage));
+ ignite_row[i].setVisibility(View.VISIBLE);
+ }
+ ignite_lights[i].set(voltage >= AltosLib.ao_igniter_good, voltage == AltosLib.MISSING);
}
- mMainLights.set(state.main_voltage >= AltosLib.ao_igniter_good, state.main_voltage == AltosLib.MISSING);
if (state.flight != 0) {
if (state.state <= AltosLib.ao_flight_pad)
- mDataLoggingView.setText("Ready to record");
+ data_logging_view.setText("Ready to record");
else if (state.state < AltosLib.ao_flight_landed)
- mDataLoggingView.setText("Recording data");
+ data_logging_view.setText("Recording data");
else
- mDataLoggingView.setText("Recorded data");
+ data_logging_view.setText("Recorded data");
} else {
- mDataLoggingView.setText("Storage full");
+ data_logging_view.setText("Storage full");
}
- mDataLoggingLights.set(state.flight != 0, state.flight == AltosLib.MISSING);
+ data_logging_lights.set(state.flight != 0, state.flight == AltosLib.MISSING);
if (state.gps != null) {
int soln = state.gps.nsat;
int nsat = state.gps.cc_gps_sat != null ? state.gps.cc_gps_sat.length : 0;
- mGPSLockedView.setText(String.format("%4d in soln, %4d in view", soln, nsat));
- mGPSLockedLights.set(state.gps.locked && state.gps.nsat >= 4, false);
+ gps_locked_view.setText(String.format("%d in soln, %d in view", soln, nsat));
+ gps_locked_lights.set(state.gps.locked && state.gps.nsat >= 4, false);
if (state.gps_ready)
- mGPSReadyView.setText("Ready");
+ gps_ready_view.setText("Ready");
else
- mGPSReadyView.setText(AltosDroid.integer("Waiting %d", state.gps_waiting));
+ gps_ready_view.setText(AltosDroid.integer("Waiting %d", state.gps_waiting));
} else
- mGPSLockedLights.set(false, true);
- mGPSReadyLights.set(state.gps_ready, state.gps == null);
+ gps_locked_lights.set(false, true);
+ gps_ready_lights.set(state.gps_ready, state.gps == null);
+ }
+
+ if (telem_state != null) {
+ if (telem_state.receiver_battery == AltosLib.MISSING) {
+ receiver_row.setVisibility(View.GONE);
+ } else {
+ receiver_voltage_view.setText(AltosDroid.number(" %4.2f V", telem_state.receiver_battery));
+ receiver_row.setVisibility(View.VISIBLE);
+ }
+ receiver_voltage_lights.set(telem_state.receiver_battery >= AltosLib.ao_battery_good, telem_state.receiver_battery == AltosLib.MISSING);
}
if (receiver != null) {
double altitude = AltosLib.MISSING;
if (receiver.hasAltitude())
altitude = receiver.getAltitude();
- mPadLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
- mPadLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
- set_value(mPadAltitudeView, AltosConvert.height, 6, altitude);
+ receiver_latitude_view.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
+ receiver_longitude_view.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
+ set_value(receiver_altitude_view, AltosConvert.height, 1, altitude);
}
}
-
}
--- /dev/null
+/*
+ * Copyright © 2013 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import org.altusmetrum.altoslib_7.*;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.location.Location;
+
+public class TabRecover extends AltosDroidTab {
+ private TextView mBearingView;
+ private TextView mDirectionView;
+ private TextView mDistanceView;
+ private TextView mTargetLatitudeView;
+ private TextView mTargetLongitudeView;
+ private TextView mReceiverLatitudeView;
+ private TextView mReceiverLongitudeView;
+ private TextView mMaxHeightView;
+ private TextView mMaxSpeedView;
+ private TextView mMaxAccelView;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.tab_recover, container, false);
+
+ mBearingView = (TextView) v.findViewById(R.id.bearing_value);
+ mDirectionView = (TextView) v.findViewById(R.id.direction_value);
+ mDistanceView = (TextView) v.findViewById(R.id.distance_value);
+ mTargetLatitudeView = (TextView) v.findViewById(R.id.target_lat_value);
+ mTargetLongitudeView = (TextView) v.findViewById(R.id.target_lon_value);
+ mReceiverLatitudeView = (TextView) v.findViewById(R.id.receiver_lat_value);
+ mReceiverLongitudeView = (TextView) v.findViewById(R.id.receiver_lon_value);
+ mMaxHeightView = (TextView) v.findViewById(R.id.max_height_value);
+ mMaxSpeedView = (TextView) v.findViewById(R.id.max_speed_value);
+ mMaxAccelView = (TextView) v.findViewById(R.id.max_accel_value);
+
+ return v;
+ }
+
+ public String tab_name() { return AltosDroid.tab_recover_name; }
+
+ public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+ if (from_receiver != null) {
+ mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
+ set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
+ String direction = AltosDroid.direction(from_receiver, receiver);
+ if (direction == null)
+ mDirectionView.setText("");
+ else
+ mDirectionView.setText(direction);
+ }
+ if (state != null && state.gps != null) {
+ mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+ mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
+ }
+
+ if (receiver != null) {
+ mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
+ mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
+ }
+
+ if (state != null) {
+ set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height());
+ set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration());
+ set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed());
+ }
+ }
+}
import android.view.ViewGroup;
import android.widget.TabHost;
import android.widget.TabWidget;
-import android.util.Log;
/**
* This is a helper class that implements the management of tabs and all
@Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
- Log.d(AltosDroid.TAG, String.format("TabsAdapter.getItem(%d)", position));
+ AltosDebug.debug("TabsAdapter.getItem(%d)", position);
info.fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
return info.fragment;
}
if (cur_frag != null) {
cur_frag.set_visible(true);
}
- Log.d(AltosDroid.TAG, String.format("TabsAdapter.onTabChanged(%s) = %d", tabId, position));
+ AltosDebug.debug("TabsAdapter.onTabChanged(%s) = %d", tabId, position);
mViewPager.setCurrentItem(position);
}
package org.altusmetrum.AltosDroid;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Environment;
-import android.util.Log;
public class TelemetryLogger {
- private static final String TAG = "TelemetryLogger";
- private static final boolean D = true;
-
private Context context = null;
private AltosLink link = null;
private AltosLog logger = null;
private void close() {
if (logger != null) {
- if (D) Log.d(TAG, "Shutting down Telemetry Logging");
+ AltosDebug.debug("Shutting down Telemetry Logging");
logger.close();
logger = null;
}
}
-
+
void handleExternalStorageState() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
if (logger == null) {
- if (D) Log.d(TAG, "Starting up Telemetry Logging");
+ AltosDebug.debug("Starting up Telemetry Logging");
logger = new AltosLog(link);
}
} else {
- if (D) Log.d(TAG, "External Storage not present - stopping");
+ AltosDebug.debug("External Storage not present - stopping");
close();
}
}
import java.text.*;
import java.io.*;
+import java.util.*;
import java.util.concurrent.*;
-import android.util.Log;
import android.os.Handler;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class TelemetryReader extends Thread {
- private static final String TAG = "TelemetryReader";
- private static final boolean D = true;
-
int crc_errors;
Handler handler;
AltosLink link;
- AltosState state = null;
LinkedBlockingQueue<AltosLine> telemQueue;
- public AltosState read() throws ParseException, AltosCRCException, InterruptedException, IOException {
+ public AltosTelemetry read() throws ParseException, AltosCRCException, InterruptedException, IOException {
AltosLine l = telemQueue.take();
if (l.line == null)
throw new IOException("IO error");
AltosTelemetry telem = AltosTelemetryLegacy.parse(l.line);
- if (state == null)
- state = new AltosState();
- else
- state = state.clone();
- telem.update_state(state);
- return state;
+ return telem;
}
public void close() {
- state = null;
link.remove_monitor(telemQueue);
link = null;
telemQueue.clear();
}
public void run() {
- AltosState state = null;
-
try {
- if (D) Log.d(TAG, "starting loop");
+ AltosDebug.debug("starting loop");
while (telemQueue != null) {
try {
- state = read();
- handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget();
+ AltosTelemetry telem = read();
+ handler.obtainMessage(TelemetryService.MSG_TELEMETRY, telem).sendToTarget();
} catch (ParseException pp) {
- Log.e(TAG, String.format("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage()));
+ AltosDebug.error("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage());
} catch (AltosCRCException ce) {
++crc_errors;
handler.obtainMessage(TelemetryService.MSG_CRC_ERROR, new Integer(crc_errors)).sendToTarget();
}
} catch (InterruptedException ee) {
} catch (IOException ie) {
+ AltosDebug.error("IO exception in telemetry reader");
+ handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, link).sendToTarget();
} finally {
close();
}
}
- public TelemetryReader (AltosLink in_link, Handler in_handler, AltosState in_state) {
- if (D) Log.d(TAG, "connected TelemetryReader create started");
+ public TelemetryReader (AltosLink in_link, Handler in_handler) {
+ AltosDebug.debug("connected TelemetryReader create started");
link = in_link;
handler = in_handler;
- state = in_state;
telemQueue = new LinkedBlockingQueue<AltosLine>();
link.add_monitor(telemQueue);
link.set_telemetry(AltosLib.ao_telemetry_standard);
- if (D) Log.d(TAG, "connected TelemetryReader created");
+ AltosDebug.debug("connected TelemetryReader created");
}
}
package org.altusmetrum.AltosDroid;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import java.util.concurrent.TimeoutException;
-import java.util.Timer;
-import java.util.TimerTask;
+import java.util.*;
import android.app.Notification;
//import android.app.NotificationManager;
import android.app.Service;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothAdapter;
+import android.hardware.usb.*;
import android.content.Intent;
import android.content.Context;
import android.os.Bundle;
import android.os.Messenger;
import android.os.RemoteException;
import android.os.Looper;
-import android.util.Log;
import android.widget.Toast;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationListener;
import android.location.Criteria;
-import org.altusmetrum.altoslib_6.*;
-
+import org.altusmetrum.altoslib_7.*;
public class TelemetryService extends Service implements LocationListener {
- private static final String TAG = "TelemetryService";
- private static final boolean D = true;
-
static final int MSG_REGISTER_CLIENT = 1;
static final int MSG_UNREGISTER_CLIENT = 2;
static final int MSG_CONNECT = 3;
- static final int MSG_CONNECTED = 4;
- static final int MSG_CONNECT_FAILED = 5;
- static final int MSG_DISCONNECTED = 6;
- static final int MSG_TELEMETRY = 7;
- static final int MSG_SETFREQUENCY = 8;
- static final int MSG_CRC_ERROR = 9;
- static final int MSG_SETBAUD = 10;
- static final int MSG_DISCONNECT = 11;
+ static final int MSG_OPEN_USB = 4;
+ static final int MSG_CONNECTED = 5;
+ static final int MSG_CONNECT_FAILED = 6;
+ static final int MSG_DISCONNECTED = 7;
+ static final int MSG_TELEMETRY = 8;
+ static final int MSG_SETFREQUENCY = 9;
+ static final int MSG_CRC_ERROR = 10;
+ static final int MSG_SETBAUD = 11;
+ static final int MSG_DISCONNECT = 12;
+ static final int MSG_DELETE_SERIAL = 13;
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
// Name of the connected device
DeviceAddress address;
- private AltosBluetooth altos_bluetooth = null;
+ private AltosDroidLink altos_link = null;
private TelemetryReader telemetry_reader = null;
private TelemetryLogger telemetry_logger = null;
@Override
public void handleMessage(Message msg) {
TelemetryService s = service.get();
+ AltosDroidLink bt = null;
if (s == null)
return;
switch (msg.what) {
s.remove_client(msg.replyTo);
break;
case MSG_CONNECT:
- if (D) Log.d(TAG, "Connect command received");
+ AltosDebug.debug("Connect command received");
DeviceAddress address = (DeviceAddress) msg.obj;
AltosDroidPreferences.set_active_device(address);
- s.start_altos_bluetooth(address);
+ s.start_altos_bluetooth(address, false);
+ break;
+ case MSG_OPEN_USB:
+ AltosDebug.debug("Open USB command received");
+ UsbDevice device = (UsbDevice) msg.obj;
+ s.start_usb(device);
break;
case MSG_DISCONNECT:
- if (D) Log.d(TAG, "Disconnect command received");
+ AltosDebug.debug("Disconnect command received");
s.address = null;
- s.stop_altos_bluetooth(true);
+ s.disconnect(true);
+ break;
+ case MSG_DELETE_SERIAL:
+ AltosDebug.debug("Delete Serial command received");
+ s.delete_serial((Integer) msg.obj);
break;
case MSG_SETFREQUENCY:
- if (D) Log.d(TAG, "MSG_SETFREQUENCY");
+ AltosDebug.debug("MSG_SETFREQUENCY");
s.telemetry_state.frequency = (Double) msg.obj;
if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
try {
- s.altos_bluetooth.set_radio_frequency(s.telemetry_state.frequency);
- s.altos_bluetooth.save_frequency();
+ s.altos_link.set_radio_frequency(s.telemetry_state.frequency);
+ s.altos_link.save_frequency();
} catch (InterruptedException e) {
} catch (TimeoutException e) {
}
s.send_to_clients();
break;
case MSG_SETBAUD:
- if (D) Log.d(TAG, "MSG_SETBAUD");
+ AltosDebug.debug("MSG_SETBAUD");
s.telemetry_state.telemetry_rate = (Integer) msg.obj;
if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
- s.altos_bluetooth.set_telemetry_rate(s.telemetry_state.telemetry_rate);
- s.altos_bluetooth.save_telemetry_rate();
+ s.altos_link.set_telemetry_rate(s.telemetry_state.telemetry_rate);
+ s.altos_link.save_telemetry_rate();
}
s.send_to_clients();
break;
*Messages from AltosBluetooth
*/
case MSG_CONNECTED:
- if (D) Log.d(TAG, "Connected to device");
+ AltosDebug.debug("MSG_CONNECTED");
+ bt = (AltosDroidLink) msg.obj;
+
+ if (bt != s.altos_link) {
+ AltosDebug.debug("Stale message");
+ break;
+ }
+ AltosDebug.debug("Connected to device");
try {
s.connected();
} catch (InterruptedException ie) {
}
break;
case MSG_CONNECT_FAILED:
+ AltosDebug.debug("MSG_CONNECT_FAILED");
+ bt = (AltosDroidLink) msg.obj;
+
+ if (bt != s.altos_link) {
+ AltosDebug.debug("Stale message");
+ break;
+ }
if (s.address != null) {
- if (D) Log.d(TAG, "Connection failed... retrying");
- s.start_altos_bluetooth(s.address);
+ AltosDebug.debug("Connection failed... retrying");
+ s.start_altos_bluetooth(s.address, true);
} else {
- s.stop_altos_bluetooth(true);
+ s.disconnect(true);
}
break;
case MSG_DISCONNECTED:
- Log.d(TAG, "MSG_DISCONNECTED");
+
+ /* This can be sent by either AltosDroidLink or TelemetryReader */
+ AltosDebug.debug("MSG_DISCONNECTED");
+ bt = (AltosDroidLink) msg.obj;
+
+ if (bt != s.altos_link) {
+ AltosDebug.debug("Stale message");
+ break;
+ }
if (s.address != null) {
- if (D) Log.d(TAG, "Connection lost... retrying");
- s.start_altos_bluetooth(s.address);
+ AltosDebug.debug("Connection lost... retrying");
+ s.start_altos_bluetooth(s.address, true);
} else {
- s.stop_altos_bluetooth(true);
+ s.disconnect(true);
}
break;
* Messages from TelemetryReader
*/
case MSG_TELEMETRY:
- s.telemetry_state.state = (AltosState) msg.obj;
- if (s.telemetry_state.state != null) {
- if (D) Log.d(TAG, "Save state");
- AltosPreferences.set_state(0, s.telemetry_state.state, null);
- }
- if (D) Log.d(TAG, "MSG_TELEMETRY");
- s.send_to_clients();
+ s.telemetry((AltosTelemetry) msg.obj);
break;
case MSG_CRC_ERROR:
// forward crc error messages
s.telemetry_state.crc_errors = (Integer) msg.obj;
- if (D) Log.d(TAG, "MSG_CRC_ERROR");
s.send_to_clients();
break;
default:
}
}
+ /* Handle telemetry packet
+ */
+ private void telemetry(AltosTelemetry telem) {
+ AltosState state;
+
+ if (telemetry_state.states.containsKey(telem.serial))
+ state = telemetry_state.states.get(telem.serial).clone();
+ else
+ state = new AltosState();
+ telem.update_state(state);
+ telemetry_state.states.put(telem.serial, state);
+ if (state != null) {
+ AltosPreferences.set_state(telem.serial, state, null);
+ }
+ send_to_clients();
+ }
+
/* Construct the message to deliver to clients
*/
private Message message() {
if (telemetry_state == null)
- Log.d(TAG, "telemetry_state null!");
- if (telemetry_state.state == null)
- Log.d(TAG, "telemetry_state.state null!");
+ AltosDebug.debug("telemetry_state null!");
+ if (telemetry_state.states == null)
+ AltosDebug.debug("telemetry_state.states null!");
return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state);
}
private void add_client(Messenger client) {
clients.add(client);
- if (D) Log.d(TAG, "Client bound to service");
+ AltosDebug.debug("Client bound to service");
/* On connect, send the current state to the new client
*/
* go ahead and try to reconnect to the device
*/
if (address != null && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) {
- if (D) Log.d(TAG, "Reconnecting now...");
- start_altos_bluetooth(address);
+ AltosDebug.debug("Reconnecting now...");
+ start_altos_bluetooth(address, false);
}
}
*/
private void remove_client(Messenger client) {
clients.remove(client);
- if (D) Log.d(TAG, "Client unbound from service");
+ AltosDebug.debug("Client unbound from service");
/* When the list of clients is empty, stop the service if
* we have no current telemetry source
*/
if (clients.isEmpty() && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) {
- if (!D) Log.d(TAG, "No clients, no connection. Stopping\n");
+ AltosDebug.debug("No clients, no connection. Stopping\n");
stopSelf();
}
}
private void send_to_client(Messenger client, Message m) {
try {
- if (D) Log.d(TAG, String.format("Send message to client %s", client.toString()));
client.send(m);
} catch (RemoteException e) {
- if (D) Log.e(TAG, String.format("Client %s disappeared", client.toString()));
+ AltosDebug.error("Client %s disappeared", client.toString());
remove_client(client);
}
}
private void send_to_clients() {
Message m = message();
- if (D) Log.d(TAG, String.format("Send message to %d clients", clients.size()));
for (Messenger client : clients)
send_to_client(client, m);
}
- private void stop_altos_bluetooth(boolean notify) {
- if (D) Log.d(TAG, "stop_altos_bluetooth(): begin");
+ private void disconnect(boolean notify) {
+ AltosDebug.debug("disconnect(): begin");
+
telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
telemetry_state.address = null;
- if (altos_bluetooth != null)
- altos_bluetooth.closing();
+ if (altos_link != null)
+ altos_link.closing();
+
+ stop_receiver_voltage_timer();
if (telemetry_reader != null) {
- if (D) Log.d(TAG, "stop_altos_bluetooth(): stopping TelemetryReader");
+ AltosDebug.debug("disconnect(): stopping TelemetryReader");
telemetry_reader.interrupt();
try {
telemetry_reader.join();
telemetry_reader = null;
}
if (telemetry_logger != null) {
- if (D) Log.d(TAG, "stop_altos_bluetooth(): stopping TelemetryLogger");
+ AltosDebug.debug("disconnect(): stopping TelemetryLogger");
telemetry_logger.stop();
telemetry_logger = null;
}
- if (altos_bluetooth != null) {
- if (D) Log.d(TAG, "stop_altos_bluetooth(): stopping AltosBluetooth");
- altos_bluetooth.close();
- altos_bluetooth = null;
+ if (altos_link != null) {
+ AltosDebug.debug("disconnect(): stopping AltosDroidLink");
+ altos_link.close();
+ altos_link = null;
}
telemetry_state.config = null;
if (notify) {
- if (D) Log.d(TAG, "stop_altos_bluetooth(): send message to clients");
+ AltosDebug.debug("disconnect(): send message to clients");
send_to_clients();
if (clients.isEmpty()) {
- if (D) Log.d(TAG, "stop_altos_bluetooth(): no clients, terminating");
+ AltosDebug.debug("disconnect(): no clients, terminating");
stopSelf();
}
}
}
- private void start_altos_bluetooth(DeviceAddress address) {
+ private void start_usb(UsbDevice device) {
+ AltosUsb d = new AltosUsb(this, device, handler);
+
+ if (d != null) {
+ disconnect(false);
+ altos_link = d;
+ try {
+ connected();
+ } catch (InterruptedException ie) {
+ }
+ }
+ }
+
+ private void delete_serial(int serial) {
+ telemetry_state.states.remove((Integer) serial);
+ AltosPreferences.remove_state(serial);
+ send_to_clients();
+ }
+
+ private void start_altos_bluetooth(DeviceAddress address, boolean pause) {
// Get the BLuetoothDevice object
BluetoothDevice device = bluetooth_adapter.getRemoteDevice(address.address);
- stop_altos_bluetooth(false);
+ disconnect(false);
this.address = address;
- if (D) Log.d(TAG, String.format("start_altos_bluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress()));
- altos_bluetooth = new AltosBluetooth(device, handler);
+ AltosDebug.debug("start_altos_bluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress());
+ altos_link = new AltosBluetooth(device, handler, pause);
telemetry_state.connect = TelemetryState.CONNECT_CONNECTING;
telemetry_state.address = address;
send_to_clients();
}
+ // Timer for receiver battery voltage monitoring
+ Timer receiver_voltage_timer;
+
+ private void update_receiver_voltage() {
+ if (altos_link != null) {
+ try {
+ double voltage = altos_link.monitor_battery();
+ telemetry_state.receiver_battery = voltage;
+ } catch (InterruptedException ie) {
+ }
+ }
+ }
+
+ private void stop_receiver_voltage_timer() {
+ if (receiver_voltage_timer != null) {
+ receiver_voltage_timer.cancel();
+ receiver_voltage_timer.purge();
+ receiver_voltage_timer = null;
+ }
+ }
+
+ private void start_receiver_voltage_timer() {
+ if (receiver_voltage_timer == null && altos_link.has_monitor_battery()) {
+ receiver_voltage_timer = new Timer();
+ receiver_voltage_timer.scheduleAtFixedRate(new TimerTask() { public void run() {update_receiver_voltage();}}, 1000L, 10000L);
+ }
+ }
+
private void connected() throws InterruptedException {
- if (D) Log.d(TAG, "connected top");
+ AltosDebug.debug("connected top");
+ AltosDebug.check_ui("connected\n");
try {
- if (altos_bluetooth == null)
+ if (altos_link == null)
throw new InterruptedException("no bluetooth");
- telemetry_state.config = altos_bluetooth.config_data();
- altos_bluetooth.set_radio_frequency(telemetry_state.frequency);
- altos_bluetooth.set_telemetry_rate(telemetry_state.telemetry_rate);
+ telemetry_state.config = altos_link.config_data();
+ altos_link.set_radio_frequency(telemetry_state.frequency);
+ altos_link.set_telemetry_rate(telemetry_state.telemetry_rate);
} catch (TimeoutException e) {
// If this timed out, then we really want to retry it, but
// probably safer to just retry the connection from scratch.
- handler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget();
+ AltosDebug.debug("connected timeout");
+ if (address != null) {
+ AltosDebug.debug("connected timeout, retrying");
+ start_altos_bluetooth(address, true);
+ } else {
+ handler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget();
+ disconnect(true);
+ }
return;
}
- if (D) Log.d(TAG, "connected bluetooth configured");
+ AltosDebug.debug("connected bluetooth configured");
telemetry_state.connect = TelemetryState.CONNECT_CONNECTED;
telemetry_state.address = address;
- telemetry_reader = new TelemetryReader(altos_bluetooth, handler, telemetry_state.state);
+ telemetry_reader = new TelemetryReader(altos_link, handler);
telemetry_reader.start();
- if (D) Log.d(TAG, "connected TelemetryReader started");
+ AltosDebug.debug("connected TelemetryReader started");
- telemetry_logger = new TelemetryLogger(this, altos_bluetooth);
+ telemetry_logger = new TelemetryLogger(this, altos_link);
- if (D) Log.d(TAG, "Notify UI of connection");
+ start_receiver_voltage_timer();
+
+ AltosDebug.debug("Notify UI of connection");
send_to_clients();
}
@Override
public void onCreate() {
+
+ AltosDebug.init(this);
+
+ // Initialise preferences
+ AltosDroidPreferences.init(this);
+
// Get local Bluetooth adapter
bluetooth_adapter = BluetoothAdapter.getDefaultAdapter();
Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
}
- // Initialise preferences
- AltosDroidPreferences.init(this);
-
telemetry_state = new TelemetryState();
// Create a reference to the NotificationManager so that we can update our notifcation text later
telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
telemetry_state.address = null;
- AltosSavedState saved_state = AltosPreferences.state(0);
+ /* Pull the saved state information out of the preferences database
+ */
+ ArrayList<Integer> serials = AltosPreferences.list_states();
+
+ telemetry_state.latest_serial = AltosPreferences.latest_state();
- if (saved_state != null) {
- if (D) Log.d(TAG, String.format("recovered old state flight %d\n", saved_state.state.flight));
- telemetry_state.state = saved_state.state;
+ for (int serial : serials) {
+ AltosSavedState saved_state = AltosPreferences.state(serial);
+ if (saved_state != null) {
+ if (serial == 0) {
+ serial = saved_state.state.serial;
+ AltosPreferences.set_state(serial, saved_state.state, saved_state.listener_state);
+ AltosPreferences.remove_state(0);
+ }
+ if (telemetry_state.latest_serial == 0)
+ telemetry_state.latest_serial = serial;
+
+ AltosDebug.debug("recovered old state serial %d flight %d\n",
+ serial,
+ saved_state.state.flight);
+ if (saved_state.state.gps != null)
+ AltosDebug.debug("\tposition %f,%f\n",
+ saved_state.state.gps.lat,
+ saved_state.state.gps.lon);
+ telemetry_state.states.put(serial, saved_state.state);
+ }
}
// Listen for GPS and Network position updates
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
-
- DeviceAddress address = AltosDroidPreferences.active_device();
- if (address != null)
- start_altos_bluetooth(address);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- Log.i("TelemetryService", "Received start id " + startId + ": " + intent);
+ AltosDebug.debug("Received start id %d: %s", startId, intent);
CharSequence text = getText(R.string.telemetry_service_started);
// Move us into the foreground.
startForeground(NOTIFICATION, notification);
+ /* Start bluetooth if we don't have a connection already */
+ if (intent != null &&
+ (telemetry_state.connect == TelemetryState.CONNECT_NONE ||
+ telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED))
+ {
+ String action = intent.getAction();
+
+ if (action.equals(AltosDroid.ACTION_BLUETOOTH)) {
+ DeviceAddress address = AltosDroidPreferences.active_device();
+ if (address != null && !address.address.startsWith("USB"))
+ start_altos_bluetooth(address, false);
+ }
+ }
+
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
// Stop the bluetooth Comms threads
- stop_altos_bluetooth(true);
+ disconnect(true);
// Demote us from the foreground, and cancel the persistent notification.
stopForeground(true);
public void onLocationChanged(Location location) {
telemetry_state.location = location;
- if (D) Log.d(TAG, "location changed");
+ AltosDebug.debug("location changed");
send_to_clients();
}
package org.altusmetrum.AltosDroid;
-import org.altusmetrum.altoslib_6.*;
+import java.util.*;
+import org.altusmetrum.altoslib_7.*;
import android.location.Location;
public class TelemetryState {
int connect;
DeviceAddress address;
AltosConfigData config;
- AltosState state;
Location location;
int crc_errors;
+ double receiver_battery;
double frequency;
int telemetry_rate;
+ HashMap<Integer,AltosState> states;
+
+ int latest_serial;
+
public TelemetryState() {
connect = CONNECT_NONE;
config = null;
- state = null;
+ states = new HashMap<Integer,AltosState>();
location = null;
crc_errors = 0;
+ receiver_battery = AltosLib.MISSING;
frequency = AltosPreferences.frequency(0);
telemetry_rate = AltosPreferences.telemetry_rate(0);
}
bin
classaltoslib.stamp
altoslib*.jar
+AltosVersion.java
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosCRCException extends Exception {
public int rssi;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.*;
import java.text.*;
/* HAS_APRS */
public int aprs_interval;
public int aprs_ssid;
+ public int aprs_format;
/* HAS_BEEP */
public int beep;
aprs_interval = -1;
aprs_ssid = -1;
+ aprs_format = -1;
beep = -1;
/* HAS_APRS */
try { aprs_interval = get_int(line, "APRS interval:"); } catch (Exception e) {}
try { aprs_ssid = get_int(line, "APRS SSID:"); } catch (Exception e) {}
+ try { aprs_format = get_int(line, "APRS format:"); } catch (Exception e) {}
/* HAS_BEEP */
try { beep = get_int(line, "Beeper setting:"); } catch (Exception e) {}
aprs_interval = source.aprs_interval();
if (aprs_ssid >= 0)
aprs_ssid = source.aprs_ssid();
+ if (aprs_format >= 0)
+ aprs_format = source.aprs_format();
/* HAS_BEEP */
if (beep >= 0)
dest.set_pyro_firing_time(pyro_firing_time);
dest.set_aprs_interval(aprs_interval);
dest.set_aprs_ssid(aprs_ssid);
+ dest.set_aprs_format(aprs_format);
dest.set_beep(beep);
dest.set_tracker_motion(tracker_motion);
dest.set_tracker_interval(tracker_interval);
link.printf("c A %d\n", aprs_interval);
if (aprs_ssid >= 0)
link.printf("c S %d\n", aprs_ssid);
+ if (aprs_format >= 0)
+ link.printf("c C %d\n", aprs_format);
/* HAS_BEEP */
if (beep >= 0)
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosConfigDataException extends Exception {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public interface AltosConfigValues {
/* set and get all of the dialog values */
public abstract void set_aprs_ssid(int new_aprs_ssid);
+ public abstract int aprs_format() throws AltosConfigDataException;
+
+ public abstract void set_aprs_format(int new_aprs_format);
+
public abstract int beep() throws AltosConfigDataException;
public abstract void set_beep(int new_beep);
/*
* Sensor data conversion functions
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosConvert {
/*
return sensor / 32767.0 * supply * (5.6 + 10.0) / 10.0;
}
+ static double tele_bt_3_battery(int raw) {
+ if (raw == AltosLib.MISSING)
+ return AltosLib.MISSING;
+ return 3.3 * mega_adc(raw) * (5.1 + 10.0) / 10.0;
+ }
+
static double easy_mini_voltage(int sensor, int serial) {
double supply = 3.3;
double diode_offset = 0.0;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosDistance extends AltosUnits {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.text.*;
import java.util.concurrent.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.text.*;
import java.util.concurrent.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public interface AltosEepromMonitor {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.File;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public interface AltosFlashListener {
public void position(String label, int percent);
--- /dev/null
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+public interface AltosFlightDisplay extends AltosUnitsListener, AltosFontListener {
+ void reset();
+
+ void show(AltosState state, AltosListenerState listener_state);
+
+ String getName();
+}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.text.*;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
--- /dev/null
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+public interface AltosFontListener {
+ void font_size_changed(int font_size);
+}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosFrequency {
public double frequency;
public String description;
+ public int hashCode() {
+ return new Double(frequency).hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+ if (!(o instanceof AltosFrequency))
+ return false;
+ AltosFrequency other = (AltosFrequency) o;
+ return other.frequency == frequency;
+ }
+
public String toString() {
return String.format("%7.3f MHz %-20s",
frequency, description);
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.text.*;
import java.util.concurrent.*;
lon = AltosParse.parse_coord(words[i++]);
alt = AltosParse.parse_int(words[i++]);
if (version > 1 || (i < words.length && !words[i].equals("SAT"))) {
- ground_speed = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(H)"));
+ ground_speed = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "m/s(H)"));
course = AltosParse.parse_int(words[i++]);
- climb_rate = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(V)"));
- hdop = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "(hdop)"));
+ climb_rate = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "m/s(V)"));
+ hdop = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "(hdop)"));
h_error = AltosParse.parse_int(words[i++]);
v_error = AltosParse.parse_int(words[i++]);
}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosGPSSat {
public int svid;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.lang.Math;
import java.io.*;
double sqr(double a) { return a * a; }
- static final double rad = Math.PI / 180;
- static final double earth_radius = 6371.2 * 1000; /* in meters */
+ public static final double rad = Math.PI / 180;
+ public static final double earth_radius = 6371.2 * 1000; /* in meters */
public static final int BEARING_LONG = AltosConvert.BEARING_LONG;
public static final int BEARING_SHORT = AltosConvert.BEARING_SHORT;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosHeight extends AltosUnits {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.LinkedList;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosHexsym {
String name;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.concurrent.*;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
state.set_callsign(config_data.callsign);
state.set_ground_accel(config_data.accel_cal_plus);
state.set_accel_g(config_data.accel_cal_plus, config_data.accel_cal_minus);
+ state.set_product(config_data.product);
for (AltosIdler idler : idlers) {
if (idler.matches(config_data)) {
idler.update_state(state, link, config_data);
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.concurrent.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public interface AltosIdleMonitorListener {
public void update(AltosState state, AltosListenerState listener_state);
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.*;
import java.io.*;
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+
+public interface AltosImage {
+ /* Discard storage for image */
+ public abstract void flush();
+}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
+import java.util.*;
+
+class KMLWriter extends PrintWriter {
+ public PrintWriter printf(String format, Object ... arguments) {
+ return printf(Locale.ROOT, format, arguments);
+ }
+
+ public KMLWriter(File name) throws FileNotFoundException {
+ super(name);
+ }
+}
public class AltosKML implements AltosWriter {
File name;
- PrintStream out;
+ PrintWriter out;
int flight_state = -1;
AltosState prev = null;
double gps_start_altitude;
end();
prev = null;
}
+ if (out != null) {
+ out.close();
+ out = null;
+ }
}
public void write(AltosState state) {
public AltosKML(File in_name) throws FileNotFoundException {
name = in_name;
- out = new PrintStream(name);
+ out = new KMLWriter(name);
}
public AltosKML(String in_string) throws FileNotFoundException {
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+public class AltosLatLon {
+ public double lat;
+ public double lon;
+
+ public int hashCode() {
+ return new Double(lat).hashCode() ^ new Double(lon).hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+ if (!(o instanceof AltosLatLon))
+ return false;
+
+ AltosLatLon other = (AltosLatLon) o;
+ return lat == other.lat && lon == other.lon;
+ }
+
+ public String toString() {
+ return String.format("%f/%f", lat, lon);
+ }
+
+ public AltosLatLon(double lat, double lon) {
+ this.lat = lat;
+ this.lon = lon;
+ }
+}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosLatitude extends AltosLocation {
public String pos() { return "N"; }
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.lang.*;
+import java.util.*;
+import java.text.*;
+import java.util.concurrent.*;
+
+public class AltosLaunchSite {
+ public String name;
+ public double latitude;
+ public double longitude;
+
+ public String toString() {
+ return name;
+ }
+
+ public AltosLaunchSite(String in_name, double in_latitude, double in_longitude) {
+ name = in_name;
+ latitude = in_latitude;
+ longitude = in_longitude;
+ }
+
+ public AltosLaunchSite(String line) throws ParseException {
+ String[] elements = line.split(":");
+
+ if (elements.length < 3)
+ throw new ParseException(String.format("Invalid site line %s", line), 0);
+
+ name = elements[0];
+
+ try {
+ latitude = AltosParse.parse_double_net(elements[1]);
+ longitude = AltosParse.parse_double_net(elements[2]);
+ } catch (ParseException pe) {
+ throw new ParseException(String.format("Invalid site line %s", line), 0);
+ }
+ }
+}
+
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.lang.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+public interface AltosLaunchSiteListener {
+ public abstract void notify_launch_sites(List<AltosLaunchSite> sites);
+}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.lang.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.net.*;
+import java.text.*;
+
+public class AltosLaunchSites extends Thread {
+ URL url;
+ LinkedList<AltosLaunchSite> sites;
+ AltosLaunchSiteListener listener;
+
+ void notify_complete() {
+ listener.notify_launch_sites(sites);
+ }
+
+ void add(AltosLaunchSite site) {
+ sites.add(site);
+ }
+
+ void add(String line) {
+ try {
+ add(new AltosLaunchSite(line));
+ } catch (ParseException pe) {
+ System.out.printf("parse exception %s\n", pe.toString());
+ }
+ }
+
+ public void run() {
+ try {
+ url = new URL(AltosLib.launch_sites_url);
+ URLConnection uc = url.openConnection();
+
+ InputStreamReader in_stream = new InputStreamReader(uc.getInputStream(), AltosLib.unicode_set);
+ BufferedReader in = new BufferedReader(in_stream);
+
+ for (;;) {
+ String line = in.readLine();
+ if (line == null)
+ break;
+ add(line);
+ }
+ } catch (Exception e) {
+ } finally {
+ notify_complete();
+ }
+ }
+
+ public AltosLaunchSites(AltosLaunchSiteListener listener) {
+ sites = new LinkedList<AltosLaunchSite>();
+ this.listener = listener;
+ start();
+ }
+}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.*;
import java.io.*;
38400, 9600, 2400
};
+ public static final int ao_aprs_format_compressed = 0;
+ public static final int ao_aprs_format_uncompressed = 1;
+
+ public static final String[] ao_aprs_format_name = {
+ "Compressed", "Uncompressed"
+ };
+
public static final String launch_sites_url = "http://www.altusmetrum.org/AltOS/launch-sites.txt";
// public static final String launch_sites_url = "file:///home/keithp/misc/text/altusmetrum/AltOS/launch-sites.txt";
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosLine {
public String line;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.concurrent.*;
public boolean reply_abort;
public int in_reply;
+ boolean cancel_enable = true;
+
+ public void set_cancel_enable(boolean e) {
+ cancel_enable = e;
+ }
boolean reply_timeout_shown = false;
private boolean check_reply_timeout() {
+ if (!cancel_enable)
+ return false;
if (!reply_timeout_shown)
reply_timeout_shown = show_reply_timeout();
return reply_abort;
if (frequency == 0)
return;
if (has_frequency)
- set_radio_freq((int) Math.floor (frequency * 1000));
+ set_radio_freq((int) Math.floor (frequency * 1000 + 0.5));
else if (has_setting)
set_radio_setting(AltosConvert.radio_frequency_to_setting(frequency, cal));
else
}
if (monitor_batt == AltosLib.MISSING)
return AltosLib.MISSING;
- return AltosConvert.cc_battery_to_voltage(monitor_batt);
+
+ double volts = AltosLib.MISSING;
+ if (config_data.product.startsWith("TeleBT-v3")) {
+ volts = AltosConvert.tele_bt_3_battery(monitor_batt);
+ } else {
+ volts = AltosConvert.cc_battery_to_voltage(monitor_batt);
+ }
+
+ return volts;
}
public AltosLink() {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public abstract class AltosLocation extends AltosUnits {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.text.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosLongitude extends AltosLocation {
public String pos() { return "E"; }
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.concurrent.*;
import java.io.*;
--- /dev/null
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.lang.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+public class AltosMap implements AltosMapTileListener, AltosMapStoreListener {
+
+ public static final int px_size = 512;
+
+ public static final int maptype_hybrid = 0;
+ public static final int maptype_roadmap = 1;
+ public static final int maptype_satellite = 2;
+ public static final int maptype_terrain = 3;
+ public static final int maptype_default = maptype_hybrid;
+
+ public static final int default_zoom = 15;
+ public static final int min_zoom = 3;
+ public static final int max_zoom = 21;
+
+ public static final String[] maptype_names = {
+ "hybrid",
+ "roadmap",
+ "satellite",
+ "terrain"
+ };
+
+ public static final String[] maptype_labels = {
+ "Hybrid",
+ "Roadmap",
+ "Satellite",
+ "Terrain"
+ };
+
+ AltosMapInterface map_interface;
+
+ AltosMapCache cache;
+
+ public AltosMapCache cache() { return cache; }
+
+ LinkedList<AltosMapMark> marks = new LinkedList<AltosMapMark>();
+
+ AltosMapPath path;
+ AltosMapLine line;
+ public AltosLatLon last_position;
+
+ boolean have_boost = false;
+ boolean have_landed = false;
+
+ ConcurrentHashMap<AltosPointInt,AltosMapTile> tiles = new ConcurrentHashMap<AltosPointInt,AltosMapTile>();
+ int load_radius;
+ AltosLatLon load_centre = null;
+ AltosMapTileListener load_listener;
+
+ int zoom = AltosMap.default_zoom;
+ int maptype = AltosMap.maptype_default;
+
+ long user_input_time;
+
+ /* Milliseconds to wait after user action before auto-scrolling
+ */
+ static final long auto_scroll_delay = 20 * 1000;
+
+ public AltosMapTransform transform;
+ AltosLatLon centre;
+
+ public void reset() {
+ // nothing
+ }
+
+ /* MapInterface wrapping functions */
+
+ public void repaint(int x, int y, int w, int h) {
+ map_interface.repaint(new AltosRectangle(x, y, w, h));
+ }
+
+ public void repaint(AltosMapRectangle damage, int pad) {
+ AltosRectangle r = transform.screen(damage);
+ repaint(r.x - pad, r.y - pad, r.width + pad * 2, r.height + pad * 2);
+ }
+
+ public void repaint() {
+ map_interface.repaint();
+ }
+
+ public int width() {
+ return map_interface.width();
+ }
+
+ public int height() {
+ return map_interface.height();
+ }
+
+ public void debug(String format, Object ... arguments) {
+ map_interface.debug(format, arguments);
+ }
+
+ static public AltosPointInt floor(AltosPointDouble point) {
+ return new AltosPointInt ((int) Math.floor(point.x / AltosMap.px_size) * AltosMap.px_size,
+ (int) Math.floor(point.y / AltosMap.px_size) * AltosMap.px_size);
+ }
+
+ static public AltosPointInt ceil(AltosPointDouble point) {
+ return new AltosPointInt ((int) Math.ceil(point.x / AltosMap.px_size) * AltosMap.px_size,
+ (int) Math.ceil(point.y / AltosMap.px_size) * AltosMap.px_size);
+ }
+
+ public void notice_user_input() {
+ user_input_time = System.currentTimeMillis();
+ }
+
+ public boolean recent_user_input() {
+ return (System.currentTimeMillis() - user_input_time) < auto_scroll_delay;
+ }
+
+ public boolean has_centre() {
+ return centre != null;
+ }
+
+ public boolean far_from_centre(AltosLatLon lat_lon) {
+
+ if (centre == null || transform == null)
+ return true;
+
+ AltosPointDouble screen = transform.screen(lat_lon);
+
+ int width = width();
+ int dx = Math.abs ((int) (double) screen.x - width/2);
+
+ if (dx > width / 4)
+ return true;
+
+ int height = height();
+ int dy = Math.abs ((int) (double) screen.y - height/2);
+
+ if (dy > height / 4)
+ return true;
+
+ return false;
+ }
+
+ public void set_transform() {
+ if (centre != null) {
+ transform = new AltosMapTransform(width(), height(), zoom, centre);
+ repaint();
+ }
+ }
+
+ private void set_zoom_label() {
+ map_interface.set_zoom_label(String.format("Zoom %d", get_zoom() - default_zoom));
+ }
+
+
+ public boolean set_zoom(int zoom) {
+ notice_user_input();
+ if (AltosMap.min_zoom <= zoom && zoom <= AltosMap.max_zoom && zoom != this.zoom) {
+ this.zoom = zoom;
+ tiles.clear();
+ set_transform();
+ set_zoom_label();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean set_zoom_centre(int zoom, AltosPointInt centre) {
+ AltosLatLon mouse_lat_lon = null;
+ boolean ret;
+
+ if (transform != null)
+ mouse_lat_lon = transform.screen_lat_lon(centre);
+
+ ret = set_zoom(zoom);
+
+ if (ret && mouse_lat_lon != null) {
+ AltosPointDouble new_mouse = transform.screen(mouse_lat_lon);
+
+ double dx = width()/2.0 - centre.x;
+ double dy = height()/2.0 - centre.y;
+
+ AltosLatLon new_centre = transform.screen_lat_lon(new AltosPointDouble(new_mouse.x + dx, new_mouse.y + dy));
+
+ centre(new_centre);
+ }
+
+ return ret;
+ }
+
+ public int get_zoom() {
+ return zoom;
+ }
+
+ public boolean set_maptype(int maptype) {
+ if (maptype != this.maptype) {
+ this.maptype = maptype;
+ tiles.clear();
+ repaint();
+ return true;
+ }
+ return false;
+ }
+
+ public void show(AltosState state, AltosListenerState listener_state) {
+
+ /* If insufficient gps data, nothing to update
+ */
+ AltosGPS gps = state.gps;
+
+ if (gps == null)
+ return;
+
+ if (!gps.locked && gps.nsat < 4)
+ return;
+
+ switch (state.state) {
+ case AltosLib.ao_flight_boost:
+ if (!have_boost) {
+ add_mark(gps.lat, gps.lon, state.state);
+ have_boost = true;
+ }
+ break;
+ case AltosLib.ao_flight_landed:
+ if (!have_landed) {
+ add_mark(gps.lat, gps.lon, state.state);
+ have_landed = true;
+ }
+ break;
+ }
+
+ if (path != null) {
+ AltosMapRectangle damage = path.add(gps.lat, gps.lon, state.state);
+
+ if (damage != null)
+ repaint(damage, AltosMapPath.stroke_width);
+ }
+
+ last_position = new AltosLatLon(gps.lat, gps.lon);
+
+ maybe_centre(gps.lat, gps.lon);
+ }
+
+ public void centre(AltosLatLon lat_lon) {
+ centre = lat_lon;
+ set_transform();
+ }
+
+ public void centre(double lat, double lon) {
+ centre(new AltosLatLon(lat, lon));
+ }
+
+ public void centre(AltosState state) {
+ if (!state.gps.locked && state.gps.nsat < 4)
+ return;
+ centre(state.gps.lat, state.gps.lon);
+ }
+
+ public void maybe_centre(double lat, double lon) {
+ AltosLatLon lat_lon = new AltosLatLon(lat, lon);
+ if (centre == null || (!recent_user_input() && far_from_centre(lat_lon)))
+ centre(lat_lon);
+ }
+
+ public void add_mark(double lat, double lon, int state) {
+ synchronized(marks) {
+ AltosMapMark mark = map_interface.new_mark(lat, lon, state);
+ if (mark != null)
+ marks.add(mark);
+ }
+ repaint();
+ }
+
+ public void clear_marks() {
+ synchronized(marks) {
+ marks.clear();
+ }
+ }
+
+ private void make_tiles() {
+ AltosPointInt upper_left;
+ AltosPointInt lower_right;
+
+ if (load_centre != null) {
+ AltosPointInt centre = floor(transform.point(load_centre));
+
+ upper_left = new AltosPointInt(centre.x - load_radius * AltosMap.px_size,
+ centre.y - load_radius * AltosMap.px_size);
+ lower_right = new AltosPointInt(centre.x + load_radius * AltosMap.px_size,
+ centre.y + load_radius * AltosMap.px_size);
+ } else {
+ upper_left = floor(transform.screen_point(new AltosPointInt(0, 0)));
+ lower_right = floor(transform.screen_point(new AltosPointInt(width(), height())));
+ }
+ for (AltosPointInt point : tiles.keySet()) {
+ if (point.x < upper_left.x || lower_right.x < point.x ||
+ point.y < upper_left.y || lower_right.y < point.y) {
+ tiles.remove(point);
+ }
+ }
+
+ cache.set_cache_size((width() / AltosMap.px_size + 2) * (height() / AltosMap.px_size + 2));
+
+ for (int y = (int) upper_left.y; y <= lower_right.y; y += AltosMap.px_size) {
+ for (int x = (int) upper_left.x; x <= lower_right.x; x += AltosMap.px_size) {
+ AltosPointInt point = new AltosPointInt(x, y);
+
+ if (!tiles.containsKey(point)) {
+ AltosLatLon ul = transform.lat_lon(point);
+ AltosLatLon center = transform.lat_lon(new AltosPointDouble(x + AltosMap.px_size/2, y + AltosMap.px_size/2));
+ AltosMapTile tile = map_interface.new_tile(this, ul, center, zoom, maptype, px_size);
+ tiles.put(point, tile);
+ }
+ }
+ }
+ }
+
+ public void set_load_params(int new_zoom, int new_type, double lat, double lon, int radius, AltosMapTileListener listener) {
+ if (AltosMap.min_zoom <= new_zoom && new_zoom <= AltosMap.max_zoom)
+ zoom = new_zoom;
+ maptype = new_type;
+ load_centre = new AltosLatLon(lat, lon);
+ load_radius = radius;
+ load_listener = listener;
+ centre(lat, lon);
+ tiles.clear();
+ make_tiles();
+ for (AltosMapTile tile : tiles.values()) {
+ tile.add_store_listener(this);
+ if (tile.store_status() != AltosMapTile.loading)
+ listener.notify_tile(tile, tile.store_status());
+ }
+ repaint();
+ }
+
+ public String getName() {
+ return "Map";
+ }
+
+ public void paint() {
+ if (centre != null)
+ make_tiles();
+
+ if (transform == null)
+ return;
+
+ for (AltosMapTile tile : tiles.values())
+ tile.paint(transform);
+
+ synchronized(marks) {
+ for (AltosMapMark mark : marks)
+ mark.paint(transform);
+ }
+
+ if (path != null)
+ path.paint(transform);
+
+ if (line != null)
+ line.paint(transform);
+ }
+
+ /* AltosMapTileListener methods */
+ public synchronized void notify_tile(AltosMapTile tile, int status) {
+ for (AltosPointInt point : tiles.keySet()) {
+ if (tile == tiles.get(point)) {
+ AltosPointInt screen = transform.screen(point);
+ repaint(screen.x, screen.y, AltosMap.px_size, AltosMap.px_size);
+ }
+ }
+ }
+
+ /* AltosMapStoreListener methods */
+ public synchronized void notify_store(AltosMapStore store, int status) {
+ if (load_listener != null) {
+ for (AltosMapTile tile : tiles.values())
+ if (store.equals(tile.store))
+ load_listener.notify_tile(tile, status);
+ }
+ }
+
+ /* UI elements */
+
+ AltosPointInt drag_start;
+
+ boolean dragged;
+
+ static final double drag_far = 20;
+
+ private void drag(int x, int y) {
+ if (drag_start == null)
+ return;
+
+ int dx = x - drag_start.x;
+ int dy = y - drag_start.y;
+
+ double distance = Math.hypot(dx, dy);
+
+ if (distance > drag_far)
+ dragged = true;
+
+ if (transform == null) {
+ debug("Transform not set in drag\n");
+ return;
+ }
+
+ AltosLatLon new_centre = transform.screen_lat_lon(new AltosPointInt(width() / 2 - dx, height() / 2 - dy));
+ centre(new_centre);
+ drag_start = new AltosPointInt(x, y);
+ }
+
+ private void drag_start(int x, int y) {
+ drag_start = new AltosPointInt(x, y);
+ dragged = false;
+ }
+
+ private void drag_stop(int x, int y) {
+ if (!dragged) {
+ if (transform == null) {
+ debug("Transform not set in stop\n");
+ return;
+ }
+ map_interface.select_object (transform.screen_lat_lon(new AltosPointInt(x,y)));
+ }
+ }
+
+ private void line_start(int x, int y) {
+ if (line != null) {
+ line.pressed(new AltosPointInt(x, y), transform);
+ repaint();
+ }
+ }
+
+ private void line(int x, int y) {
+ if (line != null) {
+ line.dragged(new AltosPointInt(x, y), transform);
+ repaint();
+ }
+ }
+
+ public void touch_start(int x, int y, boolean is_drag) {
+ notice_user_input();
+ if (is_drag)
+ drag_start(x, y);
+ else
+ line_start(x, y);
+ }
+
+ public void touch_continue(int x, int y, boolean is_drag) {
+ notice_user_input();
+ if (is_drag)
+ drag(x, y);
+ else
+ line(x, y);
+ }
+
+ public void touch_stop(int x, int y, boolean is_drag) {
+ notice_user_input();
+ if (is_drag)
+ drag_stop(x, y);
+ }
+
+ public AltosMap(AltosMapInterface map_interface) {
+ this.map_interface = map_interface;
+ cache = new AltosMapCache(map_interface);
+ line = map_interface.new_line();
+ path = map_interface.new_path();
+ set_zoom_label();
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.net.*;
+
+public class AltosMapCache implements AltosMapCacheListener {
+
+ /* An entry in the MapCache */
+ class MapCacheElement implements AltosMapStoreListener {
+
+ AltosMapTile tile; /* Notify when image has been loaded */
+ AltosImage image;
+ AltosMapStore store;
+ long used;
+
+ class loader implements Runnable {
+ public void run() {
+ if (image != null)
+ tile.notify_image(image);
+ try {
+ image = map_interface.load_image(store.file);
+ } catch (Exception ex) {
+ }
+ if (image == null)
+ tile.set_status(AltosMapTile.failed);
+ else
+ tile.set_status(AltosMapTile.success);
+ tile.notify_image(image);
+ }
+ }
+
+ private void load() {
+ loader l = new loader();
+ Thread lt = new Thread(l);
+ lt.start();
+ }
+
+ public void flush() {
+ if (image != null) {
+ image.flush();
+ image = null;
+ }
+ }
+
+ public boolean has_map() {
+ return store.status() == AltosMapTile.success;
+ }
+
+ public synchronized void notify_store(AltosMapStore store, int status) {
+ switch (status) {
+ case AltosMapTile.loading:
+ break;
+ case AltosMapTile.success:
+ load();
+ break;
+ default:
+ tile.set_status(status);
+ tile.notify_image(null);
+ }
+ }
+
+ public MapCacheElement(AltosMapTile tile, AltosMapStore store) throws IOException {
+ this.tile = tile;
+ this.image = null;
+ this.store = store;
+ this.used = 0;
+
+ int status = store.status();
+ switch (status) {
+ case AltosMapTile.loading:
+ store.add_listener(this);
+ break;
+ case AltosMapTile.success:
+ load();
+ break;
+ default:
+ tile.set_status(status);
+ tile.notify_image(null);
+ break;
+ }
+ }
+ }
+
+ int min_cache_size; /* configured minimum cache size */
+ int cache_size; /* current cache size */
+ int requested_cache_size; /* cache size computed by application */
+
+ private Object fetch_lock = new Object();
+ private Object cache_lock = new Object();
+
+ AltosMapInterface map_interface;
+
+ MapCacheElement[] elements = new MapCacheElement[cache_size];
+
+ long used;
+
+ public void set_cache_size(int new_size) {
+
+ requested_cache_size = new_size;
+
+ if (new_size < min_cache_size)
+ new_size = min_cache_size;
+
+ if (new_size == cache_size)
+ return;
+
+ synchronized(cache_lock) {
+ MapCacheElement[] new_elements = new MapCacheElement[new_size];
+
+ for (int i = 0; i < cache_size; i++) {
+ if (i < new_size)
+ new_elements[i] = elements[i];
+ else if (elements[i] != null)
+ elements[i].flush();
+ }
+ elements = new_elements;
+ cache_size = new_size;
+ }
+ }
+
+ public AltosImage get(AltosMapTile tile, AltosMapStore store, int width, int height) {
+ int oldest = -1;
+ long age = used;
+
+ synchronized(cache_lock) {
+ MapCacheElement element = null;
+ for (int i = 0; i < cache_size; i++) {
+ element = elements[i];
+
+ if (element == null) {
+ oldest = i;
+ break;
+ }
+ if (store.equals(element.store)) {
+ element.used = used++;
+ return element.image;
+ }
+ if (element.used < age) {
+ oldest = i;
+ age = element.used;
+ }
+ }
+
+ try {
+ element = new MapCacheElement(tile, store);
+ element.used = used++;
+ if (elements[oldest] != null)
+ elements[oldest].flush();
+
+ elements[oldest] = element;
+
+ if (element.image == null)
+ tile.set_status(AltosMapTile.loading);
+ else
+ tile.set_status(AltosMapTile.success);
+
+ return element.image;
+ } catch (IOException e) {
+ tile.set_status(AltosMapTile.failed);
+ return null;
+ }
+ }
+ }
+
+ public void map_cache_changed(int map_cache) {
+ min_cache_size = map_cache;
+
+ set_cache_size(requested_cache_size);
+ }
+
+ public void dispose() {
+ AltosPreferences.unregister_map_cache_listener(this);
+
+ for (int i = 0; i < cache_size; i++) {
+ MapCacheElement element = elements[i];
+
+ if (element != null)
+ element.flush();
+ }
+ }
+
+ public AltosMapCache(AltosMapInterface map_interface) {
+ this.map_interface = map_interface;
+ min_cache_size = AltosPreferences.map_cache();
+
+ set_cache_size(0);
+
+ AltosPreferences.register_map_cache_listener(this);
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+public interface AltosMapCacheListener {
+ public void map_cache_changed(int map_cache);
+}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.net.*;
+
+public interface AltosMapInterface {
+ public abstract AltosMapPath new_path();
+
+ public abstract AltosMapLine new_line();
+
+ public abstract AltosImage load_image(File file) throws Exception;
+
+ public abstract AltosMapMark new_mark(double lat, double lon, int state);
+
+ public abstract AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size);
+
+ public abstract int width();
+
+ public abstract int height();
+
+ public abstract void repaint();
+
+ public abstract void repaint(AltosRectangle damage);
+
+ public abstract void set_zoom_label(String label);
+
+ public abstract void debug(String format, Object ... arguments);
+
+ public abstract void select_object(AltosLatLon latlon);
+}
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+public abstract class AltosMapLine {
+ public AltosLatLon start, end;
+
+ static public int stroke_width = 6;
+
+ public abstract void paint(AltosMapTransform t);
+
+ private AltosLatLon lat_lon(AltosPointInt pt, AltosMapTransform t) {
+ return t.screen_lat_lon(pt);
+ }
+
+ public void dragged(AltosPointInt pt, AltosMapTransform t) {
+ end = lat_lon(pt, t);
+ }
+
+ public void pressed(AltosPointInt pt, AltosMapTransform t) {
+ start = lat_lon(pt, t);
+ end = null;
+ }
+
+ public String line_dist() {
+ String format;
+ AltosGreatCircle g = new AltosGreatCircle(start.lat, start.lon,
+ end.lat, end.lon);
+ double distance = g.distance;
+
+ if (AltosConvert.imperial_units) {
+ distance = AltosConvert.meters_to_feet(distance);
+ if (distance < 10000) {
+ format = "%4.0fft";
+ } else {
+ distance /= 5280;
+ if (distance < 10)
+ format = "%5.3fmi";
+ else if (distance < 100)
+ format = "%5.2fmi";
+ else if (distance < 1000)
+ format = "%5.1fmi";
+ else
+ format = "%5.0fmi";
+ }
+ } else {
+ if (distance < 10000) {
+ format = "%4.0fm";
+ } else {
+ distance /= 1000;
+ if (distance < 100)
+ format = "%5.2fkm";
+ else if (distance < 1000)
+ format = "%5.1fkm";
+ else
+ format = "%5.0fkm";
+ }
+ }
+ return String.format(format, distance);
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import java.lang.Math;
+import java.net.URL;
+import java.net.URLConnection;
+
+public class AltosMapLoader implements AltosMapTileListener, AltosMapStoreListener {
+ AltosMapLoaderListener listener;
+
+ double latitude, longitude;
+ int min_z;
+ int max_z;
+ int cur_z;
+ int all_types;
+ int cur_type;
+ double radius;
+
+ int tiles_loaded_layer;
+ int tiles_loaded_total;
+ int tiles_this_layer;
+ int tiles_total;
+ int layers_total;
+ int layers_loaded;
+
+ AltosMap map;
+
+ int tile_radius(int zoom) {
+ double delta_lon = AltosMapTransform.lon_from_distance(latitude, radius);
+
+ AltosMapTransform t = new AltosMapTransform(256, 256, zoom + AltosMap.default_zoom, new AltosLatLon(latitude, longitude));
+
+ AltosPointDouble center = t.point(new AltosLatLon(latitude, longitude));
+ AltosPointDouble edge = t.point(new AltosLatLon(latitude, longitude + delta_lon));
+
+ int tile_radius = (int) Math.ceil(Math.abs(center.x - edge.x) / AltosMap.px_size);
+
+ return tile_radius;
+ }
+
+ int tiles_per_layer(int zoom) {
+ int tile_radius = tile_radius(zoom);
+ return (tile_radius * 2 + 1) * (tile_radius * 2 + 1);
+ }
+
+ public void do_load() {
+ tiles_this_layer = tiles_per_layer(cur_z);
+ tiles_loaded_layer = 0;
+ listener.debug("tiles_this_layer %d (zoom %d)\n", tiles_this_layer, cur_z);
+
+ int load_radius = tile_radius(cur_z);
+ int zoom = cur_z + AltosMap.default_zoom;
+ int maptype = cur_type;
+ AltosLatLon load_centre = new AltosLatLon(latitude, longitude);
+ AltosMapTransform transform = new AltosMapTransform(256, 256, zoom, load_centre);
+
+ map.centre(load_centre);
+
+ AltosPointInt upper_left;
+ AltosPointInt lower_right;
+
+ AltosPointInt centre = AltosMap.floor(transform.point(load_centre));
+
+ upper_left = new AltosPointInt(centre.x - load_radius * AltosMap.px_size,
+ centre.y - load_radius * AltosMap.px_size);
+ lower_right = new AltosPointInt(centre.x + load_radius * AltosMap.px_size,
+ centre.y + load_radius * AltosMap.px_size);
+
+
+ for (int y = (int) upper_left.y; y <= lower_right.y; y += AltosMap.px_size) {
+ for (int x = (int) upper_left.x; x <= lower_right.x; x += AltosMap.px_size) {
+ listener.debug("Make tile at %d, %d\n", x, y);
+ AltosPointInt point = new AltosPointInt(x, y);
+ AltosLatLon ul = transform.lat_lon(point);
+ AltosLatLon center = transform.lat_lon(new AltosPointDouble(x + AltosMap.px_size/2, y + AltosMap.px_size/2));
+ AltosMapTile tile = map.map_interface.new_tile(this, ul, center, zoom, maptype, AltosMap.px_size);
+ tile.add_store_listener(this);
+ if (tile.store_status() != AltosMapTile.loading)
+ notify_tile(tile, tile.store_status());
+ }
+ }
+ }
+
+ public int next_type(int start) {
+ int next_type;
+ for (next_type = start;
+ next_type <= AltosMap.maptype_terrain && (all_types & (1 << next_type)) == 0;
+ next_type++)
+ ;
+ return next_type;
+ }
+
+ public void next_load() {
+ int next_type = next_type(cur_type + 1);
+
+ if (next_type > AltosMap.maptype_terrain) {
+ if (cur_z == max_z) {
+ return;
+ } else {
+ cur_z++;
+ }
+ next_type = next_type(0);
+ }
+ cur_type = next_type;
+ do_load();
+ }
+
+ private void start_load() {
+
+ cur_z = min_z;
+ int ntype = 0;
+
+ for (int t = AltosMap.maptype_hybrid; t <= AltosMap.maptype_terrain; t++)
+ if ((all_types & (1 << t)) != 0)
+ ntype++;
+ if (ntype == 0) {
+ all_types = (1 << AltosMap.maptype_hybrid);
+ ntype = 1;
+ }
+
+ cur_type = next_type(0);
+
+ for (int z = min_z; z <= max_z; z++)
+ tiles_total += tiles_per_layer(z);
+
+ layers_total = (max_z - min_z + 1) * ntype;
+ layers_loaded = 0;
+ tiles_loaded_total = 0;
+
+ listener.debug("total tiles %d\n", tiles_total);
+
+ listener.loader_start(tiles_total);
+ do_load();
+ }
+
+ public void load(double latitude, double longitude, int min_z, int max_z, double radius, int all_types) {
+ listener.debug("lat %f lon %f min_z %d max_z %d radius %f all_types %d\n",
+ latitude, longitude, min_z, max_z, radius, all_types);
+ this.latitude = latitude;
+ this.longitude = longitude;
+ this.min_z = min_z;
+ this.max_z = max_z;
+ this.radius = radius;
+ this.all_types = all_types;
+ start_load();
+ }
+
+ public synchronized void notify_store(AltosMapStore store, int status) {
+ boolean do_next = false;
+ if (status == AltosMapTile.loading)
+ return;
+
+ if (layers_loaded >= layers_total)
+ return;
+
+ ++tiles_loaded_total;
+ ++tiles_loaded_layer;
+ listener.debug("total %d layer %d\n", tiles_loaded_total, tiles_loaded_layer);
+
+ if (tiles_loaded_layer == tiles_this_layer) {
+ ++layers_loaded;
+ listener.debug("%d layers loaded\n", layers_loaded);
+ if (layers_loaded == layers_total) {
+ listener.loader_done(tiles_total);
+ return;
+ } else {
+ do_next = true;
+ }
+ }
+ listener.loader_notify(tiles_loaded_total,
+ tiles_total, store.file.toString());
+ if (do_next)
+ next_load();
+ }
+
+ public synchronized void notify_tile(AltosMapTile tile, int status) {
+ notify_store(tile.store, status);
+ }
+
+ public AltosMapCache cache() { return map.cache(); }
+
+ public AltosMapLoader(AltosMap map, AltosMapLoaderListener listener) {
+ this.map = map;
+ this.listener = listener;
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+public interface AltosMapLoaderListener {
+ public abstract void loader_start(int max);
+
+ public abstract void loader_notify(int cur, int max, String name);
+
+ public abstract void loader_done(int max);
+
+ public abstract void debug(String format, Object ... arguments);
+}
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+public abstract class AltosMapMark {
+
+ public AltosLatLon lat_lon;
+ public int state;
+
+ static public int stroke_width = 6;
+
+ public abstract void paint(AltosMapTransform t);
+
+ public AltosMapMark (double lat, double lon, int state) {
+ lat_lon = new AltosLatLon(lat, lon);
+ this.state = state;
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+public abstract class AltosMapPath {
+
+ public LinkedList<AltosMapPathPoint> points = new LinkedList<AltosMapPathPoint>();
+ public AltosMapPathPoint last_point = null;
+
+ static public int stroke_width = 6;
+
+ public abstract void paint(AltosMapTransform t);
+
+ public AltosMapRectangle add(double lat, double lon, int state) {
+ AltosMapPathPoint point = new AltosMapPathPoint(new AltosLatLon (lat, lon), state);
+ AltosMapRectangle rect = null;
+
+ if (!point.equals(last_point)) {
+ if (last_point != null)
+ rect = new AltosMapRectangle(last_point.lat_lon, point.lat_lon);
+ points.add (point);
+ last_point = point;
+ }
+ return rect;
+ }
+
+ public void clear () {
+ points = new LinkedList<AltosMapPathPoint>();
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+public class AltosMapPathPoint {
+ public AltosLatLon lat_lon;
+ public int state;
+
+ public int hashCode() {
+ return lat_lon.hashCode() ^ state;
+ }
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+
+ if (!(o instanceof AltosMapPathPoint))
+ return false;
+
+ AltosMapPathPoint other = (AltosMapPathPoint) o;
+
+ return lat_lon.equals(other.lat_lon) && state == other.state;
+ }
+
+ public AltosMapPathPoint(AltosLatLon lat_lon, int state) {
+ this.lat_lon = lat_lon;
+ this.state = state;
+ }
+}
+
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+public class AltosMapRectangle {
+ AltosLatLon ul, lr;
+
+ public AltosMapRectangle(AltosLatLon a, AltosLatLon b) {
+ double ul_lat, ul_lon;
+ double lr_lat, lr_lon;
+
+ if (a.lat > b.lat) {
+ ul_lat = a.lat;
+ lr_lat = b.lat;
+ } else {
+ ul_lat = b.lat;
+ lr_lat = a.lat;
+ }
+ if (a.lon < b.lon) {
+ ul_lon = a.lon;
+ lr_lon = b.lon;
+ } else {
+ ul_lon = b.lon;
+ lr_lon = a.lon;
+ }
+
+ ul = new AltosLatLon(ul_lat, ul_lon);
+ lr = new AltosLatLon(lr_lat, lr_lon);
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+public class AltosMapStore {
+ String url;
+ public File file;
+ LinkedList<AltosMapStoreListener> listeners = new LinkedList<AltosMapStoreListener>();
+
+ int status;
+
+ public int status() {
+ return status;
+ }
+
+ public synchronized void add_listener(AltosMapStoreListener listener) {
+ if (!listeners.contains(listener))
+ listeners.add(listener);
+ }
+
+ public synchronized void remove_listener(AltosMapStoreListener listener) {
+ listeners.remove(listener);
+ }
+
+ private synchronized void notify_listeners(int status) {
+ this.status = status;
+ for (AltosMapStoreListener listener : listeners)
+ listener.notify_store(this, status);
+ }
+
+ static Object forbidden_lock = new Object();
+ static long forbidden_time;
+ static boolean forbidden_set;
+
+ private int fetch_url() {
+ URL u;
+
+ try {
+ u = new URL(url);
+ } catch (java.net.MalformedURLException e) {
+ return AltosMapTile.bad_request;
+ }
+
+ byte[] data;
+ URLConnection uc = null;
+ try {
+ uc = u.openConnection();
+ String type = uc.getContentType();
+ int contentLength = uc.getContentLength();
+ if (uc instanceof HttpURLConnection) {
+ int response = ((HttpURLConnection) uc).getResponseCode();
+ switch (response) {
+ case HttpURLConnection.HTTP_FORBIDDEN:
+ case HttpURLConnection.HTTP_PAYMENT_REQUIRED:
+ case HttpURLConnection.HTTP_UNAUTHORIZED:
+ synchronized (forbidden_lock) {
+ forbidden_time = System.nanoTime();
+ forbidden_set = true;
+ return AltosMapTile.forbidden;
+ }
+ }
+ }
+ InputStream in = new BufferedInputStream(uc.getInputStream());
+ int bytesRead = 0;
+ int offset = 0;
+ data = new byte[contentLength];
+ while (offset < contentLength) {
+ bytesRead = in.read(data, offset, data.length - offset);
+ if (bytesRead == -1)
+ break;
+ offset += bytesRead;
+ }
+ in.close();
+
+ if (offset != contentLength)
+ return AltosMapTile.failed;
+
+ } catch (IOException e) {
+ return AltosMapTile.failed;
+ }
+
+ try {
+ FileOutputStream out = new FileOutputStream(file);
+ out.write(data);
+ out.flush();
+ out.close();
+ } catch (FileNotFoundException e) {
+ return AltosMapTile.bad_request;
+ } catch (IOException e) {
+ if (file.exists())
+ file.delete();
+ return AltosMapTile.bad_request;
+ }
+ return AltosMapTile.success;
+ }
+
+ static Object fetch_lock = new Object();
+
+ static final long forbidden_interval = 60l * 1000l * 1000l * 1000l;
+ static final long google_maps_ratelimit_ms = 1200;
+
+ static Object loader_lock = new Object();
+
+ static LinkedList<AltosMapStore> waiting = new LinkedList<AltosMapStore>();
+ static LinkedList<AltosMapStore> running = new LinkedList<AltosMapStore>();
+
+ static final int concurrent_loaders = 128;
+
+ static void start_loaders() {
+ while (!waiting.isEmpty() && running.size() < concurrent_loaders) {
+ AltosMapStore s = waiting.remove();
+ running.add(s);
+ Thread lt = s.make_loader_thread();
+ lt.start();
+ }
+ }
+
+ void finish_loader() {
+ synchronized(loader_lock) {
+ running.remove(this);
+ start_loaders();
+ }
+ }
+
+ void add_loader() {
+ synchronized(loader_lock) {
+ waiting.add(this);
+ start_loaders();
+ }
+ }
+
+ class loader implements Runnable {
+
+ public void run() {
+ try {
+ if (file.exists()) {
+ notify_listeners(AltosMapTile.success);
+ return;
+ }
+
+ synchronized(forbidden_lock) {
+ if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) {
+ notify_listeners(AltosMapTile.forbidden);
+ return;
+ }
+ }
+
+ int new_status;
+
+ if (!AltosVersion.has_google_maps_api_key()) {
+ synchronized (fetch_lock) {
+ long startTime = System.nanoTime();
+ new_status = fetch_url();
+ if (new_status == AltosMapTile.success) {
+ long duration_ms = (System.nanoTime() - startTime) / 1000000;
+ if (duration_ms < google_maps_ratelimit_ms) {
+ try {
+ Thread.sleep(google_maps_ratelimit_ms - duration_ms);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+ } else {
+ new_status = fetch_url();
+ }
+ notify_listeners(new_status);
+ } finally {
+ finish_loader();
+ }
+ }
+ }
+
+ private Thread make_loader_thread() {
+ return new Thread(new loader());
+ }
+
+ private void load() {
+ add_loader();
+ }
+
+ private AltosMapStore (String url, File file) {
+ this.url = url;
+ this.file = file;
+
+ if (file.exists())
+ status = AltosMapTile.success;
+ else {
+ status = AltosMapTile.loading;
+ load();
+ }
+ }
+
+ public int hashCode() {
+ return url.hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+
+ if (!(o instanceof AltosMapStore))
+ return false;
+
+ AltosMapStore other = (AltosMapStore) o;
+ return url.equals(other.url);
+ }
+
+ static HashMap<String,AltosMapStore> stores = new HashMap<String,AltosMapStore>();
+
+ public static AltosMapStore get(String url, File file) {
+ AltosMapStore store;
+ synchronized(stores) {
+ if (stores.containsKey(url)) {
+ store = stores.get(url);
+ } else {
+ store = new AltosMapStore(url, file);
+ stores.put(url, store);
+ }
+ }
+ return store;
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+public interface AltosMapStoreListener {
+ abstract void notify_store(AltosMapStore store, int status);
+}
--- /dev/null
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.util.*;
+
+public abstract class AltosMapTile implements AltosFontListener {
+ AltosMapTileListener listener;
+ public AltosLatLon upper_left, center;
+ public int px_size;
+ int zoom;
+ int maptype;
+ int scale;
+ public AltosMapStore store;
+ public AltosMapCache cache;
+ public int status;
+
+ static public final int success = 0;
+ static public final int loading = 1;
+ static public final int failed = 2;
+ static public final int bad_request = 3;
+ static public final int forbidden = 4;
+
+ private File map_file() {
+ double lat = center.lat;
+ double lon = center.lon;
+ char chlat = lat < 0 ? 'S' : 'N';
+ char chlon = lon < 0 ? 'W' : 'E';
+
+ if (lat < 0) lat = -lat;
+ if (lon < 0) lon = -lon;
+ String maptype_string = String.format("%s-", AltosMap.maptype_names[maptype]);
+ String format_string;
+ if (maptype == AltosMap.maptype_hybrid || maptype == AltosMap.maptype_satellite || maptype == AltosMap.maptype_terrain)
+ format_string = "jpg";
+ else
+ format_string = "png";
+ return new File(AltosPreferences.mapdir(),
+ String.format("map-%c%.6f,%c%.6f-%s%d%s.%s",
+ chlat, lat, chlon, lon, maptype_string, zoom, scale == 1 ? "" : String.format("-%d", scale), format_string));
+ }
+
+ private String map_url() {
+ String format_string;
+ int z = zoom;
+
+ if (maptype == AltosMap.maptype_hybrid || maptype == AltosMap.maptype_satellite || maptype == AltosMap.maptype_terrain)
+ format_string = "jpg";
+ else
+ format_string = "png32";
+
+ for (int s = 1; s < scale; s <<= 1)
+ z--;
+
+ if (AltosVersion.has_google_maps_api_key())
+ return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&scale=%d&sensor=false&maptype=%s&format=%s&key=%s",
+ center.lat, center.lon, z, px_size/scale, px_size/scale, scale, AltosMap.maptype_names[maptype], format_string, AltosVersion.google_maps_api_key);
+ else
+ return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&scale=%d&sensor=false&maptype=%s&format=%s",
+ center.lat, center.lon, z, px_size/scale, px_size/scale, AltosMap.maptype_names[maptype], format_string);
+ }
+
+ public void font_size_changed(int font_size) {
+ }
+
+ public void set_status(int status) {
+ this.status = status;
+ listener.notify_tile(this, status);
+ }
+
+ public void notify_image(AltosImage image) {
+ listener.notify_tile(this, status);
+ }
+
+ public int store_status() {
+ return store.status();
+ }
+
+ public void add_store_listener(AltosMapStoreListener listener) {
+ store.add_listener(listener);
+ }
+
+ public void remove_store_listener(AltosMapStoreListener listener) {
+ store.remove_listener(listener);
+ }
+
+ public abstract void paint(AltosMapTransform t);
+
+ public AltosMapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
+ this.listener = listener;
+ this.upper_left = upper_left;
+ this.cache = listener.cache();
+
+ while (center.lon < -180.0)
+ center.lon += 360.0;
+ while (center.lon > 180.0)
+ center.lon -= 360.0;
+
+ this.center = center;
+ this.zoom = zoom;
+ this.maptype = maptype;
+ this.px_size = px_size;
+ this.scale = scale;
+
+ status = AltosMapTile.loading;
+ store = AltosMapStore.get(map_url(), map_file());
+ }
+
+ public AltosMapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+ this(listener, upper_left, center, zoom, maptype, px_size, 1);
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+public interface AltosMapTileListener {
+ abstract public void notify_tile(AltosMapTile tile, int status);
+
+ abstract public AltosMapCache cache();
+}
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+public class AltosMapTransform {
+
+ double scale_x, scale_y;
+
+ double offset_x, offset_y;
+
+ public AltosLatLon lat_lon (AltosPointDouble point) {
+ double lat, lon;
+ double rads;
+
+ lon = point.x/scale_x;
+ rads = 2 * Math.atan(Math.exp(-point.y/scale_y));
+ lat = Math.toDegrees(rads - Math.PI/2);
+
+ return new AltosLatLon(lat,lon);
+ }
+
+ public AltosLatLon lat_lon (AltosPointInt point) {
+ return lat_lon(new AltosPointDouble(point.x, point.y));
+ }
+
+ public AltosPointDouble screen_point(AltosPointInt screen) {
+ return new AltosPointDouble(screen.x + offset_x, screen.y + offset_y);
+ }
+
+ public AltosPointDouble screen_point(AltosPointDouble screen) {
+ return new AltosPointDouble(screen.x + offset_x, screen.y + offset_y);
+ }
+
+ public double hypot(AltosLatLon a, AltosLatLon b) {
+ AltosPointDouble a_pt = point(a);
+ AltosPointDouble b_pt = point(b);
+
+ return Math.hypot(a_pt.x - b_pt.x, a_pt.y - b_pt.y);
+ }
+
+ public AltosLatLon screen_lat_lon(AltosPointInt screen) {
+ return lat_lon(screen_point(screen));
+ }
+
+ public AltosLatLon screen_lat_lon(AltosPointDouble screen) {
+ return lat_lon(screen_point(screen));
+ }
+
+ public AltosPointDouble point(AltosLatLon lat_lon) {
+ double x, y;
+ double e;
+
+ x = lat_lon.lon * scale_x;
+
+ e = Math.sin(Math.toRadians(lat_lon.lat));
+ e = Math.max(e,-(1-1.0E-15));
+ e = Math.min(e, 1-1.0E-15 );
+
+ y = 0.5*Math.log((1+e)/(1-e))*-scale_y;
+
+ return new AltosPointDouble(x, y);
+ }
+
+ public AltosPointDouble screen(AltosPointDouble point) {
+ return new AltosPointDouble(point.x - offset_x, point.y - offset_y);
+ }
+
+ public AltosPointInt screen(AltosPointInt point) {
+ return new AltosPointInt((int) (point.x - offset_x + 0.5),
+ (int) (point.y - offset_y + 0.5));
+ }
+
+ public AltosRectangle screen(AltosMapRectangle map_rect) {
+ AltosPointDouble ul = screen(map_rect.ul);
+ AltosPointDouble lr = screen(map_rect.lr);
+
+ return new AltosRectangle((int) ul.x, (int) ul.y, (int) (lr.x - ul.x), (int) (lr.y - ul.y));
+ }
+
+ public AltosPointDouble screen(AltosLatLon lat_lon) {
+ return screen(point(lat_lon));
+ }
+
+ private boolean has_location;
+
+ public boolean has_location() {
+ return has_location;
+ }
+
+ public AltosMapTransform(int width, int height, int zoom, AltosLatLon centre_lat_lon) {
+ scale_x = 256/360.0 * Math.pow(2, zoom);
+ scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom);
+
+ AltosPointDouble centre_pt = point(centre_lat_lon);
+
+ has_location = (centre_lat_lon.lat != 0 || centre_lat_lon.lon != 0);
+ offset_x = centre_pt.x - width / 2.0;
+ offset_y = centre_pt.y - height / 2.0;
+ }
+
+ public static double lon_from_distance(double lat, double distance) {
+ double c = AltosGreatCircle.earth_radius * Math.cos(lat * Math.PI / 180) * 2 * Math.PI;
+
+ if (c < 10)
+ return 0;
+ return distance/c * 360.0;
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+public interface AltosMapZoomListener {
+ abstract public void zoom_changed(int zoom);
+}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.concurrent.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.concurrent.*;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosNoSymbol extends Exception {
public AltosNoSymbol(String name) {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosOrient extends AltosUnits {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
+import java.util.*;
import java.text.*;
public class AltosParse {
}
}
- public static double parse_double(String v) throws ParseException {
+ static NumberFormat nf_locale = NumberFormat.getInstance();
+
+ static NumberFormat nf_net = NumberFormat.getInstance(Locale.ROOT);
+
+ public static double parse_double_locale(String str) throws ParseException {
try {
- return Double.parseDouble(v);
- } catch (NumberFormatException e) {
- throw new ParseException("error parsing double " + v, 0);
+ return nf_locale.parse(str.trim()).doubleValue();
+ } catch (ParseException pe) {
+ throw new ParseException("error parsing double " + str, 0);
+ }
+ }
+
+ public static double parse_double_net(String str) throws ParseException {
+ try {
+ return nf_net.parse(str.trim()).doubleValue();
+ } catch (ParseException pe) {
+ throw new ParseException("error parsing double " + str, 0);
}
}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+public class AltosPointDouble {
+ public double x, y;
+
+ public int hashCode() {
+ return new Double(x).hashCode() ^ new Double(y).hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+
+ if (!(o instanceof AltosPointDouble))
+ return false;
+
+ AltosPointDouble n = (AltosPointDouble) o;
+
+ return n.x == x && n.y == y;
+ }
+
+ public AltosPointDouble(double x, double y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public AltosPointDouble(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public AltosPointDouble(AltosPointInt p) {
+ this.x = p.x;
+ this.y = p.y;
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+public class AltosPointInt {
+ public int x, y;
+
+ public int hashCode() {
+ return x ^ y;
+ }
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+
+ if (!(o instanceof AltosPointInt))
+ return false;
+
+ AltosPointInt n = (AltosPointInt) o;
+
+ return n.x == x && n.y == y;
+ }
+
+ public AltosPointInt(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public AltosPointInt(double x, double y) {
+ this.x = (int) (x + 0.5);
+ this.y = (int) (y + 0.5);
+ }
+
+ public AltosPointInt(AltosPointDouble pt_d) {
+ this.x = (int) (pt_d.x + 0.5);
+ this.y = (int) (pt_d.y + 0.5);
+ }
+}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
+import java.text.*;
public class AltosPreferences {
public static AltosPreferencesBackend backend = null;
public final static String logfilePreferenceFormat = "LOGFILE-%d";
/* state preference name */
+ public final static String statePreferenceHead = "STATE-";
public final static String statePreferenceFormat = "STATE-%d";
+ public final static String statePreferenceLatest = "STATE-LATEST";
/* voice preference name */
public final static String voicePreference = "VOICE";
public final static String unitsPreference = "IMPERIAL-UNITS";
+ /* Maps cache size preference name */
+ final static String mapCachePreference = "MAP-CACHE";
+
+ static LinkedList<AltosMapCacheListener> map_cache_listeners;
+
+ public static int map_cache = 9;
+
public static AltosFrequency[] load_common_frequencies() {
AltosFrequency[] frequencies = null;
boolean existing = false;
common_frequencies = load_common_frequencies();
AltosConvert.imperial_units = backend.getBoolean(unitsPreference, false);
+
+ map_cache = backend.getInt(mapCachePreference, 9);
+ map_cache_listeners = new LinkedList<AltosMapCacheListener>();
}
public static void flush_preferences() {
synchronized(backend) {
backend.putBytes(String.format(statePreferenceFormat, serial), bytes);
+ backend.putInt(statePreferenceLatest, serial);
flush_preferences();
}
} catch (IOException ie) {
}
}
+ public static ArrayList<Integer> list_states() {
+ String[] keys = backend.keys();
+ ArrayList<Integer> states = new ArrayList<Integer>();
+
+ for (String key : keys) {
+ if (key.startsWith(statePreferenceHead)) {
+ try {
+ int serial = AltosParse.parse_int(key.substring(statePreferenceHead.length()));
+ states.add(serial);
+ } catch (ParseException pe) {
+ }
+ }
+ }
+ return states;
+ }
+
+ public static void remove_state(int serial) {
+ synchronized(backend) {
+ backend.remove(String.format(statePreferenceFormat, serial));
+ }
+ }
+
+ public static int latest_state() {
+ int latest = 0;
+ synchronized (backend) {
+ latest = backend.getInt(statePreferenceLatest, 0);
+ }
+ return latest;
+ }
+
public static AltosSavedState state(int serial) {
byte[] bytes = null;
units_listeners.remove(l);
}
}
+
+
+ public static void register_map_cache_listener(AltosMapCacheListener l) {
+ synchronized(backend) {
+ map_cache_listeners.add(l);
+ }
+ }
+
+ public static void unregister_map_cache_listener(AltosMapCacheListener l) {
+ synchronized (backend) {
+ map_cache_listeners.remove(l);
+ }
+ }
+
+ public static void set_map_cache(int new_map_cache) {
+ synchronized(backend) {
+ map_cache = new_map_cache;
+ backend.putInt(mapCachePreference, map_cache);
+ flush_preferences();
+ for (AltosMapCacheListener l: map_cache_listeners)
+ l.map_cache_changed(map_cache);
+ }
+ }
+
+ public static int map_cache() {
+ synchronized(backend) {
+ return map_cache;
+ }
+ }
}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.File;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.*;
import java.text.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosQuaternion {
double r; /* real bit */
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+public class AltosRectangle {
+ public int x, y, width, height;
+
+ public AltosRectangle(int x, int y, int w, int h) {
+ this.x = x;
+ this.y = y;
+ this.width = w;
+ this.height = h;
+ }
+}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
public void update(AltosState state) throws InterruptedException {
/* Make it run in realtime after the rocket leaves the pad */
if (state.state > AltosLib.ao_flight_pad && state.time_change > 0)
- Thread.sleep((int) (Math.min(state.time_change,10) * 100));
+ Thread.sleep((int) (Math.min(state.time_change,10) * 1000));
state.set_received_time(System.currentTimeMillis());
}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosRotation {
private AltosQuaternion rotation;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.concurrent.TimeoutException;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.concurrent.TimeoutException;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.concurrent.TimeoutException;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.concurrent.TimeoutException;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.concurrent.TimeoutException;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.concurrent.TimeoutException;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.util.concurrent.TimeoutException;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosSpeed extends AltosUnits {
* Track flight state from telemetry or eeprom data stream
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
public int set;
+ static final double filter_len = 2.0;
static final double ascent_filter_len = 0.5;
- static final double descent_filter_len = 0.5;
+ static final double descent_filter_len = 5.0;
/* derived data */
}
void set_filtered(double new_value, double time) {
- if (prev_value != AltosLib.MISSING)
- new_value = (prev_value * 15.0 + new_value) / 16.0;
+ if (prev_value != AltosLib.MISSING) {
+ double f = 1/Math.exp((time - prev_set_time) / filter_len);
+ new_value = f * new_value + (1-f) * prev_value;
+ }
set(new_value, time);
}
return AltosLib.state_name(state);
}
+ public void set_product(String product) {
+ this.product = product;
+ }
+
public void set_state(int state) {
if (state != AltosLib.ao_flight_invalid) {
this.state = state;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public interface AltosStateUpdate {
public void update_state(AltosState state) throws InterruptedException;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.text.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosTelemetryConfiguration extends AltosTelemetryStandard {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.io.*;
import java.util.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.text.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosTelemetryLocation extends AltosTelemetryStandard {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.text.*;
import java.util.HashMap;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosTelemetryMegaData extends AltosTelemetryStandard {
int state;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosTelemetryMegaSensor extends AltosTelemetryStandard {
int accel;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosTelemetryMetrumData extends AltosTelemetryStandard {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosTelemetryMetrumSensor extends AltosTelemetryStandard {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosTelemetryMini extends AltosTelemetryStandard {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosTelemetryRaw extends AltosTelemetryStandard {
public AltosTelemetryRaw(int[] bytes) {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
import java.text.*;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosTelemetrySatellite extends AltosTelemetryStandard {
int channels;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosTelemetrySensor extends AltosTelemetryStandard {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public abstract class AltosTelemetryStandard extends AltosTelemetry {
int[] bytes;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosTemperature extends AltosUnits {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
+
+import java.text.*;
public abstract class AltosUnits {
public abstract int show_fraction(int width, boolean imperial_units);
- public double parse(String s, boolean imperial_units) throws NumberFormatException {
- double v = Double.parseDouble(s);
+ public double parse_locale(String s, boolean imperial_units) throws ParseException {
+ double v = AltosParse.parse_double_locale(s);
+ return inverse(v, imperial_units);
+ }
+
+ public double parse_net(String s, boolean imperial_units) throws ParseException {
+ double v = AltosParse.parse_double_net(s);
return inverse(v, imperial_units);
}
- public double parse(String s) throws NumberFormatException {
- return parse(s, AltosConvert.imperial_units);
+ public double parse_locale(String s) throws ParseException {
+ return parse_locale(s, AltosConvert.imperial_units);
+ }
+
+ public double parse_net(String s) throws ParseException {
+ return parse_net(s, AltosConvert.imperial_units);
}
public double value(double v) {
public String say_units(double v) {
return say_units(v, AltosConvert.imperial_units);
}
-}
\ No newline at end of file
+}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public interface AltosUnitsListener {
public void units_changed(boolean imperial_units);
--- /dev/null
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+public class AltosVersion {
+ public final static String version = "@VERSION@";
+
+ public final static String google_maps_api_key = @GOOGLEKEY@;
+
+ public static boolean has_google_maps_api_key() {
+ return google_maps_api_key != null && google_maps_api_key.length() > 1;
+ }
+}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public class AltosVoltage extends AltosUnits {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_7;
public interface AltosWriter {
AltosPyro.java \
AltosWriter.java \
AltosQuaternion.java \
- AltosRotation.java
+ AltosRotation.java \
+ AltosImage.java \
+ AltosLatLon.java \
+ AltosMap.java \
+ AltosMapCache.java \
+ AltosMapCacheListener.java \
+ AltosMapInterface.java \
+ AltosMapLine.java \
+ AltosMapMark.java \
+ AltosMapPath.java \
+ AltosMapPathPoint.java \
+ AltosMapRectangle.java \
+ AltosMapStore.java \
+ AltosMapStoreListener.java \
+ AltosMapTile.java \
+ AltosMapTileListener.java \
+ AltosMapTransform.java \
+ AltosMapZoomListener.java \
+ AltosPointDouble.java \
+ AltosPointInt.java \
+ AltosRectangle.java \
+ AltosFlightDisplay.java \
+ AltosFontListener.java \
+ AltosLaunchSite.java \
+ AltosLaunchSiteListener.java \
+ AltosLaunchSites.java \
+ AltosMapLoaderListener.java \
+ AltosMapLoader.java \
+ AltosVersion.java
JAR=altoslib_$(ALTOSLIB_VERSION).jar
import java.awt.*;
import libaltosJNI.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class Altos extends AltosUILib {
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosAscent extends AltosUIFlightTab {
JLabel cur, max;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosCompanionInfo extends JTable implements AltosFlightDisplay {
private AltosFlightInfoTableModel model;
import java.io.*;
import java.util.concurrent.*;
import java.text.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosConfig implements ActionListener {
package altosui;
+import java.text.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosConfigPyroUI
extends AltosUIDialog
if (units != null) {
try {
- double v = units.parse(value.getText(), !imperial_units);
+ double v = units.parse_locale(value.getText(), !imperial_units);
set(enabled(), v);
- } catch (NumberFormatException ne) {
+ } catch (ParseException pe) {
set(enabled(), 0.0);
}
}
AltosUnits units = AltosPyro.pyro_to_units(flag);
try {
if (units != null)
- return units.parse(value.getText());
- return Double.parseDouble(value.getText());
- } catch (NumberFormatException e) {
+ return units.parse_locale(value.getText());
+ return AltosParse.parse_double_locale(value.getText());
+ } catch (ParseException e) {
throw new AltosConfigDataException("\"%s\": %s\n", value.getText(), e.getMessage());
}
}
String v = pyro_firing_time_value.getSelectedItem().toString();
try {
- return Double.parseDouble(v);
- } catch (NumberFormatException e) {
+ return AltosParse.parse_double_locale(v);
+ } catch (ParseException e) {
throw new AltosConfigDataException("Invalid pyro firing time \"%s\"", v);
}
}
import javax.swing.*;
import java.io.*;
import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosConfigTD implements ActionListener {
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosConfigTDUI
extends AltosUIDialog
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import java.text.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosConfigUI
extends AltosUIDialog
JLabel rate_label;
JLabel aprs_interval_label;
JLabel aprs_ssid_label;
+ JLabel aprs_format_label;
JLabel flight_log_max_label;
JLabel ignite_mode_label;
JLabel pad_orientation_label;
AltosUIRateList rate_value;
JComboBox<String> aprs_interval_value;
JComboBox<Integer> aprs_ssid_value;
+ JComboBox<String> aprs_format_value;
JComboBox<String> flight_log_max_value;
JComboBox<String> ignite_mode_value;
JComboBox<String> pad_orientation_value;
void set_aprs_ssid_tool_tip() {
if (aprs_ssid_value.isEnabled())
- aprs_interval_value.setToolTipText("Set the APRS SSID (secondary station identifier)");
- else if (aprs_interval_value.isEnabled())
- aprs_interval_value.setToolTipText("Software version doesn't support setting the APRS SSID");
+ aprs_ssid_value.setToolTipText("Set the APRS SSID (secondary station identifier)");
+ else if (aprs_ssid_value.isEnabled())
+ aprs_ssid_value.setToolTipText("Software version doesn't support setting the APRS SSID");
else
- aprs_interval_value.setToolTipText("Hardware doesn't support APRS");
+ aprs_ssid_value.setToolTipText("Hardware doesn't support APRS");
+ }
+
+ void set_aprs_format_tool_tip() {
+ if (aprs_format_value.isEnabled())
+ aprs_format_value.setToolTipText("Set the APRS format (compressed/uncompressed)");
+ else if (aprs_format_value.isEnabled())
+ aprs_format_value.setToolTipText("Software version doesn't support setting the APRS format");
+ else
+ aprs_format_value.setToolTipText("Hardware doesn't support APRS");
}
void set_flight_log_max_tool_tip() {
set_aprs_ssid_tool_tip();
row++;
+ /* APRS format */
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = row;
+ c.gridwidth = 4;
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ c.ipady = 5;
+ aprs_format_label = new JLabel("APRS format:");
+ pane.add(aprs_format_label, c);
+
+ c = new GridBagConstraints();
+ c.gridx = 4; c.gridy = row;
+ c.gridwidth = 4;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ c.ipady = 5;
+ aprs_format_value = new JComboBox<String>(AltosLib.ao_aprs_format_name);
+ aprs_format_value.setEditable(false);
+ aprs_format_value.addItemListener(this);
+ aprs_format_value.setMaximumRowCount(AltosLib.ao_aprs_format_name.length);
+ pane.add(aprs_format_value, c);
+ set_aprs_format_tool_tip();
+ row++;
+
/* Callsign */
c = new GridBagConstraints();
c.gridx = 0; c.gridy = row;
}
- public int main_deploy() {
- return (int) (AltosConvert.height.parse(main_deploy_value.getSelectedItem().toString()) + 0.5);
+ public int main_deploy() throws AltosConfigDataException {
+ String str = main_deploy_value.getSelectedItem().toString();
+ try {
+ return (int) (AltosConvert.height.parse_locale(str) + 0.5);
+ } catch (ParseException pe) {
+ throw new AltosConfigDataException("invalid main deploy height %s", str);
+ }
}
String get_main_deploy_label() {
String v = main_deploy_value.getSelectedItem().toString();
main_deploy_label.setText(get_main_deploy_label());
set_main_deploy_values();
- int m = (int) (AltosConvert.height.parse(v, !imperial_units) + 0.5);
- set_main_deploy(m);
+ try {
+ int m = (int) (AltosConvert.height.parse_locale(v, !imperial_units) + 0.5);
+ set_main_deploy(m);
+ } catch (ParseException pe) {
+ }
if (tracker_motion_value.isEnabled()) {
String motion = tracker_motion_value.getSelectedItem().toString();
tracker_motion_label.setText(get_tracker_motion_label());
set_tracker_motion_values();
- set_tracker_motion((int) (AltosConvert.height.parse(motion, !imperial_units) + 0.5));
+ try {
+ int m = (int) (AltosConvert.height.parse_locale(motion, !imperial_units) + 0.5);
+ set_tracker_motion(m);
+ } catch (ParseException pe) {
+ }
}
if (!was_dirty)
}
public int tracker_motion() throws AltosConfigDataException {
- return (int) AltosConvert.height.parse(tracker_motion_value.getSelectedItem().toString());
+ String str = tracker_motion_value.getSelectedItem().toString();
+ try {
+ return (int) (AltosConvert.height.parse_locale(str) + 0.5);
+ } catch (ParseException pe) {
+ throw new AltosConfigDataException("invalid tracker motion %s", str);
+ }
}
public void set_tracker_interval(int tracker_interval) {
Integer i = (Integer) aprs_ssid_value.getSelectedItem();
return i;
}
+
+ public void set_aprs_format(int new_aprs_format) {
+ aprs_format_value.setVisible(new_aprs_format >= 0);
+ aprs_format_label.setVisible(new_aprs_format >= 0);
+
+ aprs_format_value.setSelectedIndex(Math.max(0,new_aprs_format));
+ set_aprs_format_tool_tip();
+ }
+
+ public int aprs_format() throws AltosConfigDataException {
+ return aprs_format_value.getSelectedIndex();
+ }
}
import java.beans.*;
import javax.swing.*;
import javax.swing.event.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosConfigureUI
extends AltosUIConfigure
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosDescent extends AltosUIFlightTab {
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosFlightStatus extends JComponent implements AltosFlightDisplay {
GridBagLayout layout;
import java.text.*;
import java.util.prefs.*;
import java.util.concurrent.LinkedBlockingQueue;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosFlightStatusTableModel extends AbstractTableModel {
private String[] columnNames = {
package altosui;
import java.awt.event.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosFlightStatusUpdate implements ActionListener {
import javax.swing.*;
import java.util.*;
import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {
AltosVoice voice;
AltosDescent descent;
AltosLanded landed;
AltosCompanionInfo companion;
- AltosUIMap sitemap;
+ AltosUIMapNew sitemap;
boolean has_map;
boolean has_companion;
boolean has_state;
bag = getContentPane();
bag.setLayout(new GridBagLayout());
- GridBagConstraints c = new GridBagConstraints();
-
setTitle(String.format("AltOS %s", reader.name));
/* Stick channel selector at top of table for telemetry monitoring */
if (serial >= 0) {
+ set_inset(3);
+
// Frequency menu
frequencies = new AltosUIFreqList(AltosUIPreferences.frequency(serial));
frequencies.set_product("Monitor");
reader.save_frequency();
}
});
- c.gridx = 0;
- c.gridy = 0;
- c.weightx = 0;
- c.weighty = 0;
- c.insets = new Insets(3, 3, 3, 3);
- c.fill = GridBagConstraints.NONE;
- c.anchor = GridBagConstraints.WEST;
- bag.add (frequencies, c);
+ bag.add (frequencies, constraints(0, 1));
// Telemetry rate list
rates = new AltosUIRateList(AltosUIPreferences.telemetry_rate(serial));
}
});
rates.setEnabled(reader.supports_telemetry_rate(AltosLib.ao_telemetry_rate_2400));
- c.gridx = 1;
- c.gridy = 0;
- c.weightx = 0;
- c.weighty = 0;
- c.insets = new Insets(3, 3, 3, 3);
- c.fill = GridBagConstraints.NONE;
- c.anchor = GridBagConstraints.WEST;
- bag.add (rates, c);
+ bag.add (rates, constraints(1, 1));
// Telemetry format list
if (reader.supports_telemetry(Altos.ao_telemetry_standard)) {
reader.save_telemetry();
}
});
- c.gridx = 2;
- c.gridy = 0;
- c.weightx = 0;
- c.weighty = 0;
- c.fill = GridBagConstraints.NONE;
- c.anchor = GridBagConstraints.WEST;
- bag.add (telemetries, c);
- c.insets = new Insets(0, 0, 0, 0);
+ bag.add (telemetries, constraints(2, 1));
} else {
String version;
version = "Telemetry: None";
telemetry = new JLabel(version);
- c.gridx = 2;
- c.gridy = 0;
- c.weightx = 0;
- c.weighty = 0;
- c.fill = GridBagConstraints.NONE;
- c.anchor = GridBagConstraints.WEST;
- bag.add (telemetry, c);
- c.insets = new Insets(0, 0, 0, 0);
+ bag.add (telemetry, constraints(2, 1));
}
+ next_row();
}
+ set_inset(0);
/* Flight status is always visible */
flightStatus = new AltosFlightStatus();
displays.add(flightStatus);
- c.gridx = 0;
- c.gridy = 1;
- c.fill = GridBagConstraints.HORIZONTAL;
- c.weightx = 1;
- c.gridwidth = 3;
- bag.add(flightStatus, c);
+ bag.add(flightStatus, constraints(0, 4, GridBagConstraints.HORIZONTAL));
+ next_row();
/* The rest of the window uses a tabbed pane to
* show one of the alternate data views
has_companion = false;
has_state = false;
- sitemap = new AltosUIMap();
+ sitemap = new AltosUIMapNew();
displays.add(sitemap);
has_map = false;
/* Make the tabbed pane use the rest of the window space */
- c.gridx = 0;
- c.gridy = 2;
- c.fill = GridBagConstraints.BOTH;
- c.weightx = 1;
- c.weighty = 1;
- bag.add(pane, c);
+ bag.add(pane, constraints(0, 4, GridBagConstraints.BOTH));
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
JTabbedPane pane;
AltosGraph graph;
AltosUIEnable enable;
- AltosUIMap map;
+ AltosUIMapNew map;
AltosState state;
AltosGraphDataSet graphDataSet;
AltosFlightStats stats;
for (AltosState state : states) {
if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) {
if (map == null)
- map = new AltosUIMap();
+ map = new AltosUIMapNew();
map.show(state, null);
has_gps = true;
}
import java.io.*;
import java.util.concurrent.*;
import java.util.Arrays;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDisplay, AltosIdleMonitorListener, DocumentListener {
AltosDevice device;
AltosFlightStatus flightStatus;
AltosIgnitor ignitor;
AltosIdleMonitor thread;
+ AltosUIMapNew sitemap;
int serial;
boolean remote;
boolean has_ignitor;
+ boolean has_map;
void stop_display() {
if (thread != null) {
has_ignitor = false;
}
}
+ if (state.gps != null && state.gps.connected) {
+ if (!has_map) {
+ pane.add("Site Map", sitemap);
+ has_map = true;
+ }
+ } else {
+ if (has_map) {
+ pane.remove(sitemap);
+ has_map = false;
+ }
+ }
+
// try {
pad.show(state, listener_state);
flightStatus.show(state, listener_state);
flightInfo.show(state, listener_state);
- ignitor.show(state, listener_state);
+ if (has_ignitor)
+ ignitor.show(state, listener_state);
+ if (has_map)
+ sitemap.show(state, listener_state);
// } catch (Exception e) {
// System.out.print("Show exception " + e);
// }
public void changedUpdate(DocumentEvent e) {
if (callsign_value != null) {
String callsign = callsign_value.getText();
+ System.out.printf("callsign set to %s\n", callsign);
thread.set_callsign(callsign);
AltosUIPreferences.set_callsign(callsign);
}
changedUpdate(e);
}
- int row = 0;
-
- public GridBagConstraints constraints (int x, int width, int fill) {
- GridBagConstraints c = new GridBagConstraints();
- Insets insets = new Insets(4, 4, 4, 4);
-
- c.insets = insets;
- c.fill = fill;
- if (width == 3)
- c.anchor = GridBagConstraints.CENTER;
- else if (x == 2)
- c.anchor = GridBagConstraints.EAST;
- else
- c.anchor = GridBagConstraints.WEST;
- c.gridx = x;
- c.gridwidth = width;
- c.gridy = row;
- return c;
- }
-
- public GridBagConstraints constraints(int x, int width) {
- return constraints(x, width, GridBagConstraints.NONE);
- }
-
void idle_exception(JFrame owner, Exception e) {
if (e instanceof FileNotFoundException) {
JOptionPane.showMessageDialog(owner,
AltosSerial link;
try {
link = new AltosSerial(device);
- link.set_frame(this);
} catch (Exception ex) {
idle_exception(in_owner, ex);
return;
}
+ link.set_frame(this);
+
+ /* We let the user set the freq/callsign, so don't bother with the cancel dialog */
+ link.set_cancel_enable(false);
bag = getContentPane();
bag.setLayout(new GridBagLayout());
/* Stick frequency selector at top of table for telemetry monitoring */
if (remote && serial >= 0) {
+ set_inset(3);
+
// Frequency menu
frequencies = new AltosUIFreqList(AltosUIPreferences.frequency(serial));
frequencies.addActionListener(new ActionListener() {
callsign_value = new JTextField(AltosUIPreferences.callsign());
callsign_value.getDocument().addDocumentListener(this);
callsign_value.setToolTipText("Callsign sent in packet mode");
- bag.add(callsign_value, constraints(2, 1, GridBagConstraints.BOTH));
- row++;
+ bag.add(callsign_value, constraints(2, 1, GridBagConstraints.HORIZONTAL));
+ next_row();
}
+ set_inset(0);
/* Flight status is always visible */
flightStatus = new AltosFlightStatus();
- bag.add(flightStatus, constraints(0, 3, GridBagConstraints.HORIZONTAL));
- row++;
+ bag.add(flightStatus, constraints(0, 4, GridBagConstraints.HORIZONTAL));
+
+ next_row();
/* The rest of the window uses a tabbed pane to
* show one of the alternate data views
ignitor = new AltosIgnitor();
+ sitemap = new AltosUIMapNew();
+
/* Make the tabbed pane use the rest of the window space */
- bag.add(pane, constraints(0, 3, GridBagConstraints.BOTH));
+ bag.add(pane, constraints(0, 4, GridBagConstraints.BOTH));
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosIgniteUI
extends AltosUIDialog
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosIgnitor extends AltosUIFlightTab {
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosLanded extends AltosUIFlightTab implements ActionListener {
import java.io.*;
import java.util.concurrent.*;
import java.awt.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosLaunch {
AltosDevice device;
import java.io.*;
import java.text.*;
import java.util.concurrent.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_7.*;
class FireButton extends JButton {
protected void processMouseEvent(MouseEvent e) {
package altosui;
import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosPad extends AltosUIFlightTab {
public double voltage(AltosState state) { return AltosLib.MISSING; }
- public boolean hide(double v) { return v == AltosLib.MISSING; }
public double good() { return AltosLib.ao_battery_good; }
+ public boolean hide(AltosState state, AltosListenerState listener_state, int i) {
+ return value(state, listener_state, i) == AltosLib.MISSING;
+ }
+
public double value(AltosState state, AltosListenerState listener_state, int i) {
if (listener_state == null)
return AltosLib.MISSING;
public AltosPad() {
int y = 0;
add(new Battery(this, y++));
+ add(new ReceiverBattery(this, y++));
add(new Apogee(this, y++));
add(new Main(this, y++));
add(new LoggingReady(this, y++));
add(new GPSLocked(this, y++));
add(new GPSReady(this, y++));
- add(new ReceiverBattery(this, y++));
add(new PadLat(this, y++));
add(new PadLon(this, y++));
add(new PadAlt(this, y++));
import javax.swing.*;
import java.io.*;
import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class AltosUI extends AltosUIFrame {
public AltosVoice voice = new AltosVoice();
}
void LoadMaps() {
- new AltosUIMapPreload(AltosUI.this);
+ new AltosUIMapPreloadNew(AltosUI.this);
}
void LaunchController() {
for (int i = 0; i < args.length; i++) {
if (args[i].equals("--help"))
help(0);
- else if (args[i].equals("--fetchmaps")) {
- if (args.length < i + 3) {
- help(1);
- } else {
- double lat = Double.parseDouble(args[i+1]);
- double lon = Double.parseDouble(args[i+2]);
-// AltosSiteMap.prefetchMaps(lat, lon);
- i += 2;
- }
- } else if (args[i].equals("--replay"))
+ else if (args[i].equals("--replay"))
process = process_replay;
else if (args[i].equals("--kml"))
process = process_kml;
File "freetts.jar"
File "jfreechart.jar"
File "jcommon.jar"
+ File "../icon/${WIN_APP_EXE}"
File "*.dll"
File "../icon/${WIN_APP_ICON}"
- CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}"
+ CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}"
SectionEnd
Section "${REG_NAME} Desktop Shortcut"
- CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}"
+ CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}"
SectionEnd
Section "Firmware"
SetOutPath $INSTDIR
- File "../icon/${WIN_APP_EXE}"
File "../icon/${WIN_TELEM_EXE}"
File "../icon/${WIN_EEPROM_EXE}"
DeleteRegKey HKCR ".telem\${PROG_ID_EEPROM}"
DeleteRegValue HKCR ".telem\OpenWithProgids" "${PROG_ID_EEPROM}"
- SearchPath $1 "javaw.exe"
-
; .eeprom elements
WriteRegStr HKCR "${PROG_ID_EEPROM}" "" "Altus Metrum Log File"
WriteRegStr HKCR "${PROG_ID_EEPROM}" "FriendlyTypeName" "Altus Metrum Log File"
WriteRegStr HKCR "${PROG_ID_EEPROM}\CurVer" "" "${PROG_ID_EEPROM}"
WriteRegStr HKCR "${PROG_ID_EEPROM}\DefaultIcon" "" '"$INSTDIR\${WIN_EEPROM_EXE}",-101'
- WriteRegExpandStr HKCR "${PROG_ID_EEPROM}\shell\open\command" "" '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"'
+ WriteRegExpandStr HKCR "${PROG_ID_EEPROM}\shell\open\command" "" '"$INSTDIR\${WIN_APP_EXE}" "%1"'
WriteRegStr HKCR ".eeprom" "" "${PROG_ID_EEPROM}"
WriteRegStr HKCR ".eeprom" "PerceivedType" "Altus Metrum Log File"
WriteRegStr HKCR "${PROG_ID_TELEM}" "FriendlyTypeName" "Altus Metrum Telemetry File"
WriteRegStr HKCR "${PROG_ID_TELEM}\CurVer" "" "${PROG_ID_TELEM}"
WriteRegStr HKCR "${PROG_ID_TELEM}\DefaultIcon" "" '"$INSTDIR\${WIN_TELEM_EXE}",-101'
- WriteRegExpandStr HKCR "${PROG_ID_TELEM}\shell\open\command" "" '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"'
+ WriteRegExpandStr HKCR "${PROG_ID_TELEM}\shell\open\command" "" '"$INSTDIR\${WIN_APP_EXE}" "%1"'
WriteRegStr HKCR ".telem" "" "${PROG_ID_TELEM}"
WriteRegStr HKCR ".telem" "PerceivedType" "Altus Metrum Telemetry File"
-AltosUIVersion.java
bin
classaltosuilib.stamp
altosuilib*.jar
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import libaltosJNI.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosBTDevice extends altos_bt_device implements AltosDevice {
return false;
}
+ public int hashCode() {
+ return getName().hashCode() ^ getAddr().hashCode();
+ }
+
public boolean equals(Object o) {
+ if (o == null)
+ return false;
+
if (!(o instanceof AltosBTDevice))
return false;
AltosBTDevice other = (AltosBTDevice) o;
return getName().equals(other.getName()) && getAddr().equals(other.getAddr());
}
- public int hashCode() {
- return getName().hashCode() ^ getAddr().hashCode();
- }
-
public AltosBTDevice(String name, String addr) {
AltosUILib.load_library();
libaltos.altos_bt_fill_in(name, addr,this);
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.util.*;
import libaltosJNI.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosBTDeviceIterator implements Iterator<AltosBTDevice> {
AltosBTDevice current;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.util.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosBTKnown implements Iterable<AltosBTDevice> {
LinkedList<AltosBTDevice> devices = new LinkedList<AltosBTDevice>();
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import java.awt.event.*;
import javax.swing.plaf.basic.*;
import java.util.*;
import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosBTManage extends AltosUIDialog implements ActionListener, Iterable<AltosBTDevice> {
LinkedBlockingQueue<AltosBTDevice> found_devices;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosCSVUI
extends AltosUIDialog
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
+import java.text.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
class AltosEditFreqUI extends AltosUIDialog implements ActionListener {
Frame frame;
String d_s = description.getText();
try {
- double f_d = Double.parseDouble(f_s);
+ double f_d = AltosParse.parse_double_locale(f_s);
return new AltosFrequency(f_d, d_s);
- } catch (NumberFormatException ne) {
+ } catch (ParseException ne) {
}
return null;
}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.io.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosDataChooser extends JFileChooser {
JFrame frame;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import libaltosJNI.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import javax.swing.*;
import java.awt.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import javax.swing.*;
import java.awt.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import java.text.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosDisplayThread extends Thread {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosEepromDelete implements Runnable {
AltosEepromList flights;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosEepromManage implements ActionListener {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import java.awt.event.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosEepromMonitorUI extends AltosUIDialog implements AltosEepromMonitor {
JFrame owner;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
class AltosEepromItem implements ActionListener {
AltosEepromLog log;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import java.awt.event.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.io.*;
import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosFlashUI
extends AltosUIDialog
+++ /dev/null
-/*
- * Copyright © 2010 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import org.altusmetrum.altoslib_6.*;
-
-public interface AltosFlightDisplay extends AltosUnitsListener, AltosFontListener {
- void reset();
-
- void show(AltosState state, AltosListenerState listener_state);
-
- String getName();
-}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import javax.swing.table.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import javax.swing.*;
import java.util.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosFlightStatsTable extends JComponent implements AltosFontListener {
GridBagLayout layout;
+++ /dev/null
-/*
- * Copyright © 2011 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-public interface AltosFontListener {
- void font_size_changed(int font_size);
-}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosGraphDataPoint implements AltosUIDataPoint {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.lang.*;
import java.io.*;
import java.util.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
class AltosGraphIterator implements Iterator<AltosUIDataPoint> {
AltosGraphDataSet dataSet;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosInfoTable extends JTable implements AltosFlightDisplay, HierarchyListener {
private AltosFlightInfoTableModel model;
if (state != null) {
if (state.device_type != AltosLib.MISSING)
info_add_row(0, "Device", "%s", AltosLib.product_name(state.device_type));
+ else if (state.product != null)
+ info_add_row(0, "Device", "%s", state.product);
if (state.altitude() != AltosLib.MISSING)
info_add_row(0, "Altitude", "%6.0f m", state.altitude());
if (state.ground_altitude() != AltosLib.MISSING)
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import javax.swing.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import javax.swing.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
public interface AltosPositionListener {
public void position_changed(int position);
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosRomconfigUI
extends AltosUIDialog
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.text.*;
import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
class AltosScanResult {
String callsign;
rate = in_rate;
}
- public boolean equals(AltosScanResult other) {
+ public int hashCode() {
+ return serial ^ frequency.hashCode() ^ telemetry ^ rate;
+ }
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+ if (!(o instanceof AltosScanResult))
+ return false;
+ AltosScanResult other = (AltosScanResult) o;
return (serial == other.serial &&
- frequency.frequency == other.frequency.frequency &&
+ frequency.equals(other.frequency) &&
telemetry == other.telemetry &&
rate == other.rate);
}
* Deal with TeleDongle on a serial port
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.io.*;
import java.util.*;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
import libaltosJNI.*;
/*
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
public class AltosSerialInUseException extends Exception {
public AltosDevice device;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.event.*;
+import org.altusmetrum.altoslib_7.*;
class DelegatingRenderer implements ListCellRenderer<Object> {
row++;
pane.add(new JLabel (String.format("AltOS version %s (%smaps key)",
- AltosUIVersion.version,
- AltosUIVersion.has_google_maps_api_key() ? "" : "no ")),
+ AltosVersion.version,
+ AltosVersion.has_google_maps_api_key() ? "" : "no ")),
constraints(0, 3));
row++;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
public class AltosUIDataMissing extends Exception {
public int id;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
public interface AltosUIDataPoint {
public abstract double x() throws AltosUIDataMissing;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
public interface AltosUIDataSet {
public abstract String name();
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import java.awt.event.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.concurrent.*;
import java.util.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public abstract class AltosUIFlightTab extends JComponent implements AltosFlightDisplay, HierarchyListener {
public GridBagLayout layout;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import java.awt.event.*;
}
}
}
+
+ int row = 0;
+
+ public void next_row() {
+ row++;
+ }
+
+ int inset = 0;
+
+ public void set_inset(int i) {
+ inset = i;
+ }
+
+ public GridBagConstraints constraints (int x, int width, int fill, int anchor, double weightx, double weighty) {
+ return new GridBagConstraints(x, /* x */
+ row, /* y */
+ width, /* width */
+ 1, /* height */
+ weightx, /* weightx */
+ weighty, /* weighty */
+ anchor, /* anchor */
+ fill, /* fill */
+ new Insets(inset,inset,inset,inset), /* insets */
+ 0, /* ipadx */
+ 0); /* ipady */
+ }
+
+ public GridBagConstraints constraints (int x, int width, int fill, int anchor) {
+ double weightx = 0;
+ double weighty = 0;
+
+ if (fill == GridBagConstraints.NONE) {
+ weightx = 0;
+ weighty = 0;
+ } else if (fill == GridBagConstraints.HORIZONTAL) {
+ weightx = 1;
+ weighty = 0;
+ } else if (fill == GridBagConstraints.VERTICAL) {
+ weightx = 0;
+ weighty = 1;
+ } else if (fill == GridBagConstraints.BOTH) {
+ weightx = 1;
+ weighty = 1;
+ }
+
+ return constraints (x, width, fill, anchor, weightx, weighty);
+ }
+
+ public GridBagConstraints constraints (int x, int width, int fill) {
+ return constraints (x, width, fill, GridBagConstraints.WEST);
+ }
+
+ public GridBagConstraints constraints(int x, int width) {
+ return constraints(x, width, GridBagConstraints.NONE);
+ }
+
void init() {
AltosUIPreferences.register_ui_listener(this);
AltosUIPreferences.register_position_listener(this);
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosUIFreqList extends JComboBox<AltosFrequency> {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_7;
+
+import javax.swing.*;
+import javax.imageio.ImageIO;
+import java.awt.image.*;
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+
+public class AltosUIImage implements AltosImage {
+ public Image image;
+
+ /* Discard storage for image */
+ public void flush() {
+ image.flush();
+ }
+
+ public AltosUIImage(Image image) {
+ this.image = image;
+ }
+}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public abstract class AltosUIIndicator implements AltosFontListener, AltosUnitsListener {
JLabel label;
+++ /dev/null
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.Math;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUILatLon {
- public double lat;
- public double lon;
-
- public boolean equals(AltosUILatLon other) {
- if (other == null)
- return false;
- return lat == other.lat && lon == other.lon;
- }
-
- public AltosUILatLon(double lat, double lon) {
- this.lat = lat;
- this.lon = lon;
- }
-}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import libaltosJNI.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosUILib extends AltosLib {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
public interface AltosUIListener {
public void ui_changed(String look_and_feel);
+++ /dev/null
-/*
- * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.Math;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUIMap extends JComponent implements AltosFlightDisplay, AltosUIMapZoomListener {
-
- static final int px_size = 512;
-
- static final int maptype_hybrid = 0;
- static final int maptype_roadmap = 1;
- static final int maptype_satellite = 2;
- static final int maptype_terrain = 3;
- static final int maptype_default = maptype_hybrid;
-
- static final String[] maptype_names = {
- "hybrid",
- "roadmap",
- "satellite",
- "terrain"
- };
-
- public static final String[] maptype_labels = {
- "Hybrid",
- "Roadmap",
- "Satellite",
- "Terrain"
- };
-
- public static final Color stateColors[] = {
- Color.WHITE, // startup
- Color.WHITE, // idle
- Color.WHITE, // pad
- Color.RED, // boost
- Color.PINK, // fast
- Color.YELLOW, // coast
- Color.CYAN, // drogue
- Color.BLUE, // main
- Color.BLACK, // landed
- Color.BLACK, // invalid
- Color.CYAN, // stateless
- };
-
- public void reset() {
- // nothing
- }
-
- public void font_size_changed(int font_size) {
- view.set_font();
- }
-
- public void units_changed(boolean imperial_units) {
- view.set_units();
- }
-
- JLabel zoom_label;
-
- private void set_zoom_label() {
- zoom_label.setText(String.format("Zoom %d", view.zoom() - view.default_zoom));
- }
-
- public void zoom_changed(int zoom) {
- set_zoom_label();
- }
-
- public void set_zoom(int zoom) {
- view.set_zoom(zoom);
- }
-
- public int get_zoom() {
- return view.zoom();
- }
-
- public void set_maptype(int type) {
- view.set_maptype(type);
- maptype_combo.setSelectedIndex(type);
- }
-
- public void show(AltosState state, AltosListenerState listener_state) {
- view.show(state, listener_state);
- }
-
- public void centre(double lat, double lon) {
- view.centre(lat, lon);
- }
-
- public void centre(AltosState state) {
- if (!state.gps.locked && state.gps.nsat < 4)
- return;
- centre(state.gps.lat, state.gps.lon);
- }
-
- public void add_mark(double lat, double lon, int state) {
- view.add_mark(lat, lon, state);
- }
-
- public void clear_marks() {
- view.clear_marks();
- }
-
- AltosUIMapView view;
-
- private GridBagLayout layout = new GridBagLayout();
-
- JComboBox<String> maptype_combo;
-
- public void set_load_params(double lat, double lon, int radius, AltosUIMapTileListener listener) {
- view.set_load_params(lat, lon, radius, listener);
- }
-
- public boolean all_fetched() {
- return view.all_fetched();
- }
-
- public static void prefetch_maps(double lat, double lon) {
- }
-
- public String getName() {
- return "Map";
- }
-
- public AltosUIMap() {
-
- view = new AltosUIMapView();
-
- view.setPreferredSize(new Dimension(500,500));
- view.setVisible(true);
- view.setEnabled(true);
- view.add_zoom_listener(this);
-
- GridBagLayout my_layout = new GridBagLayout();
-
- setLayout(my_layout);
-
- GridBagConstraints c = new GridBagConstraints();
- c.anchor = GridBagConstraints.CENTER;
- c.fill = GridBagConstraints.BOTH;
- c.gridx = 0;
- c.gridy = 0;
- c.gridwidth = 1;
- c.gridheight = 10;
- c.weightx = 1;
- c.weighty = 1;
- add(view, c);
-
- int y = 0;
-
- zoom_label = new JLabel("", JLabel.CENTER);
- set_zoom_label();
-
- c = new GridBagConstraints();
- c.anchor = GridBagConstraints.CENTER;
- c.fill = GridBagConstraints.HORIZONTAL;
- c.gridx = 1;
- c.gridy = y++;
- c.weightx = 0;
- c.weighty = 0;
- add(zoom_label, c);
-
- JButton zoom_reset = new JButton("0");
- zoom_reset.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- set_zoom(view.default_zoom);
- }
- });
-
- c = new GridBagConstraints();
- c.anchor = GridBagConstraints.CENTER;
- c.fill = GridBagConstraints.HORIZONTAL;
- c.gridx = 1;
- c.gridy = y++;
- c.weightx = 0;
- c.weighty = 0;
- add(zoom_reset, c);
-
- JButton zoom_in = new JButton("+");
- zoom_in.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- set_zoom(get_zoom() + 1);
- }
- });
-
- c = new GridBagConstraints();
- c.anchor = GridBagConstraints.CENTER;
- c.fill = GridBagConstraints.HORIZONTAL;
- c.gridx = 1;
- c.gridy = y++;
- c.weightx = 0;
- c.weighty = 0;
- add(zoom_in, c);
-
- JButton zoom_out = new JButton("-");
- zoom_out.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- set_zoom(get_zoom() - 1);
- }
- });
- c = new GridBagConstraints();
- c.anchor = GridBagConstraints.CENTER;
- c.fill = GridBagConstraints.HORIZONTAL;
- c.gridx = 1;
- c.gridy = y++;
- c.weightx = 0;
- c.weighty = 0;
- add(zoom_out, c);
-
- maptype_combo = new JComboBox<String>(maptype_labels);
-
- maptype_combo.setEditable(false);
- maptype_combo.setMaximumRowCount(maptype_combo.getItemCount());
- maptype_combo.addItemListener(new ItemListener() {
- public void itemStateChanged(ItemEvent e) {
- view.set_maptype(maptype_combo.getSelectedIndex());
- }
- });
-
- c = new GridBagConstraints();
- c.anchor = GridBagConstraints.CENTER;
- c.fill = GridBagConstraints.HORIZONTAL;
- c.gridx = 1;
- c.gridy = y++;
- c.weightx = 0;
- c.weighty = 0;
- add(maptype_combo, c);
- }
-}
+++ /dev/null
-/*
- * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import javax.swing.*;
-import javax.imageio.ImageIO;
-import java.awt.image.*;
-import java.awt.*;
-import java.io.*;
-import java.net.*;
-
-public class AltosUIMapCache implements AltosUIMapCacheListener {
- static final int success = 0;
- static final int loading = 1;
- static final int failed = 2;
- static final int bad_request = 3;
- static final int forbidden = 4;
-
- int min_cache_size; /* configured minimum cache size */
- int cache_size; /* current cache size */
- int requested_cache_size; /* cache size computed by application */
-
- private Object fetch_lock = new Object();
- private Object cache_lock = new Object();
-
- AltosUIMapImage[] images = new AltosUIMapImage[cache_size];
-
- long used;
-
- public void set_cache_size(int new_size) {
-
- requested_cache_size = new_size;
-
- if (new_size < min_cache_size)
- new_size = min_cache_size;
-
- if (new_size == cache_size)
- return;
-
- synchronized(cache_lock) {
- AltosUIMapImage[] new_images = new AltosUIMapImage[new_size];
-
- for (int i = 0; i < cache_size; i++) {
- if (i < new_size)
- new_images[i] = images[i];
- else if (images[i] != null)
- images[i].flush();
- }
- images = new_images;
- cache_size = new_size;
- }
- }
-
- public Image get(AltosUIMapTile tile, AltosUIMapStore store, int width, int height) {
- int oldest = -1;
- long age = used;
-
- synchronized(cache_lock) {
- AltosUIMapImage image = null;
- for (int i = 0; i < cache_size; i++) {
- image = images[i];
-
- if (image == null) {
- oldest = i;
- break;
- }
- if (store.equals(image.store)) {
- image.used = used++;
- return image.image;
- }
- if (image.used < age) {
- oldest = i;
- age = image.used;
- }
- }
-
- try {
- image = new AltosUIMapImage(tile, store);
- image.used = used++;
- if (images[oldest] != null)
- images[oldest].flush();
-
- images[oldest] = image;
-
- if (image.image == null)
- tile.set_status(loading);
- else
- tile.set_status(success);
-
- return image.image;
- } catch (IOException e) {
- tile.set_status(failed);
- return null;
- }
- }
- }
-
- public void map_cache_changed(int map_cache) {
- min_cache_size = map_cache;
-
- set_cache_size(requested_cache_size);
- }
-
- public void dispose() {
- AltosUIPreferences.unregister_map_cache_listener(this);
-
- for (int i = 0; i < cache_size; i++) {
- AltosUIMapImage image = images[i];
-
- if (image != null)
- image.flush();
- }
- }
-
- public AltosUIMapCache() {
- min_cache_size = AltosUIPreferences.map_cache();
-
- set_cache_size(0);
-
- AltosUIPreferences.register_map_cache_listener(this);
- }
-}
+++ /dev/null
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-public interface AltosUIMapCacheListener {
- public void map_cache_changed(int map_cache);
-}
+++ /dev/null
-/*
- * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import javax.swing.*;
-import javax.imageio.ImageIO;
-import java.awt.image.*;
-import java.awt.*;
-import java.io.*;
-import java.net.*;
-
-public class AltosUIMapImage implements AltosUIMapStoreListener {
- static final long google_maps_ratelimit_ms = 1200;
- // Google limits static map queries to 50 per minute per IP, so
- // each query should take at least 1.2 seconds.
-
- static final int success = 0;
- static final int loading = 1;
- static final int failed = 2;
- static final int bad_request = 3;
- static final int forbidden = 4;
-
- static long forbidden_time;
- static boolean forbidden_set = false;
- static final long forbidden_interval = 60l * 1000l * 1000l * 1000l;
-
- AltosUIMapTile tile; /* Notify when image has been loaded */
- Image image;
- AltosUIMapStore store;
- long used;
-
- class loader implements Runnable {
- public void run() {
- if (image != null)
- tile.notify_image(image);
- try {
- image = ImageIO.read(store.file);
- } catch (Exception ex) {
- }
- if (image == null)
- tile.set_status(failed);
- else
- tile.set_status(success);
- tile.notify_image(image);
- }
- }
-
- private void load() {
- loader l = new loader();
- Thread lt = new Thread(l);
- lt.start();
- }
-
- public void flush() {
- if (image != null) {
- image.flush();
- image = null;
- }
- }
-
- public boolean has_map() {
- return store.status() == AltosUIMapStore.success;
- }
-
- public synchronized void notify_store(AltosUIMapStore store, int status) {
- switch (status) {
- case AltosUIMapStore.loading:
- break;
- case AltosUIMapStore.success:
- load();
- break;
- default:
- tile.set_status(status);
- tile.notify_image(null);
- }
- }
-
- public AltosUIMapImage(AltosUIMapTile tile, AltosUIMapStore store) throws IOException {
- this.tile = tile;
- this.image = null;
- this.store = store;
- this.used = 0;
-
- int status = store.status();
- switch (status) {
- case AltosUIMapStore.loading:
- store.add_listener(this);
- break;
- case AltosUIMapStore.success:
- load();
- break;
- default:
- tile.set_status(status);
- tile.notify_image(null);
- break;
- }
- }
-}
+++ /dev/null
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.Math;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUIMapLine {
- AltosUILatLon start, end;
-
- private Font font = null;
- static public int stroke_width = 6;
-
- public void set_font(Font font) {
- this.font = font;
- }
-
- private AltosUILatLon lat_lon(MouseEvent e, AltosUIMapTransform t) {
- return t.screen_lat_lon(e.getPoint());
- }
-
- public void dragged(MouseEvent e, AltosUIMapTransform t) {
- end = lat_lon(e, t);
- }
-
- public void pressed(MouseEvent e, AltosUIMapTransform t) {
- start = lat_lon(e, t);
- end = null;
- }
-
- private String line_dist() {
- String format;
- AltosGreatCircle g = new AltosGreatCircle(start.lat, start.lon,
- end.lat, end.lon);
- double distance = g.distance;
-
- if (AltosConvert.imperial_units) {
- distance = AltosConvert.meters_to_feet(distance);
- if (distance < 10000) {
- format = "%4.0fft";
- } else {
- distance /= 5280;
- if (distance < 10)
- format = "%5.3fmi";
- else if (distance < 100)
- format = "%5.2fmi";
- else if (distance < 1000)
- format = "%5.1fmi";
- else
- format = "%5.0fmi";
- }
- } else {
- if (distance < 10000) {
- format = "%4.0fm";
- } else {
- distance /= 1000;
- if (distance < 100)
- format = "%5.2fkm";
- else if (distance < 1000)
- format = "%5.1fkm";
- else
- format = "%5.0fkm";
- }
- }
- return String.format(format, distance);
- }
-
- public void paint(Graphics2D g, AltosUIMapTransform t) {
-
- if (start == null || end == null)
- return;
-
- g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
-
- Line2D.Double line = new Line2D.Double(t.screen(start),
- t.screen(end));
-
- g.setColor(Color.WHITE);
- g.setStroke(new BasicStroke(stroke_width+4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
- g.draw(line);
-
- g.setColor(Color.BLUE);
- g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
- g.draw(line);
-
- String message = line_dist();
- Rectangle2D bounds;
- bounds = font.getStringBounds(message, g.getFontRenderContext());
-
- float x = (float) line.x1;
- float y = (float) line.y1 + (float) bounds.getHeight() / 2.0f;
-
- if (line.x1 < line.x2) {
- x -= (float) bounds.getWidth() + 2.0f;
- } else {
- x += 2.0f;
- }
-
- g.setFont(font);
- g.setColor(Color.WHITE);
- for (int dy = -2; dy <= 2; dy += 2)
- for (int dx = -2; dx <= 2; dx += 2)
- g.drawString(message, x + dx, y + dy);
- g.setColor(Color.BLUE);
- g.drawString(message, x, y);
- }
-}
+++ /dev/null
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.Math;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUIMapMark {
-
- AltosUILatLon lat_lon;
- int state;
-
- static public int stroke_width = 6;
-
- public void paint(Graphics2D g, AltosUIMapTransform t) {
-
- Point2D.Double pt = t.screen(lat_lon);
-
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
- RenderingHints.VALUE_ANTIALIAS_ON);
- g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
-
- if (0 <= state && state < AltosUIMap.stateColors.length)
- g.setColor(AltosUIMap.stateColors[state]);
- else
- g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
-
- g.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10);
- g.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40);
- g.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70);
- }
-
- public AltosUIMapMark (double lat, double lon, int state) {
- lat_lon = new AltosUILatLon(lat, lon);
- this.state = state;
- }
-}
--- /dev/null
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altosuilib_7;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import javax.swing.*;
+import java.io.*;
+import java.lang.Math;
+import java.awt.geom.*;
+import java.util.*;
+import java.util.concurrent.*;
+import javax.imageio.*;
+import org.altusmetrum.altoslib_7.*;
+
+public class AltosUIMapNew extends JComponent implements AltosFlightDisplay, AltosMapInterface {
+
+ AltosMap map;
+ Graphics2D g;
+ Font tile_font;
+ Font line_font;
+
+ static Point2D.Double point2d(AltosPointDouble pt) {
+ return new Point2D.Double(pt.x, pt.y);
+ }
+
+ static final AltosPointDouble point_double(Point pt) {
+ return new AltosPointDouble(pt.x, pt.y);
+ }
+
+ class MapMark extends AltosMapMark {
+ public void paint(AltosMapTransform t) {
+ AltosPointDouble pt = t.screen(lat_lon);
+
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+ if (0 <= state && state < AltosUIMapNew.stateColors.length)
+ g.setColor(AltosUIMapNew.stateColors[state]);
+ else
+ g.setColor(AltosUIMapNew.stateColors[AltosLib.ao_flight_invalid]);
+
+ g.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10);
+ g.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40);
+ g.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70);
+ }
+
+ MapMark(double lat, double lon, int state) {
+ super(lat, lon, state);
+ }
+ }
+
+ class MapView extends JComponent implements MouseMotionListener, MouseListener, ComponentListener, MouseWheelListener {
+
+ private VolatileImage create_back_buffer() {
+ return getGraphicsConfiguration().createCompatibleVolatileImage(getWidth(), getHeight());
+ }
+
+ private void do_paint(Graphics my_g) {
+ g = (Graphics2D) my_g;
+
+ map.paint();
+ }
+
+ public void paint(Graphics my_g) {
+ VolatileImage back_buffer = create_back_buffer();
+
+ Graphics2D top_g = (Graphics2D) my_g;
+
+ do {
+ GraphicsConfiguration gc = getGraphicsConfiguration();
+ int code = back_buffer.validate(gc);
+ if (code == VolatileImage.IMAGE_INCOMPATIBLE)
+ back_buffer = create_back_buffer();
+
+ Graphics g_back = back_buffer.getGraphics();
+ g_back.setClip(top_g.getClip());
+ do_paint(g_back);
+ g_back.dispose();
+
+ top_g.drawImage(back_buffer, 0, 0, this);
+ } while (back_buffer.contentsLost());
+ back_buffer.flush();
+ }
+
+ public void repaint(AltosRectangle damage) {
+ repaint(damage.x, damage.y, damage.width, damage.height);
+ }
+
+ private boolean is_drag_event(MouseEvent e) {
+ return e.getModifiers() == InputEvent.BUTTON1_MASK;
+ }
+
+ /* MouseMotionListener methods */
+
+ public void mouseDragged(MouseEvent e) {
+ map.touch_continue(e.getPoint().x, e.getPoint().y, is_drag_event(e));
+ }
+
+ public void mouseMoved(MouseEvent e) {
+ }
+
+ /* MouseListener methods */
+ public void mouseClicked(MouseEvent e) {
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ public void mouseExited(MouseEvent e) {
+ }
+
+ public void mousePressed(MouseEvent e) {
+ map.touch_start(e.getPoint().x, e.getPoint().y, is_drag_event(e));
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ }
+
+ /* MouseWheelListener methods */
+
+ public void mouseWheelMoved(MouseWheelEvent e) {
+ int zoom_change = e.getWheelRotation();
+
+ map.set_zoom_centre(map.get_zoom() - zoom_change, new AltosPointInt(e.getPoint().x, e.getPoint().y));
+ }
+
+ /* ComponentListener methods */
+
+ public void componentHidden(ComponentEvent e) {
+ }
+
+ public void componentMoved(ComponentEvent e) {
+ }
+
+ public void componentResized(ComponentEvent e) {
+ map.set_transform();
+ }
+
+ public void componentShown(ComponentEvent e) {
+ map.set_transform();
+ }
+
+ MapView() {
+ addComponentListener(this);
+ addMouseMotionListener(this);
+ addMouseListener(this);
+ addMouseWheelListener(this);
+ }
+ }
+
+ class MapLine extends AltosMapLine {
+
+ public void paint(AltosMapTransform t) {
+
+ if (start == null || end == null)
+ return;
+
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+
+ Line2D.Double line = new Line2D.Double(point2d(t.screen(start)),
+ point2d(t.screen(end)));
+
+ g.setColor(Color.WHITE);
+ g.setStroke(new BasicStroke(stroke_width+4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+ g.draw(line);
+
+ g.setColor(Color.BLUE);
+ g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+ g.draw(line);
+
+ String message = line_dist();
+ Rectangle2D bounds;
+ bounds = line_font.getStringBounds(message, g.getFontRenderContext());
+
+ float x = (float) line.x1;
+ float y = (float) line.y1 + (float) bounds.getHeight() / 2.0f;
+
+ if (line.x1 < line.x2) {
+ x -= (float) bounds.getWidth() + 2.0f;
+ } else {
+ x += 2.0f;
+ }
+
+ g.setFont(line_font);
+ g.setColor(Color.WHITE);
+ for (int dy = -2; dy <= 2; dy += 2)
+ for (int dx = -2; dx <= 2; dx += 2)
+ g.drawString(message, x + dx, y + dy);
+ g.setColor(Color.BLUE);
+ g.drawString(message, x, y);
+ }
+
+ public MapLine() {
+ }
+ }
+
+ class MapPath extends AltosMapPath {
+ public void paint(AltosMapTransform t) {
+ Point2D.Double prev = null;
+
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+ for (AltosMapPathPoint point : points) {
+ Point2D.Double cur = point2d(t.screen(point.lat_lon));
+ if (prev != null) {
+ Line2D.Double line = new Line2D.Double (prev, cur);
+ Rectangle bounds = line.getBounds();
+
+ if (g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height)) {
+ if (0 <= point.state && point.state < AltosUIMapNew.stateColors.length)
+ g.setColor(AltosUIMapNew.stateColors[point.state]);
+ else
+ g.setColor(AltosUIMapNew.stateColors[AltosLib.ao_flight_invalid]);
+
+ g.draw(line);
+ }
+ }
+ prev = cur;
+ }
+ }
+ }
+
+ class MapTile extends AltosMapTile {
+ public MapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+ super(listener, upper_left, center, zoom, maptype, px_size);
+ }
+
+ public void paint(AltosMapTransform t) {
+
+ AltosPointDouble point_double = t.screen(upper_left);
+ Point point = new Point((int) (point_double.x + 0.5),
+ (int) (point_double.y + 0.5));
+
+ if (!g.hitClip(point.x, point.y, px_size, px_size))
+ return;
+
+ AltosImage altos_image = cache.get(this, store, px_size, px_size);
+
+ AltosUIImage ui_image = (AltosUIImage) altos_image;
+
+ Image image = null;
+
+ if (ui_image != null)
+ image = ui_image.image;
+
+ if (image != null) {
+ g.drawImage(image, point.x, point.y, null);
+ } else {
+ g.setColor(Color.GRAY);
+ g.fillRect(point.x, point.y, px_size, px_size);
+
+ if (t.has_location()) {
+ String message = null;
+ switch (status) {
+ case AltosMapTile.loading:
+ message = "Loading...";
+ break;
+ case AltosMapTile.bad_request:
+ message = "Internal error";
+ break;
+ case AltosMapTile.failed:
+ message = "Network error, check connection";
+ break;
+ case AltosMapTile.forbidden:
+ message = "Too many requests, try later";
+ break;
+ }
+ if (message != null && tile_font != null) {
+ g.setFont(tile_font);
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+ Rectangle2D bounds = tile_font.getStringBounds(message, g.getFontRenderContext());
+
+ float x = px_size / 2.0f;
+ float y = px_size / 2.0f;
+ x = x - (float) bounds.getWidth() / 2.0f;
+ y = y + (float) bounds.getHeight() / 2.0f;
+ g.setColor(Color.BLACK);
+ g.drawString(message, (float) point_double.x + x, (float) point_double.y + y);
+ }
+ }
+ }
+ }
+ }
+
+ public static final Color stateColors[] = {
+ Color.WHITE, // startup
+ Color.WHITE, // idle
+ Color.WHITE, // pad
+ Color.RED, // boost
+ Color.PINK, // fast
+ Color.YELLOW, // coast
+ Color.CYAN, // drogue
+ Color.BLUE, // main
+ Color.BLACK, // landed
+ Color.BLACK, // invalid
+ Color.CYAN, // stateless
+ };
+
+ /* AltosMapInterface functions */
+
+ public AltosMapPath new_path() {
+ return new MapPath();
+ }
+
+ public AltosMapLine new_line() {
+ return new MapLine();
+ }
+
+ public AltosImage load_image(File file) throws Exception {
+ return new AltosUIImage(ImageIO.read(file));
+ }
+
+ public AltosMapMark new_mark(double lat, double lon, int state) {
+ return new MapMark(lat, lon, state);
+ }
+
+ public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+ return new MapTile(listener, upper_left, center, zoom, maptype, px_size);
+ }
+
+ public int width() {
+ return view.getWidth();
+ }
+
+ public int height() {
+ return view.getHeight();
+ }
+
+ public void repaint() {
+ view.repaint();
+ }
+
+ public void repaint(AltosRectangle damage) {
+ view.repaint(damage);
+ }
+
+ public void set_zoom_label(String label) {
+ zoom_label.setText(label);
+ }
+
+ public void select_object(AltosLatLon latlon) {
+ debug("select at %f,%f\n", latlon.lat, latlon.lon);
+ }
+
+ public void debug(String format, Object ... arguments) {
+ System.out.printf(format, arguments);
+ }
+
+
+ /* AltosFlightDisplay interface */
+
+ public void set_font() {
+ tile_font = AltosUILib.value_font;
+ line_font = AltosUILib.status_font;
+ }
+
+ public void font_size_changed(int font_size) {
+ set_font();
+ repaint();
+ }
+
+ public void units_changed(boolean imperial_units) {
+ repaint();
+ }
+
+ JLabel zoom_label;
+
+ public void set_maptype(int type) {
+ map.set_maptype(type);
+ maptype_combo.setSelectedIndex(type);
+ }
+
+ /* AltosUIMapPreload functions */
+
+ public void set_zoom(int zoom) {
+ map.set_zoom(zoom);
+ }
+
+ public void add_mark(double lat, double lon, int status) {
+ map.add_mark(lat, lon, status);
+ }
+
+ public void clear_marks() {
+ map.clear_marks();
+ }
+
+ /* AltosFlightDisplay interface */
+ public void reset() {
+ // nothing
+ }
+
+ public void show(AltosState state, AltosListenerState listener_state) {
+ map.show(state, listener_state);
+ }
+
+ public String getName() {
+ return "Map";
+ }
+
+ /* AltosGraphUI interface */
+ public void centre(AltosState state) {
+ map.centre(state);
+ }
+
+ /* internal layout bits */
+ private GridBagLayout layout = new GridBagLayout();
+
+ JComboBox<String> maptype_combo;
+
+ MapView view;
+
+ public AltosUIMapNew() {
+
+ set_font();
+
+ view = new MapView();
+
+ view.setPreferredSize(new Dimension(500,500));
+ view.setVisible(true);
+ view.setEnabled(true);
+
+ GridBagLayout my_layout = new GridBagLayout();
+
+ setLayout(my_layout);
+
+ GridBagConstraints c = new GridBagConstraints();
+ c.anchor = GridBagConstraints.CENTER;
+ c.fill = GridBagConstraints.BOTH;
+ c.gridx = 0;
+ c.gridy = 0;
+ c.gridwidth = 1;
+ c.gridheight = 10;
+ c.weightx = 1;
+ c.weighty = 1;
+ add(view, c);
+
+ int y = 0;
+
+ zoom_label = new JLabel("", JLabel.CENTER);
+
+ c = new GridBagConstraints();
+ c.anchor = GridBagConstraints.CENTER;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ c.gridy = y++;
+ c.weightx = 0;
+ c.weighty = 0;
+ add(zoom_label, c);
+
+ JButton zoom_reset = new JButton("0");
+ zoom_reset.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ map.set_zoom(map.default_zoom);
+ }
+ });
+
+ c = new GridBagConstraints();
+ c.anchor = GridBagConstraints.CENTER;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ c.gridy = y++;
+ c.weightx = 0;
+ c.weighty = 0;
+ add(zoom_reset, c);
+
+ JButton zoom_in = new JButton("+");
+ zoom_in.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ map.set_zoom(map.get_zoom() + 1);
+ }
+ });
+
+ c = new GridBagConstraints();
+ c.anchor = GridBagConstraints.CENTER;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ c.gridy = y++;
+ c.weightx = 0;
+ c.weighty = 0;
+ add(zoom_in, c);
+
+ JButton zoom_out = new JButton("-");
+ zoom_out.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ map.set_zoom(map.get_zoom() - 1);
+ }
+ });
+ c = new GridBagConstraints();
+ c.anchor = GridBagConstraints.CENTER;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ c.gridy = y++;
+ c.weightx = 0;
+ c.weighty = 0;
+ add(zoom_out, c);
+
+ maptype_combo = new JComboBox<String>(map.maptype_labels);
+
+ maptype_combo.setEditable(false);
+ maptype_combo.setMaximumRowCount(maptype_combo.getItemCount());
+ maptype_combo.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ map.set_maptype(maptype_combo.getSelectedIndex());
+ }
+ });
+
+ c = new GridBagConstraints();
+ c.anchor = GridBagConstraints.CENTER;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ c.gridy = y++;
+ c.weightx = 0;
+ c.weighty = 0;
+ add(maptype_combo, c);
+
+ map = new AltosMap(this);
+ }
+}
+++ /dev/null
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.Math;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-class PathPoint {
- AltosUILatLon lat_lon;
- int state;
-
- public PathPoint(AltosUILatLon lat_lon, int state) {
- this.lat_lon = lat_lon;
- this.state = state;
- }
-
- public boolean equals(PathPoint other) {
- if (other == null)
- return false;
-
- return lat_lon.equals(other.lat_lon) && state == other.state;
- }
-}
-
-public class AltosUIMapPath {
-
- LinkedList<PathPoint> points = new LinkedList<PathPoint>();
- PathPoint last_point = null;
-
- static public int stroke_width = 6;
-
- public void paint(Graphics2D g, AltosUIMapTransform t) {
- Point2D.Double prev = null;
-
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
- RenderingHints.VALUE_ANTIALIAS_ON);
- g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
-
- for (PathPoint point : points) {
- Point2D.Double cur = t.screen(point.lat_lon);
- if (prev != null) {
- Line2D.Double line = new Line2D.Double (prev, cur);
- Rectangle bounds = line.getBounds();
-
- if (g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height)) {
- if (0 <= point.state && point.state < AltosUIMap.stateColors.length)
- g.setColor(AltosUIMap.stateColors[point.state]);
- else
- g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
-
- g.draw(line);
- }
- }
- prev = cur;
- }
- }
-
- public AltosUIMapRectangle add(double lat, double lon, int state) {
- PathPoint point = new PathPoint(new AltosUILatLon (lat, lon), state);
- AltosUIMapRectangle rect = null;
-
- if (!point.equals(last_point)) {
- if (last_point != null)
- rect = new AltosUIMapRectangle(last_point.lat_lon, point.lat_lon);
- points.add (point);
- last_point = point;
- }
- return rect;
- }
-
- public void clear () {
- points = new LinkedList<PathPoint>();
- }
-}
+++ /dev/null
-/*
- * Copyright © 2011 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.util.*;
-import java.text.*;
-import java.lang.Math;
-import java.net.URL;
-import java.net.URLConnection;
-import org.altusmetrum.altoslib_6.*;
-
-class AltosUIMapPos extends Box {
- AltosUIFrame owner;
- JLabel label;
- JComboBox hemi;
- JTextField deg;
- JLabel deg_label;
- JTextField min;
- JLabel min_label;
-
- public void set_value(double new_value) {
- double d, m;
- int h;
-
- h = 0;
- if (new_value < 0) {
- h = 1;
- new_value = -new_value;
- }
- d = Math.floor(new_value);
- deg.setText(String.format("%3.0f", d));
- m = (new_value - d) * 60.0;
- min.setText(String.format("%7.4f", m));
- hemi.setSelectedIndex(h);
- }
-
- public double get_value() throws NumberFormatException {
- int h = hemi.getSelectedIndex();
- String d_t = deg.getText();
- String m_t = min.getText();
- double d, m, v;
- try {
- d = Double.parseDouble(d_t);
- } catch (NumberFormatException ne) {
- JOptionPane.showMessageDialog(owner,
- String.format("Invalid degrees \"%s\"",
- d_t),
- "Invalid number",
- JOptionPane.ERROR_MESSAGE);
- throw ne;
- }
- try {
- if (m_t.equals(""))
- m = 0;
- else
- m = Double.parseDouble(m_t);
- } catch (NumberFormatException ne) {
- JOptionPane.showMessageDialog(owner,
- String.format("Invalid minutes \"%s\"",
- m_t),
- "Invalid number",
- JOptionPane.ERROR_MESSAGE);
- throw ne;
- }
- v = d + m/60.0;
- if (h == 1)
- v = -v;
- return v;
- }
-
- public AltosUIMapPos(AltosUIFrame in_owner,
- String label_value,
- String[] hemi_names,
- double default_value) {
- super(BoxLayout.X_AXIS);
- owner = in_owner;
- label = new JLabel(label_value);
- hemi = new JComboBox<String>(hemi_names);
- hemi.setEditable(false);
- deg = new JTextField(5);
- deg.setMinimumSize(deg.getPreferredSize());
- deg.setHorizontalAlignment(JTextField.RIGHT);
- deg_label = new JLabel("°");
- min = new JTextField(9);
- min.setMinimumSize(min.getPreferredSize());
- min_label = new JLabel("'");
- set_value(default_value);
- add(label);
- add(Box.createRigidArea(new Dimension(5, 0)));
- add(hemi);
- add(Box.createRigidArea(new Dimension(5, 0)));
- add(deg);
- add(Box.createRigidArea(new Dimension(5, 0)));
- add(deg_label);
- add(Box.createRigidArea(new Dimension(5, 0)));
- add(min);
- add(Box.createRigidArea(new Dimension(5, 0)));
- add(min_label);
- }
-}
-
-class AltosUISite {
- String name;
- double latitude;
- double longitude;
-
- public String toString() {
- return name;
- }
-
- public AltosUISite(String in_name, double in_latitude, double in_longitude) {
- name = in_name;
- latitude = in_latitude;
- longitude = in_longitude;
- }
-
- public AltosUISite(String line) throws ParseException {
- String[] elements = line.split(":");
-
- if (elements.length < 3)
- throw new ParseException(String.format("Invalid site line %s", line), 0);
-
- name = elements[0];
-
- try {
- latitude = Double.parseDouble(elements[1]);
- longitude = Double.parseDouble(elements[2]);
- } catch (NumberFormatException ne) {
- throw new ParseException(String.format("Invalid site line %s", line), 0);
- }
- }
-}
-
-class AltosUISites extends Thread {
- AltosUIMapPreload preload;
- URL url;
- LinkedList<AltosUISite> sites;
-
- void notify_complete() {
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- preload.set_sites();
- }
- });
- }
-
- void add(AltosUISite site) {
- sites.add(site);
- }
-
- void add(String line) {
- try {
- add(new AltosUISite(line));
- } catch (ParseException pe) {
- }
- }
-
- public void run() {
- try {
- URLConnection uc = url.openConnection();
- //int length = uc.getContentLength();
-
- InputStreamReader in_stream = new InputStreamReader(uc.getInputStream(), AltosLib.unicode_set);
- BufferedReader in = new BufferedReader(in_stream);
-
- for (;;) {
- String line = in.readLine();
- if (line == null)
- break;
- add(line);
- }
- } catch (IOException e) {
- } finally {
- notify_complete();
- }
- }
-
- public AltosUISites(AltosUIMapPreload in_preload) {
- sites = new LinkedList<AltosUISite>();
- preload = in_preload;
- try {
- url = new URL(AltosLib.launch_sites_url);
- } catch (java.net.MalformedURLException e) {
- notify_complete();
- }
- start();
- }
-}
-
-public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, ItemListener, AltosUIMapTileListener {
- AltosUIFrame owner;
- AltosUIMap map;
- AltosUIMapCache cache = new AltosUIMapCache();
-
- AltosUIMapPos lat;
- AltosUIMapPos lon;
-
- JProgressBar pbar;
- int pbar_max;
- int pbar_cur;
-
- AltosUISites sites;
- JLabel site_list_label;
- JComboBox<AltosUISite> site_list;
-
- JToggleButton load_button;
- boolean loading;
- JButton close_button;
-
- JCheckBox[] maptypes = new JCheckBox[AltosUIMap.maptype_terrain - AltosUIMap.maptype_hybrid + 1];
-
- JComboBox<Integer> min_zoom;
- JComboBox<Integer> max_zoom;
- JComboBox<Integer> radius;
-
- Integer[] zooms = { -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6 };
- Integer[] radii = { 1, 2, 3, 4, 5 };
-
- static final String[] lat_hemi_names = { "N", "S" };
- static final String[] lon_hemi_names = { "E", "W" };
-
- class updatePbar implements Runnable {
- String s;
-
- public updatePbar(String in_s) {
- s = in_s;
- }
-
- public void run() {
- int n = ++pbar_cur;
-
- pbar.setMaximum(pbar_max);
- pbar.setValue(n);
- pbar.setString(s);
- }
- }
-
- double latitude, longitude;
- int min_z;
- int max_z;
- int cur_z;
- int all_types;
- int cur_type;
- int r;
-
- int tiles_per_layer;
- int tiles_loaded;
- int layers_total;
- int layers_loaded;
-
-
- private void do_load() {
- tiles_loaded = 0;
- map.set_zoom(cur_z + AltosUIMapView.default_zoom);
- map.set_maptype(cur_type);
- map.set_load_params(latitude, longitude, r, this);
- }
-
- private int next_type(int start) {
- int next_type;
- for (next_type = start;
- next_type <= AltosUIMap.maptype_terrain && (all_types & (1 << next_type)) == 0;
- next_type++)
- ;
- return next_type;
- }
-
- private void next_load() {
- int next_type = next_type(cur_type + 1);
-
- if (next_type > AltosUIMap.maptype_terrain) {
- if (cur_z == max_z) {
- return;
- } else {
- cur_z++;
- }
- next_type = next_type(0);
- }
- cur_type = next_type;
- do_load();
- }
-
- private void start_load() {
- cur_z = min_z;
- int ntype = 0;
- all_types = 0;
- for (int t = AltosUIMap.maptype_hybrid; t <= AltosUIMap.maptype_terrain; t++)
- if (maptypes[t].isSelected()) {
- all_types |= (1 << t);
- ntype++;
- }
- if (ntype == 0) {
- all_types |= (1 << AltosUIMap.maptype_hybrid);
- ntype = 1;
- }
-
- cur_type = next_type(0);
- tiles_per_layer = (r * 2 + 1) * (r * 2 + 1);
- layers_total = (max_z - min_z + 1) * ntype;
- layers_loaded = 0;
- pbar_max = layers_total * tiles_per_layer;
- pbar_cur = 0;
-
- map.clear_marks();
- map.add_mark(latitude,longitude, AltosLib.ao_flight_boost);
- do_load();
- }
-
- /* AltosUIMapTileListener methods */
-
- public synchronized void notify_tile(AltosUIMapTile tile, int status) {
- if (status == AltosUIMapStore.loading)
- return;
-
- SwingUtilities.invokeLater(new updatePbar(tile.store.file.toString()));
- ++tiles_loaded;
- if (tiles_loaded == tiles_per_layer) {
- ++layers_loaded;
- if (layers_loaded == layers_total) {
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- pbar.setValue(0);
- pbar.setString("");
- load_button.setSelected(false);
- loading = false;
- }
- });
- } else {
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- next_load();
- }
- });
- }
- }
- }
-
- public AltosUIMapCache cache() { return cache; }
-
- public void set_sites() {
- int i = 1;
- for (AltosUISite site : sites.sites) {
- site_list.insertItemAt(site, i);
- i++;
- }
- }
-
- public void itemStateChanged(ItemEvent e) {
- int state = e.getStateChange();
-
- if (state == ItemEvent.SELECTED) {
- Object o = e.getItem();
- if (o instanceof AltosUISite) {
- AltosUISite site = (AltosUISite) o;
- lat.set_value(site.latitude);
- lon.set_value(site.longitude);
- }
- }
- }
-
- public void actionPerformed(ActionEvent e) {
- String cmd = e.getActionCommand();
-
- if (cmd.equals("close"))
- setVisible(false);
-
- if (cmd.equals("load")) {
- if (!loading) {
- try {
- latitude = lat.get_value();
- longitude = lon.get_value();
- min_z = (Integer) min_zoom.getSelectedItem();
- max_z = (Integer) max_zoom.getSelectedItem();
- if (max_z < min_z)
- max_z = min_z;
- r = (Integer) radius.getSelectedItem();
- loading = true;
- } catch (NumberFormatException ne) {
- load_button.setSelected(false);
- }
- start_load();
- }
- }
- }
-
- public AltosUIMapPreload(AltosUIFrame in_owner) {
- owner = in_owner;
-
- Container pane = getContentPane();
- GridBagConstraints c = new GridBagConstraints();
- Insets i = new Insets(4,4,4,4);
-
- setTitle("AltOS Load Maps");
-
- pane.setLayout(new GridBagLayout());
-
- map = new AltosUIMap();
-
- c.fill = GridBagConstraints.BOTH;
- c.anchor = GridBagConstraints.CENTER;
- c.insets = i;
- c.weightx = 1;
- c.weighty = 1;
-
- c.gridx = 0;
- c.gridy = 0;
- c.gridwidth = 10;
- c.anchor = GridBagConstraints.CENTER;
-
- pane.add(map, c);
-
- pbar = new JProgressBar();
- pbar.setMinimum(0);
- pbar.setMaximum(1);
- pbar.setValue(0);
- pbar.setString("");
- pbar.setStringPainted(true);
-
- c.fill = GridBagConstraints.HORIZONTAL;
- c.anchor = GridBagConstraints.CENTER;
- c.insets = i;
- c.weightx = 1;
- c.weighty = 0;
-
- c.gridx = 0;
- c.gridy = 1;
- c.gridwidth = 10;
-
- pane.add(pbar, c);
-
- site_list_label = new JLabel ("Known Launch Sites:");
-
- c.fill = GridBagConstraints.NONE;
- c.anchor = GridBagConstraints.CENTER;
- c.insets = i;
- c.weightx = 1;
- c.weighty = 0;
-
- c.gridx = 0;
- c.gridy = 2;
- c.gridwidth = 1;
-
- pane.add(site_list_label, c);
-
- site_list = new JComboBox<AltosUISite>(new AltosUISite[] { new AltosUISite("Site List", 0, 0) });
- site_list.addItemListener(this);
-
- sites = new AltosUISites(this);
-
- c.fill = GridBagConstraints.HORIZONTAL;
- c.anchor = GridBagConstraints.CENTER;
- c.insets = i;
- c.weightx = 1;
- c.weighty = 0;
-
- c.gridx = 1;
- c.gridy = 2;
- c.gridwidth = 1;
-
- pane.add(site_list, c);
-
- lat = new AltosUIMapPos(owner,
- "Latitude:",
- lat_hemi_names,
- 37.167833333);
- c.fill = GridBagConstraints.NONE;
- c.anchor = GridBagConstraints.CENTER;
- c.insets = i;
- c.weightx = 0;
- c.weighty = 0;
-
- c.gridx = 0;
- c.gridy = 3;
- c.gridwidth = 1;
- c.anchor = GridBagConstraints.CENTER;
-
- pane.add(lat, c);
-
- lon = new AltosUIMapPos(owner,
- "Longitude:",
- lon_hemi_names,
- -97.73975);
-
- c.fill = GridBagConstraints.NONE;
- c.anchor = GridBagConstraints.CENTER;
- c.insets = i;
- c.weightx = 0;
- c.weighty = 0;
-
- c.gridx = 1;
- c.gridy = 3;
- c.gridwidth = 1;
- c.anchor = GridBagConstraints.CENTER;
-
- pane.add(lon, c);
-
- load_button = new JToggleButton("Load Map");
- load_button.addActionListener(this);
- load_button.setActionCommand("load");
-
- c.fill = GridBagConstraints.NONE;
- c.anchor = GridBagConstraints.CENTER;
- c.insets = i;
- c.weightx = 1;
- c.weighty = 0;
-
- c.gridx = 0;
- c.gridy = 4;
- c.gridwidth = 1;
- c.anchor = GridBagConstraints.CENTER;
-
- pane.add(load_button, c);
-
- close_button = new JButton("Close");
- close_button.addActionListener(this);
- close_button.setActionCommand("close");
-
- c.fill = GridBagConstraints.NONE;
- c.anchor = GridBagConstraints.CENTER;
- c.insets = i;
- c.weightx = 1;
- c.weighty = 0;
-
- c.gridx = 1;
- c.gridy = 4;
- c.gridwidth = 1;
- c.anchor = GridBagConstraints.CENTER;
-
- pane.add(close_button, c);
-
- JLabel types_label = new JLabel("Map Types");
- c.gridx = 2;
- c.gridwidth = 2;
- c.gridy = 2;
- pane.add(types_label, c);
-
- c.gridwidth = 1;
-
- for (int type = AltosUIMap.maptype_hybrid; type <= AltosUIMap.maptype_terrain; type++) {
- maptypes[type] = new JCheckBox(AltosUIMap.maptype_labels[type],
- type == AltosUIMap.maptype_hybrid);
- c.gridx = 2 + (type >> 1);
- c.fill = GridBagConstraints.HORIZONTAL;
- c.gridy = (type & 1) + 3;
- pane.add(maptypes[type], c);
- }
-
- JLabel min_zoom_label = new JLabel("Minimum Zoom");
- c.gridx = 4;
- c.gridy = 2;
- pane.add(min_zoom_label, c);
-
- min_zoom = new JComboBox<Integer>(zooms);
- min_zoom.setSelectedItem(zooms[10]);
- min_zoom.setEditable(false);
- c.gridx = 5;
- c.gridy = 2;
- pane.add(min_zoom, c);
-
- JLabel max_zoom_label = new JLabel("Maximum Zoom");
- c.gridx = 4;
- c.gridy = 3;
- pane.add(max_zoom_label, c);
-
- max_zoom = new JComboBox<Integer>(zooms);
- max_zoom.setSelectedItem(zooms[14]);
- max_zoom.setEditable(false);
- c.gridx = 5;
- c.gridy = 3;
- pane.add(max_zoom, c);
-
- JLabel radius_label = new JLabel("Tile Radius");
- c.gridx = 4;
- c.gridy = 4;
- pane.add(radius_label, c);
-
- radius = new JComboBox<Integer>(radii);
- radius.setSelectedItem(radii[4]);
- radius.setEditable(true);
- c.gridx = 5;
- c.gridy = 4;
- pane.add(radius, c);
-
- pack();
- setLocationRelativeTo(owner);
- setVisible(true);
- }
-}
--- /dev/null
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altosuilib_7;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import java.lang.Math;
+import java.net.URL;
+import java.net.URLConnection;
+import org.altusmetrum.altoslib_7.*;
+
+class AltosUIMapPos extends Box {
+ AltosUIFrame owner;
+ JLabel label;
+ JComboBox hemi;
+ JTextField deg;
+ JLabel deg_label;
+ JTextField min;
+ JLabel min_label;
+
+ public void set_value(double new_value) {
+ double d, m;
+ int h;
+
+ h = 0;
+ if (new_value < 0) {
+ h = 1;
+ new_value = -new_value;
+ }
+ d = Math.floor(new_value);
+ deg.setText(String.format("%3.0f", d));
+ m = (new_value - d) * 60.0;
+ min.setText(String.format("%7.4f", m));
+ hemi.setSelectedIndex(h);
+ }
+
+ public double get_value() throws ParseException {
+ int h = hemi.getSelectedIndex();
+ String d_t = deg.getText();
+ String m_t = min.getText();
+ double d, m, v;
+ try {
+ d = AltosParse.parse_double_locale(d_t);
+ } catch (ParseException pe) {
+ JOptionPane.showMessageDialog(owner,
+ String.format("Invalid degrees \"%s\"",
+ d_t),
+ "Invalid number",
+ JOptionPane.ERROR_MESSAGE);
+ throw pe;
+ }
+ try {
+ if (m_t.equals(""))
+ m = 0;
+ else
+ m = AltosParse.parse_double_locale(m_t);
+ } catch (ParseException pe) {
+ JOptionPane.showMessageDialog(owner,
+ String.format("Invalid minutes \"%s\"",
+ m_t),
+ "Invalid number",
+ JOptionPane.ERROR_MESSAGE);
+ throw pe;
+ }
+ v = d + m/60.0;
+ if (h == 1)
+ v = -v;
+ return v;
+ }
+
+ public AltosUIMapPos(AltosUIFrame in_owner,
+ String label_value,
+ String[] hemi_names,
+ double default_value) {
+ super(BoxLayout.X_AXIS);
+ owner = in_owner;
+ label = new JLabel(label_value);
+ hemi = new JComboBox<String>(hemi_names);
+ hemi.setEditable(false);
+ deg = new JTextField(5);
+ deg.setMinimumSize(deg.getPreferredSize());
+ deg.setHorizontalAlignment(JTextField.RIGHT);
+ deg_label = new JLabel("°");
+ min = new JTextField(9);
+ min.setMinimumSize(min.getPreferredSize());
+ min_label = new JLabel("'");
+ set_value(default_value);
+ add(label);
+ add(Box.createRigidArea(new Dimension(5, 0)));
+ add(hemi);
+ add(Box.createRigidArea(new Dimension(5, 0)));
+ add(deg);
+ add(Box.createRigidArea(new Dimension(5, 0)));
+ add(deg_label);
+ add(Box.createRigidArea(new Dimension(5, 0)));
+ add(min);
+ add(Box.createRigidArea(new Dimension(5, 0)));
+ add(min_label);
+ }
+}
+
+public class AltosUIMapPreloadNew extends AltosUIFrame implements ActionListener, ItemListener, AltosLaunchSiteListener, AltosMapLoaderListener {
+ AltosUIFrame owner;
+ AltosUIMapNew map;
+
+ AltosUIMapPos lat;
+ AltosUIMapPos lon;
+
+ JProgressBar pbar;
+
+ AltosMapLoader loader;
+
+ JLabel site_list_label;
+ JComboBox<AltosLaunchSite> site_list;
+
+ JToggleButton load_button;
+ boolean loading;
+ JButton close_button;
+
+ JCheckBox[] maptypes = new JCheckBox[AltosMap.maptype_terrain - AltosMap.maptype_hybrid + 1];
+
+ JComboBox<Integer> min_zoom;
+ JComboBox<Integer> max_zoom;
+ JComboBox<Double> radius;
+
+ Integer[] zooms = { -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6 };
+
+ Double[] radius_mi = { 1.0, 2.0, 5.0, 10.0, 20.0 };
+ Double radius_def_mi = 5.0;
+ Double[] radius_km = { 2.0, 5.0, 10.0, 20.0, 30.0 };
+ Double radius_def_km = 10.0;
+
+
+ static final String[] lat_hemi_names = { "N", "S" };
+ static final String[] lon_hemi_names = { "E", "W" };
+
+ double latitude, longitude;
+
+ /* AltosMapLoaderListener interfaces */
+ public void loader_start(final int max) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ pbar.setMaximum(max);
+ pbar.setValue(0);
+ pbar.setString("");
+ map.clear_marks();
+ map.add_mark(latitude, longitude, AltosLib.ao_flight_boost);
+ }
+ });
+ }
+
+ public void loader_notify(final int cur, final int max, final String name) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ pbar.setValue(cur);
+ pbar.setString(name);
+ }
+ });
+ }
+
+ public void loader_done(int max) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ pbar.setValue(0);
+ pbar.setString("");
+ load_button.setSelected(false);
+ loading = false;
+ }
+ });
+ }
+
+ public void debug(String format, Object ... arguments) {
+ System.out.printf(format, arguments);
+ }
+
+
+ private int all_types() {
+ int all_types = 0;
+ for (int t = AltosMap.maptype_hybrid; t <= AltosMap.maptype_terrain; t++)
+ if (maptypes[t].isSelected())
+ all_types |= (1 << t);
+ return all_types;
+ }
+
+ public void itemStateChanged(ItemEvent e) {
+ int state = e.getStateChange();
+
+ if (state == ItemEvent.SELECTED) {
+ Object o = e.getItem();
+ if (o instanceof AltosLaunchSite) {
+ AltosLaunchSite site = (AltosLaunchSite) o;
+ lat.set_value(site.latitude);
+ lon.set_value(site.longitude);
+ }
+ }
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+
+ if (cmd.equals("close"))
+ setVisible(false);
+
+ if (cmd.equals("load")) {
+ if (!loading) {
+ try {
+ latitude = lat.get_value();
+ longitude = lon.get_value();
+ int min_z = (Integer) min_zoom.getSelectedItem();
+ int max_z = (Integer) max_zoom.getSelectedItem();
+ if (max_z < min_z)
+ max_z = min_z;
+ Double r = (Double) radius.getSelectedItem();
+
+ if (AltosPreferences.imperial_units())
+ r = AltosConvert.distance.inverse(r);
+ else
+ r = r * 1000;
+ loading = true;
+
+ loader.load(latitude, longitude, min_z, max_z, r, all_types());
+ } catch (ParseException pe) {
+ load_button.setSelected(false);
+ }
+ }
+ }
+ }
+
+ public void notify_launch_sites(final java.util.List<AltosLaunchSite> sites) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ int i = 1;
+ for (AltosLaunchSite site : sites) {
+ site_list.insertItemAt(site, i);
+ i++;
+ }
+ }
+ });
+ }
+
+ public AltosUIMapPreloadNew(AltosUIFrame in_owner) {
+ owner = in_owner;
+
+ Container pane = getContentPane();
+ GridBagConstraints c = new GridBagConstraints();
+ Insets i = new Insets(4,4,4,4);
+
+ setTitle("AltOS Load Maps");
+
+ pane.setLayout(new GridBagLayout());
+
+ map = new AltosUIMapNew();
+
+ loader = new AltosMapLoader(map.map, this);
+
+ c.fill = GridBagConstraints.BOTH;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 1;
+
+ c.gridx = 0;
+ c.gridy = 0;
+ c.gridwidth = 10;
+ c.anchor = GridBagConstraints.CENTER;
+
+ pane.add(map, c);
+
+ pbar = new JProgressBar();
+ pbar.setMinimum(0);
+ pbar.setMaximum(1);
+ pbar.setValue(0);
+ pbar.setString("");
+ pbar.setStringPainted(true);
+
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 0;
+
+ c.gridx = 0;
+ c.gridy = 1;
+ c.gridwidth = 10;
+
+ pane.add(pbar, c);
+
+ site_list_label = new JLabel ("Known Launch Sites:");
+
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 0;
+
+ c.gridx = 0;
+ c.gridy = 2;
+ c.gridwidth = 1;
+
+ pane.add(site_list_label, c);
+
+ site_list = new JComboBox<AltosLaunchSite>(new AltosLaunchSite[] { new AltosLaunchSite("Site List", 0, 0) });
+ site_list.addItemListener(this);
+
+ new AltosLaunchSites(this);
+
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 0;
+
+ c.gridx = 1;
+ c.gridy = 2;
+ c.gridwidth = 1;
+
+ pane.add(site_list, c);
+
+ lat = new AltosUIMapPos(owner,
+ "Latitude:",
+ lat_hemi_names,
+ 37.167833333);
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 0;
+ c.weighty = 0;
+
+ c.gridx = 0;
+ c.gridy = 3;
+ c.gridwidth = 1;
+ c.anchor = GridBagConstraints.CENTER;
+
+ pane.add(lat, c);
+
+ lon = new AltosUIMapPos(owner,
+ "Longitude:",
+ lon_hemi_names,
+ -97.73975);
+
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 0;
+ c.weighty = 0;
+
+ c.gridx = 1;
+ c.gridy = 3;
+ c.gridwidth = 1;
+ c.anchor = GridBagConstraints.CENTER;
+
+ pane.add(lon, c);
+
+ load_button = new JToggleButton("Load Map");
+ load_button.addActionListener(this);
+ load_button.setActionCommand("load");
+
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 0;
+
+ c.gridx = 0;
+ c.gridy = 4;
+ c.gridwidth = 1;
+ c.anchor = GridBagConstraints.CENTER;
+
+ pane.add(load_button, c);
+
+ close_button = new JButton("Close");
+ close_button.addActionListener(this);
+ close_button.setActionCommand("close");
+
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 0;
+
+ c.gridx = 1;
+ c.gridy = 4;
+ c.gridwidth = 1;
+ c.anchor = GridBagConstraints.CENTER;
+
+ pane.add(close_button, c);
+
+ JLabel types_label = new JLabel("Map Types");
+ c.gridx = 2;
+ c.gridwidth = 2;
+ c.gridy = 2;
+ pane.add(types_label, c);
+
+ c.gridwidth = 1;
+
+ for (int type = AltosMap.maptype_hybrid; type <= AltosMap.maptype_terrain; type++) {
+ maptypes[type] = new JCheckBox(AltosMap.maptype_labels[type],
+ type == AltosMap.maptype_hybrid);
+ c.gridx = 2 + (type >> 1);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridy = (type & 1) + 3;
+ pane.add(maptypes[type], c);
+ }
+
+ JLabel min_zoom_label = new JLabel("Minimum Zoom");
+ c.gridx = 4;
+ c.gridy = 2;
+ pane.add(min_zoom_label, c);
+
+ min_zoom = new JComboBox<Integer>(zooms);
+ min_zoom.setSelectedItem(zooms[10]);
+ min_zoom.setEditable(false);
+ c.gridx = 5;
+ c.gridy = 2;
+ pane.add(min_zoom, c);
+
+ JLabel max_zoom_label = new JLabel("Maximum Zoom");
+ c.gridx = 4;
+ c.gridy = 3;
+ pane.add(max_zoom_label, c);
+
+ max_zoom = new JComboBox<Integer>(zooms);
+ max_zoom.setSelectedItem(zooms[14]);
+ max_zoom.setEditable(false);
+ c.gridx = 5;
+ c.gridy = 3;
+ pane.add(max_zoom, c);
+
+ JLabel radius_label = new JLabel(String.format("Map Radius (%s)",
+ AltosPreferences.imperial_units() ? "miles" : "km"));
+ c.gridx = 4;
+ c.gridy = 4;
+ pane.add(radius_label, c);
+
+ Double[] radii;
+ Double radius_default;
+
+ if (AltosPreferences.imperial_units())
+ radii = radius_mi;
+ else
+ radii = radius_km;
+ radius = new JComboBox<Double>(radii);
+ radius.setSelectedItem(radii[2]);
+ radius.setEditable(true);
+ c.gridx = 5;
+ c.gridy = 4;
+ pane.add(radius, c);
+
+ pack();
+ setLocationRelativeTo(owner);
+ setVisible(true);
+ }
+}
+++ /dev/null
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-public class AltosUIMapRectangle {
- AltosUILatLon ul, lr;
-
- public AltosUIMapRectangle(AltosUILatLon a, AltosUILatLon b) {
- double ul_lat, ul_lon;
- double lr_lat, lr_lon;
-
- if (a.lat > b.lat) {
- ul_lat = a.lat;
- lr_lat = b.lat;
- } else {
- ul_lat = b.lat;
- lr_lat = a.lat;
- }
- if (a.lon < b.lon) {
- ul_lon = a.lon;
- lr_lon = b.lon;
- } else {
- ul_lon = b.lon;
- lr_lon = a.lon;
- }
-
- ul = new AltosUILatLon(ul_lat, ul_lon);
- lr = new AltosUILatLon(lr_lat, lr_lon);
- }
-}
+++ /dev/null
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
-public class AltosUIMapStore {
- String url;
- File file;
- LinkedList<AltosUIMapStoreListener> listeners = new LinkedList<AltosUIMapStoreListener>();
-
- static final int success = 0;
- static final int loading = 1;
- static final int failed = 2;
- static final int bad_request = 3;
- static final int forbidden = 4;
-
- int status;
-
- public int status() {
- return status;
- }
-
- public synchronized void add_listener(AltosUIMapStoreListener listener) {
- if (!listeners.contains(listener))
- listeners.add(listener);
- }
-
- public synchronized void remove_listener(AltosUIMapStoreListener listener) {
- listeners.remove(listener);
- }
-
- private synchronized void notify_listeners(int status) {
- this.status = status;
- for (AltosUIMapStoreListener listener : listeners)
- listener.notify_store(this, status);
- }
-
- static Object forbidden_lock = new Object();
- static long forbidden_time;
- static boolean forbidden_set;
-
- private int fetch_url() {
- URL u;
-
- try {
- u = new URL(url);
- } catch (java.net.MalformedURLException e) {
- return bad_request;
- }
-
- byte[] data;
- URLConnection uc = null;
- try {
- uc = u.openConnection();
- String type = uc.getContentType();
- int contentLength = uc.getContentLength();
- if (uc instanceof HttpURLConnection) {
- int response = ((HttpURLConnection) uc).getResponseCode();
- switch (response) {
- case HttpURLConnection.HTTP_FORBIDDEN:
- case HttpURLConnection.HTTP_PAYMENT_REQUIRED:
- case HttpURLConnection.HTTP_UNAUTHORIZED:
- synchronized (forbidden_lock) {
- forbidden_time = System.nanoTime();
- forbidden_set = true;
- return forbidden;
- }
- }
- }
- InputStream in = new BufferedInputStream(uc.getInputStream());
- int bytesRead = 0;
- int offset = 0;
- data = new byte[contentLength];
- while (offset < contentLength) {
- bytesRead = in.read(data, offset, data.length - offset);
- if (bytesRead == -1)
- break;
- offset += bytesRead;
- }
- in.close();
-
- if (offset != contentLength)
- return failed;
-
- } catch (IOException e) {
- return failed;
- }
-
- try {
- FileOutputStream out = new FileOutputStream(file);
- out.write(data);
- out.flush();
- out.close();
- } catch (FileNotFoundException e) {
- return bad_request;
- } catch (IOException e) {
- if (file.exists())
- file.delete();
- return bad_request;
- }
- return success;
- }
-
- static Object fetch_lock = new Object();
-
- static final long forbidden_interval = 60l * 1000l * 1000l * 1000l;
- static final long google_maps_ratelimit_ms = 1200;
-
- class loader implements Runnable {
-
- public void run() {
- if (file.exists()) {
- notify_listeners(success);
- return;
- }
-
- synchronized(forbidden_lock) {
- if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) {
- notify_listeners(forbidden);
- return;
- }
- }
-
- int new_status;
-
- if (!AltosUIVersion.has_google_maps_api_key()) {
- synchronized (fetch_lock) {
- long startTime = System.nanoTime();
- new_status = fetch_url();
- if (new_status == success) {
- long duration_ms = (System.nanoTime() - startTime) / 1000000;
- if (duration_ms < google_maps_ratelimit_ms) {
- try {
- Thread.sleep(google_maps_ratelimit_ms - duration_ms);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- }
- }
- } else {
- new_status = fetch_url();
- }
- notify_listeners(new_status);
- }
- }
-
- private void load() {
- loader l = new loader();
- Thread lt = new Thread(l);
- lt.start();
- }
-
- private AltosUIMapStore (String url, File file) {
- this.url = url;
- this.file = file;
-
- if (file.exists())
- status = success;
- else {
- status = loading;
- load();
- }
- }
-
- public boolean equals(AltosUIMapStore other) {
- return url.equals(other.url);
- }
-
- static HashMap<String,AltosUIMapStore> stores = new HashMap<String,AltosUIMapStore>();
-
- public static AltosUIMapStore get(String url, File file) {
- AltosUIMapStore store;
- synchronized(stores) {
- if (stores.containsKey(url)) {
- store = stores.get(url);
- } else {
- store = new AltosUIMapStore(url, file);
- stores.put(url, store);
- }
- }
- return store;
- }
-
-}
+++ /dev/null
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-public interface AltosUIMapStoreListener {
- abstract void notify_store(AltosUIMapStore store, int status);
-}
+++ /dev/null
-/*
- * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import java.awt.*;
-import java.awt.image.*;
-import javax.swing.*;
-import javax.imageio.*;
-import java.awt.geom.*;
-import java.io.*;
-import java.util.*;
-import java.awt.RenderingHints.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUIMapTile {
- AltosUIMapTileListener listener;
- AltosUILatLon upper_left, center;
- int px_size;
- int zoom;
- int maptype;
- AltosUIMapStore store;
- AltosUIMapCache cache;
- int status;
-
- private File map_file() {
- double lat = center.lat;
- double lon = center.lon;
- char chlat = lat < 0 ? 'S' : 'N';
- char chlon = lon < 0 ? 'W' : 'E';
-
- if (lat < 0) lat = -lat;
- if (lon < 0) lon = -lon;
- String maptype_string = String.format("%s-", AltosUIMap.maptype_names[maptype]);
- String format_string;
- if (maptype == AltosUIMap.maptype_hybrid || maptype == AltosUIMap.maptype_satellite || maptype == AltosUIMap.maptype_terrain)
- format_string = "jpg";
- else
- format_string = "png";
- return new File(AltosUIPreferences.mapdir(),
- String.format("map-%c%.6f,%c%.6f-%s%d.%s",
- chlat, lat, chlon, lon, maptype_string, zoom, format_string));
- }
-
- private String map_url() {
- String format_string;
- if (maptype == AltosUIMap.maptype_hybrid || maptype == AltosUIMap.maptype_satellite || maptype == AltosUIMap.maptype_terrain)
- format_string = "jpg";
- else
- format_string = "png32";
-
- if (AltosUIVersion.has_google_maps_api_key())
- return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=%s&format=%s&key=%s",
- center.lat, center.lon, zoom, px_size, px_size, AltosUIMap.maptype_names[maptype], format_string, AltosUIVersion.google_maps_api_key);
- else
- return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=%s&format=%s",
- center.lat, center.lon, zoom, px_size, px_size, AltosUIMap.maptype_names[maptype], format_string);
- }
- private Font font = null;
-
- public void set_font(Font font) {
- this.font = font;
- }
-
- int painting_serial;
- int painted_serial;
-
- Image image;
-
- public void paint_graphics(Graphics2D g2d, AltosUIMapTransform t, int serial) {
- if (serial < painted_serial)
- return;
-
- Point2D.Double point_double = t.screen(upper_left);
- Point point = new Point((int) (point_double.x + 0.5),
- (int) (point_double.y + 0.5));
-
- painted_serial = serial;
-
- if (!g2d.hitClip(point.x, point.y, px_size, px_size))
- return;
-
- if (image != null) {
- g2d.drawImage(image, point.x, point.y, null);
- image = null;
- } else {
- g2d.setColor(Color.GRAY);
- g2d.fillRect(point.x, point.y, px_size, px_size);
-
- if (t.has_location()) {
- String message = null;
- switch (status) {
- case AltosUIMapCache.loading:
- message = "Loading...";
- break;
- case AltosUIMapCache.bad_request:
- message = "Internal error";
- break;
- case AltosUIMapCache.failed:
- message = "Network error, check connection";
- break;
- case AltosUIMapCache.forbidden:
- message = "Too many requests, try later";
- break;
- }
- if (message != null && font != null) {
- g2d.setFont(font);
- g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
- Rectangle2D bounds = font.getStringBounds(message, g2d.getFontRenderContext());
-
- float x = px_size / 2.0f;
- float y = px_size / 2.0f;
- x = x - (float) bounds.getWidth() / 2.0f;
- y = y + (float) bounds.getHeight() / 2.0f;
- g2d.setColor(Color.BLACK);
- g2d.drawString(message, (float) point_double.x + x, (float) point_double.y + y);
- }
- }
- }
- }
-
- public void set_status(int status) {
- this.status = status;
- listener.notify_tile(this, status);
- }
-
- public void notify_image(Image image) {
- listener.notify_tile(this, status);
- }
-
- public void paint(Graphics g, AltosUIMapTransform t) {
- Graphics2D g2d = (Graphics2D) g;
- boolean queued = false;
-
- Point2D.Double point = t.screen(upper_left);
-
- if (!g.hitClip((int) (point.x + 0.5), (int) (point.y + 0.5), px_size, px_size))
- return;
-
- ++painting_serial;
-
- if (image == null && t.has_location())
- image = cache.get(this, store, px_size, px_size);
-
- paint_graphics(g2d, t, painting_serial);
- }
-
- public int store_status() {
- return store.status();
- }
-
- public void add_store_listener(AltosUIMapStoreListener listener) {
- store.add_listener(listener);
- }
-
- public void remove_store_listener(AltosUIMapStoreListener listener) {
- store.remove_listener(listener);
- }
-
- public AltosUIMapTile(AltosUIMapTileListener listener, AltosUILatLon upper_left, AltosUILatLon center, int zoom, int maptype, int px_size, Font font) {
- this.listener = listener;
- this.upper_left = upper_left;
- cache = listener.cache();
-
- while (center.lon < -180.0)
- center.lon += 360.0;
- while (center.lon > 180.0)
- center.lon -= 360.0;
-
- this.center = center;
- this.zoom = zoom;
- this.maptype = maptype;
- this.px_size = px_size;
- this.font = font;
- status = AltosUIMapCache.loading;
- store = AltosUIMapStore.get(map_url(), map_file());
- }
-}
+++ /dev/null
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-public interface AltosUIMapTileListener {
- abstract public void notify_tile(AltosUIMapTile tile, int status);
-
- abstract public AltosUIMapCache cache();
-}
+++ /dev/null
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.Math;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUIMapTransform {
-
- double scale_x, scale_y;
-
- double offset_x, offset_y;
-
- public AltosUILatLon lat_lon (Point2D.Double point) {
- double lat, lon;
- double rads;
-
- lon = point.x/scale_x;
- rads = 2 * Math.atan(Math.exp(-point.y/scale_y));
- lat = Math.toDegrees(rads - Math.PI/2);
-
- return new AltosUILatLon(lat,lon);
- }
-
- public Point2D.Double screen_point(Point screen) {
- return new Point2D.Double(screen.x + offset_x, screen.y + offset_y);
- }
-
- public AltosUILatLon screen_lat_lon(Point screen) {
- return lat_lon(screen_point(screen));
- }
-
- public Point2D.Double point(AltosUILatLon lat_lon) {
- double x, y;
- double e;
-
- x = lat_lon.lon * scale_x;
-
- e = Math.sin(Math.toRadians(lat_lon.lat));
- e = Math.max(e,-(1-1.0E-15));
- e = Math.min(e, 1-1.0E-15 );
-
- y = 0.5*Math.log((1+e)/(1-e))*-scale_y;
-
- return new Point2D.Double(x, y);
- }
-
- public Point2D.Double screen(Point2D.Double point) {
- return new Point2D.Double(point.x - offset_x, point.y - offset_y);
- }
-
- public Point screen(Point point) {
- return new Point((int) (point.x - offset_x + 0.5),
- (int) (point.y - offset_y + 0.5));
- }
-
- public Rectangle screen(AltosUIMapRectangle map_rect) {
- Point2D.Double ul = screen(map_rect.ul);
- Point2D.Double lr = screen(map_rect.lr);
-
- return new Rectangle((int) ul.x, (int) ul.y, (int) (lr.x - ul.x), (int) (lr.y - ul.y));
- }
-
- public Point2D.Double screen(AltosUILatLon lat_lon) {
- return screen(point(lat_lon));
- }
-
- private boolean has_location;
-
- public boolean has_location() {
- return has_location;
- }
-
- public AltosUIMapTransform(int width, int height, int zoom, AltosUILatLon centre_lat_lon) {
- scale_x = 256/360.0 * Math.pow(2, zoom);
- scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom);
-
- Point2D.Double centre_pt = point(centre_lat_lon);
-
- has_location = (centre_lat_lon.lat != 0 || centre_lat_lon.lon != 0);
- offset_x = centre_pt.x - width / 2.0;
- offset_y = centre_pt.y - height / 2.0;
- }
-}
+++ /dev/null
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.image.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.*;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUIMapView extends Component implements MouseMotionListener, MouseListener, MouseWheelListener, ComponentListener, AltosUIMapTileListener, AltosUIMapStoreListener {
-
- AltosUIMapPath path = new AltosUIMapPath();
-
- AltosUIMapLine line = new AltosUIMapLine();
-
- AltosUIMapCache cache = new AltosUIMapCache();
-
- LinkedList<AltosUIMapMark> marks = new LinkedList<AltosUIMapMark>();
-
- LinkedList<AltosUIMapZoomListener> zoom_listeners = new LinkedList<AltosUIMapZoomListener>();
-
- boolean have_boost = false;
- boolean have_landed = false;
-
- ConcurrentHashMap<Point,AltosUIMapTile> tiles = new ConcurrentHashMap<Point,AltosUIMapTile>();
-
- static final int default_zoom = 15;
- static final int min_zoom = 3;
- static final int max_zoom = 21;
- static final int px_size = 512;
-
- int load_radius;
- AltosUILatLon load_centre = null;
- AltosUIMapTileListener load_listener;
-
- int zoom = default_zoom;
- int maptype = AltosUIMap.maptype_default;
-
- long user_input_time;
-
- /* Milliseconds to wait after user action before auto-scrolling
- */
- static final long auto_scroll_delay = 20 * 1000;
-
- AltosUIMapTransform transform;
- AltosUILatLon centre;
-
- public void set_font() {
- line.set_font(AltosUILib.status_font);
- for (AltosUIMapTile tile : tiles.values())
- tile.set_font(AltosUILib.value_font);
- repaint();
- }
-
- public void set_units() {
- repaint();
- }
-
- private boolean is_drag_event(MouseEvent e) {
- return e.getModifiers() == InputEvent.BUTTON1_MASK;
- }
-
- Point drag_start;
-
- private void drag(MouseEvent e) {
- if (drag_start == null)
- return;
-
- int dx = e.getPoint().x - drag_start.x;
- int dy = e.getPoint().y - drag_start.y;
-
- AltosUILatLon new_centre = transform.screen_lat_lon(new Point(getWidth() / 2 - dx, getHeight() / 2 - dy));
- centre (new_centre.lat, new_centre.lon);
- drag_start = e.getPoint();
- }
-
- private void drag_start(MouseEvent e) {
- drag_start = e.getPoint();
- }
-
- private void notice_user_input() {
- user_input_time = System.currentTimeMillis();
- }
-
- private boolean recent_user_input() {
- return (System.currentTimeMillis() - user_input_time) < auto_scroll_delay;
- }
-
- /* MouseMotionListener methods */
-
- public void mouseDragged(MouseEvent e) {
- notice_user_input();
- if (is_drag_event(e))
- drag(e);
- else {
- line.dragged(e, transform);
- repaint();
- }
- }
-
- public void mouseMoved(MouseEvent e) {
- }
-
- /* MouseListener methods */
- public void mouseClicked(MouseEvent e) {
- }
-
- public void mouseEntered(MouseEvent e) {
- }
-
- public void mouseExited(MouseEvent e) {
- }
-
- public void mousePressed(MouseEvent e) {
- notice_user_input();
- if (is_drag_event(e))
- drag_start(e);
- else {
- line.pressed(e, transform);
- repaint();
- }
- }
-
- public void mouseReleased(MouseEvent e) {
- }
-
- /* MouseWheelListener methods */
-
- public void mouseWheelMoved(MouseWheelEvent e) {
- int zoom_change = e.getWheelRotation();
-
- notice_user_input();
- AltosUILatLon mouse_lat_lon = transform.screen_lat_lon(e.getPoint());
- set_zoom(zoom() - zoom_change);
-
- Point2D.Double new_mouse = transform.screen(mouse_lat_lon);
-
- int dx = getWidth()/2 - e.getPoint().x;
- int dy = getHeight()/2 - e.getPoint().y;
-
- AltosUILatLon new_centre = transform.screen_lat_lon(new Point((int) new_mouse.x + dx, (int) new_mouse.y + dy));
-
- centre(new_centre.lat, new_centre.lon);
- }
-
- /* ComponentListener methods */
-
- public void componentHidden(ComponentEvent e) {
- }
-
- public void componentMoved(ComponentEvent e) {
- }
-
- public void componentResized(ComponentEvent e) {
- set_transform();
- }
-
- public void componentShown(ComponentEvent e) {
- set_transform();
- }
-
- public void repaint(Rectangle r, int pad) {
- repaint(r.x - pad, r.y - pad, r.width + pad*2, r.height + pad*2);
- }
-
- public void repaint(AltosUIMapRectangle rect, int pad) {
- repaint (transform.screen(rect), pad);
- }
-
- private boolean far_from_centre(AltosUILatLon lat_lon) {
-
- if (centre == null || transform == null)
- return true;
-
- Point2D.Double screen = transform.screen(lat_lon);
-
- int width = getWidth();
- int dx = Math.abs ((int) screen.x - width/2);
-
- if (dx > width / 4)
- return true;
-
- int height = getHeight();
- int dy = Math.abs ((int) screen.y - height/2);
-
- if (dy > height / 4)
- return true;
-
- return false;
- }
-
- public void show(AltosState state, AltosListenerState listener_state) {
-
- /* If insufficient gps data, nothing to update
- */
- AltosGPS gps = state.gps;
-
- if (gps == null)
- return;
-
- if (!gps.locked && gps.nsat < 4)
- return;
-
- AltosUIMapRectangle damage = path.add(gps.lat, gps.lon, state.state);
-
- switch (state.state) {
- case AltosLib.ao_flight_boost:
- if (!have_boost) {
- add_mark(gps.lat, gps.lon, state.state);
- have_boost = true;
- }
- break;
- case AltosLib.ao_flight_landed:
- if (!have_landed) {
- add_mark(gps.lat, gps.lon, state.state);
- have_landed = true;
- }
- break;
- }
-
- if (damage != null)
- repaint(damage, AltosUIMapPath.stroke_width);
- maybe_centre(gps.lat, gps.lon);
- }
-
- private void set_transform() {
- Rectangle bounds = getBounds();
-
- transform = new AltosUIMapTransform(bounds.width, bounds.height, zoom, centre);
- repaint();
- }
-
- public boolean set_zoom(int zoom) {
- if (min_zoom <= zoom && zoom <= max_zoom && zoom != this.zoom) {
- this.zoom = zoom;
- tiles.clear();
- set_transform();
-
- for (AltosUIMapZoomListener listener : zoom_listeners)
- listener.zoom_changed(this.zoom);
-
- return true;
- }
- return false;
- }
-
- public void add_zoom_listener(AltosUIMapZoomListener listener) {
- if (!zoom_listeners.contains(listener))
- zoom_listeners.add(listener);
- }
-
- public void remove_zoom_listener(AltosUIMapZoomListener listener) {
- zoom_listeners.remove(listener);
- }
-
- public void set_load_params(double lat, double lon, int radius, AltosUIMapTileListener listener) {
- load_centre = new AltosUILatLon(lat, lon);
- load_radius = radius;
- load_listener = listener;
- centre(lat, lon);
- make_tiles();
- for (AltosUIMapTile tile : tiles.values()) {
- tile.add_store_listener(this);
- if (tile.store_status() != AltosUIMapStore.loading)
- listener.notify_tile(tile, tile.store_status());
- }
- repaint();
- }
-
- public boolean all_fetched() {
- for (AltosUIMapTile tile : tiles.values()) {
- if (tile.store_status() == AltosUIMapStore.loading)
- return false;
- }
- return true;
- }
-
- public boolean set_maptype(int maptype) {
- if (maptype != this.maptype) {
- this.maptype = maptype;
- tiles.clear();
- repaint();
- return true;
- }
- return false;
- }
-
- public int get_maptype() {
- return maptype;
- }
-
- public int zoom() {
- return zoom;
- }
-
- public void centre(AltosUILatLon lat_lon) {
- centre = lat_lon;
- set_transform();
- }
-
- public void centre(double lat, double lon) {
- centre(new AltosUILatLon(lat, lon));
- }
-
- public void maybe_centre(double lat, double lon) {
- AltosUILatLon lat_lon = new AltosUILatLon(lat, lon);
- if (centre == null || (!recent_user_input() && far_from_centre(lat_lon)))
- centre(lat_lon);
- }
-
- private VolatileImage create_back_buffer() {
- return getGraphicsConfiguration().createCompatibleVolatileImage(getWidth(), getHeight());
- }
-
- private Point floor(Point2D.Double point) {
- return new Point ((int) Math.floor(point.x / px_size) * px_size,
- (int) Math.floor(point.y / px_size) * px_size);
- }
-
- private Point ceil(Point2D.Double point) {
- return new Point ((int) Math.ceil(point.x / px_size) * px_size,
- (int) Math.ceil(point.y / px_size) * px_size);
- }
-
- private void make_tiles() {
- Point upper_left;
- Point lower_right;
-
- if (load_centre != null) {
- Point centre = floor(transform.point(load_centre));
-
- upper_left = new Point(centre.x - load_radius * px_size,
- centre.y - load_radius * px_size);
- lower_right = new Point(centre.x + load_radius * px_size,
- centre.y + load_radius * px_size);
- } else {
- upper_left = floor(transform.screen_point(new Point(0, 0)));
- lower_right = floor(transform.screen_point(new Point(getWidth(), getHeight())));
- }
- LinkedList<Point> to_remove = new LinkedList<Point>();
-
- for (Point point : tiles.keySet()) {
- if (point.x < upper_left.x || lower_right.x < point.x ||
- point.y < upper_left.y || lower_right.y < point.y) {
- to_remove.add(point);
- }
- }
-
- for (Point point : to_remove)
- tiles.remove(point);
-
- cache.set_cache_size((getWidth() / px_size + 2) * (getHeight() / px_size + 2));
- for (int y = upper_left.y; y <= lower_right.y; y += px_size) {
- for (int x = upper_left.x; x <= lower_right.x; x += px_size) {
- Point point = new Point(x, y);
-
- if (!tiles.containsKey(point)) {
- AltosUILatLon ul = transform.lat_lon(new Point2D.Double(x, y));
- AltosUILatLon center = transform.lat_lon(new Point2D.Double(x + px_size/2, y + px_size/2));
- AltosUIMapTile tile = new AltosUIMapTile(this, ul, center, zoom, maptype,
- px_size, AltosUILib.value_font);
- tiles.put(point, tile);
- }
- }
- }
- }
-
- /* AltosUIMapTileListener methods */
- public synchronized void notify_tile(AltosUIMapTile tile, int status) {
- for (Point point : tiles.keySet()) {
- if (tile == tiles.get(point)) {
- Point screen = transform.screen(point);
- repaint(screen.x, screen.y, px_size, px_size);
- }
- }
- }
-
- public AltosUIMapCache cache() { return cache; }
-
- /* AltosUIMapStoreListener methods */
- public synchronized void notify_store(AltosUIMapStore store, int status) {
- if (load_listener != null) {
- for (AltosUIMapTile tile : tiles.values())
- if (store.equals(tile.store))
- load_listener.notify_tile(tile, status);
- }
- }
-
- private void do_paint(Graphics g) {
- Graphics2D g2d = (Graphics2D) g;
-
- make_tiles();
-
- for (AltosUIMapTile tile : tiles.values())
- tile.paint(g2d, transform);
-
- synchronized(marks) {
- for (AltosUIMapMark mark : marks)
- mark.paint(g2d, transform);
- }
-
- path.paint(g2d, transform);
-
- line.paint(g2d, transform);
- }
-
- public void paint(Graphics g) {
- VolatileImage back_buffer = create_back_buffer();
- do {
- GraphicsConfiguration gc = getGraphicsConfiguration();
- int code = back_buffer.validate(gc);
- if (code == VolatileImage.IMAGE_INCOMPATIBLE)
- back_buffer = create_back_buffer();
-
- Graphics g_back = back_buffer.getGraphics();
- g_back.setClip(g.getClip());
- do_paint(g_back);
- g_back.dispose();
-
- g.drawImage(back_buffer, 0, 0, this);
- } while (back_buffer.contentsLost());
- back_buffer.flush();
- }
-
- public void update(Graphics g) {
- paint(g);
- }
-
- public void add_mark(double lat, double lon, int state) {
- synchronized(marks) {
- marks.add(new AltosUIMapMark(lat, lon, state));
- }
- repaint();
- }
-
- public void clear_marks() {
- synchronized(marks) {
- marks.clear();
- }
- }
-
- public AltosUIMapView() {
- centre(0, 0);
-
- addComponentListener(this);
- addMouseMotionListener(this);
- addMouseListener(this);
- addMouseWheelListener(this);
- set_font();
- }
-}
+++ /dev/null
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-public interface AltosUIMapZoomListener {
- abstract public void zoom_changed(int zoom);
-}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.io.*;
import java.util.*;
import java.awt.Component;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosUIPreferences extends AltosPreferences {
public static int position = AltosUILib.position_top_left;
- static LinkedList<AltosUIMapCacheListener> map_cache_listeners;
-
- public static int map_cache = 9;
-
public static void init() {
AltosPreferences.init(new AltosUIPreferencesBackend());
position = backend.getInt(positionPreference, AltosUILib.position_top_left);
position_listeners = new LinkedList<AltosPositionListener>();
-
- map_cache = backend.getInt(mapCachePreference, 9);
- map_cache_listeners = new LinkedList<AltosUIMapCacheListener>();
}
static { init(); }
return position;
}
}
-
- public static void register_map_cache_listener(AltosUIMapCacheListener l) {
- synchronized(backend) {
- map_cache_listeners.add(l);
- }
- }
-
- public static void unregister_map_cache_listener(AltosUIMapCacheListener l) {
- synchronized (backend) {
- map_cache_listeners.remove(l);
- }
- }
-
- public static void set_map_cache(int new_map_cache) {
- synchronized(backend) {
- map_cache = new_map_cache;
- backend.putInt(mapCachePreference, map_cache);
- flush_preferences();
- for (AltosUIMapCacheListener l: map_cache_listeners)
- l.map_cache_changed(map_cache);
- }
- }
-
- public static int map_cache() {
- synchronized(backend) {
- return map_cache;
- }
- }
}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.io.File;
import java.util.prefs.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
import javax.swing.filechooser.FileSystemView;
public class AltosUIPreferencesBackend implements AltosPreferencesBackend {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosUIRateList extends JComboBox<String> {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.util.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class AltosUITelemetryList extends JComboBox<String> {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public abstract class AltosUIUnitsIndicator extends AltosUIIndicator {
return hide(value(state, i));
}
+ public boolean hide(AltosState state, AltosListenerState listener_state, int i) {
+ return hide(state, i);
+ }
+
public double value (AltosState state, AltosListenerState listener_state, int i) {
return value(state, i);
}
v[i] = value(state, listener_state, i);
else
v[i] = AltosLib.MISSING;
- if (hide(state, i))
+ if (hide(state, listener_state, i))
hide = true;
}
+++ /dev/null
-/*
- * Copyright © 2011 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-public class AltosUIVersion {
- public final static String version = "@VERSION@";
-
- public final static String google_maps_api_key = @GOOGLEKEY@;
-
- static boolean has_google_maps_api_key() {
- return google_maps_api_key != null && google_maps_api_key.length() > 1;
- }
-}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public abstract class AltosUIVoltageIndicator extends AltosUIUnitsIndicator {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.util.*;
import libaltosJNI.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import com.sun.speech.freetts.Voice;
import com.sun.speech.freetts.VoiceManager;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.awt.*;
import java.awt.event.*;
GrabNDrag.java \
AltosDevice.java \
AltosDeviceDialog.java \
- AltosFlightDisplay.java \
- AltosFontListener.java \
AltosPositionListener.java \
AltosUIConfigure.java \
AltosUIAxis.java \
AltosUIPreferencesBackend.java \
AltosUIPreferences.java \
AltosUISeries.java \
- AltosUIVersion.java \
AltosUSBDevice.java \
AltosVoice.java \
AltosDisplayThread.java \
AltosBTDeviceIterator.java \
AltosBTManage.java \
AltosBTKnown.java \
- AltosUIMap.java \
- AltosUIMapView.java \
- AltosUIMapLine.java \
- AltosUIMapMark.java \
- AltosUIMapPath.java \
- AltosUIMapTile.java \
- AltosUIMapCache.java \
- AltosUIMapCacheListener.java \
- AltosUIMapImage.java \
- AltosUIMapTransform.java \
- AltosUIMapRectangle.java \
- AltosUIMapZoomListener.java \
- AltosUIMapTileListener.java \
- AltosUIMapPreload.java \
- AltosUIMapStore.java \
- AltosUIMapStoreListener.java \
- AltosUILatLon.java \
+ AltosUIMapNew.java \
+ AltosUIMapPreloadNew.java \
AltosUIFlightTab.java \
AltosUIIndicator.java \
AltosUIUnitsIndicator.java \
AltosUIFreqList.java \
AltosUITelemetryList.java \
AltosUIRateList.java \
+ AltosUIImage.java \
OSXAdapter.java
JAR=altosuilib_$(ALTOSUILIB_VERSION).jar
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_7;
import java.lang.reflect.*;
import java.util.HashMap;
;;
esac
-while true; do
- echo 'C 1' > $dev
-
- echo -n "Generating RF carrier. Please enter measured frequency [enter for done]: "
-
- read FREQ
-
- echo 'C 0' > $dev
-
+../ao-tools/ao-cal-freq/ao-cal-freq --dev=$dev
+case $? in
+ 0)
calline=`./get-radio-cal $dev`
- CURRENT_CAL=`echo $calline | awk '{print $2}'`
+ CAL_VALUE=`echo $calline | awk '{print $2}'`
CURRENT_FREQ=`echo $calline | awk '{print $4}'`
- CAL_VALUE=$CURRENT_CAL
-
- case "$FREQ" in
- "")
- echo $SERIAL","$CAL_VALUE >> cal_values
- exit 0
- ;;
- *)
- echo "Current radio calibration "$CURRENT_CAL
- echo "Current radio frequency "$CURRENT_FREQ
-
- CAL_VALUE=`nickle -e "floor($CURRENT_FREQ / $FREQ * $CURRENT_CAL + 0.5)"`
-
- echo "Programming flash with cal value " $CAL_VALUE
-
- dd if=$dev iflag=nonblock
-
- cat << EOF > $dev
-c f $CAL_VALUE
-c w
-EOF
-
- echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE
- ;;
- esac
-done
-
+ echo $SERIAL","$CAL_VALUE >> cal_values
+ exit 0
+ ;;
+ *)
+ exit 1
+ ;;
+esac
echo
ret=1
-ao-list | while read product serial dev; do
+../ao-tools/ao-list/ao-list | while read product serial dev; do
case "$product" in
"$PRODUCT-v$VERSION")
echo""
echo "$PRODUCT-v$VERSION" serial "$serial" is ready to ship
+ echo "\007"
ret=0
;;
esac
#!/bin/sh
-if [ -x /usr/bin/ao-flash-stm ]; then
- STMLOAD=/usr/bin/ao-flash-stm
+if [ -x ../ao-tools/ao-flash/ao-flash-stm ]; then
+ STMLOAD=../ao-tools/ao-flash/ao-flash-stm
else
echo "Can't find ao-flash-stm! Aborting."
exit 1
fi
-if [ -x /usr/bin/ao-usbload ]; then
- USBLOAD=/usr/bin/ao-usbload
+if [ -x ../ao-tools/ao-usbload/ao-usbload ]; then
+ USBLOAD=../ao-tools/ao-usbload/ao-usbload
else
echo "Can't find ao-usbload! Aborting."
exit 1
sleep 2
-dev=`ao-list | awk '/EasyMega-v'"$VERSION"'/ { print $3; exit(0); }'`
+dev=`../ao-tools/ao-list/ao-list | awk '/EasyMega-v'"$VERSION"'/ { print $3; exit(0); }'`
case "$dev" in
/dev/tty*)
echo 'E 0' > $dev
-../ao-tools/ao-cal-accel/ao-cal-accel $dev
+../ao-tools/ao-cal-accel/ao-cal-accel $dev || exit 1
echo 'E 1' > $dev
;;
esac
-echo 'E 0' > $dev
+SERIAL=$SERIAL ./cal-freq $dev
./test-telegps
-SERIAL=$SERIAL ./cal-freq $dev
-
exit $?
ao-load ao-telem ao-send-telem ao-sky-flash \
ao-dumpflash ao-edit-telem ao-dump-up ao-elftohex \
ao-flash ao-usbload ao-test-igniter ao-test-baro \
- ao-test-flash ao-cal-accel ao-test-gps ao-usbtrng
+ ao-test-flash ao-cal-accel ao-test-gps ao-usbtrng \
+ ao-cal-freq
if LIBSTLINK
SUBDIRS += ao-stmload
endif
--- /dev/null
+ao-cal-freq
--- /dev/null
+bin_PROGRAMS=ao-cal-freq
+
+AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS)
+
+ao_cal_freq_DEPENDENCIES = $(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_cal_freq_LDADD=$(top_builddir)/ao-tools/lib/libao-tools.a $(LIBUSB_LIBS) -lm
+
+ao_cal_freq_SOURCES=ao-cal-freq.c
+
+man_MANS = ao-cal-freq.1
--- /dev/null
+.\"
+.\" Copyright © 2009 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-LOAD 1 "ao-cal-freq" ""
+.SH NAME
+ao-cal-freq \- Calibrate AltOS flight computer frequency
+.SH SYNOPSIS
+.B "ao-cal-freq"
+[\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
+.SH DESCRIPTION
+.I ao-cal-freq
+drives the frequency calibration process and saves the result.
+.SH OPTIONS
+.TP
+\-T tty-device | --tty tty-device
+This selects which tty device the debugger uses to communicate with
+the target device. The special name 'BITBANG' directs ao-dbg to use
+the cp2103 connection, otherwise this should be a usb serial port
+connected to a suitable cc1111 debug node.
+.TP
+\-D AltOS-device | --device AltOS-device
+Search for a connected device. This requires an argument of one of the
+following forms:
+.IP
+TeleMega:2
+.br
+TeleMega
+.br
+2
+.IP
+Leaving out the product name will cause the tool to select a suitable
+product, leaving out the serial number will cause the tool to match
+one of the available devices.
+.SH USAGE
+.I ao-cal-freq
+opens the target device, interactively calibrates the frequency, then
+shows the resulting calibration values and saves them to configuration
+memory.
+.SH AUTHOR
+Keith Packard
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <err.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdbool.h>
+#include <termios.h>
+#include <math.h>
+#include "ao-elf.h"
+#include "ccdbg.h"
+#include "cc-usb.h"
+#include "cc.h"
+#include "ao-verbose.h"
+
+static const struct option options[] = {
+ { .name = "tty", .has_arg = 1, .val = 'T' },
+ { .name = "device", .has_arg = 1, .val = 'D' },
+ { .name = "raw", .has_arg = 0, .val = 'r' },
+ { .name = "verbose", .has_arg = 1, .val = 'v' },
+ { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+ fprintf(stderr, "usage: %s [--verbose=<verbose>] [--device=<device>] [-tty=<tty>]\n", program);
+ exit(1);
+}
+
+void
+done(struct cc_usb *cc, int code)
+{
+ cc_usb_close(cc);
+ exit (code);
+}
+
+static int
+ends_with(char *whole, char *suffix)
+{
+ int whole_len = strlen(whole);
+ int suffix_len = strlen(suffix);
+
+ if (suffix_len > whole_len)
+ return 0;
+ return strcmp(whole + whole_len - suffix_len, suffix) == 0;
+}
+
+static int
+starts_with(char *whole, char *prefix)
+{
+ int whole_len = strlen(whole);
+ int prefix_len = strlen(prefix);
+
+ if (prefix_len > whole_len)
+ return 0;
+ return strncmp(whole, prefix, prefix_len) == 0;
+}
+
+static char **
+tok(char *line) {
+ char **strs = malloc (sizeof (char *)), *str;
+ int n = 0;
+
+ while ((str = strtok(line, " \t"))) {
+ line = NULL;
+ strs = realloc(strs, (n + 2) * sizeof (char *));
+ strs[n] = strdup(str);
+ n++;
+ }
+ strs[n] = '\0';
+ return strs;
+}
+
+static void
+free_strs(char **strs) {
+ char *str;
+ int i;
+
+ for (i = 0; (str = strs[i]) != NULL; i++)
+ free(str);
+ free(strs);
+}
+
+struct flash {
+ struct flash *next;
+ char line[512];
+ char **strs;
+};
+
+static struct flash *
+flash(struct cc_usb *usb)
+{
+ struct flash *head = NULL, **tail = &head;
+ cc_usb_printf(usb, "c s\nv\n");
+ for (;;) {
+ char line[512];
+ struct flash *b;
+
+ cc_usb_getline(usb, line, sizeof (line));
+ b = malloc (sizeof (struct flash));
+ strcpy(b->line, line);
+ b->strs = tok(line);
+ b->next = NULL;
+ *tail = b;
+ tail = &b->next;
+ if (strstr(line, "software-version"))
+ break;
+ }
+ return head;
+}
+
+static void
+free_flash(struct flash *b) {
+ struct flash *n;
+
+ while (b) {
+ n = b->next;
+ free_strs(b->strs);
+ free(b);
+ b = n;
+ }
+}
+
+char **
+find_flash(struct flash *b, char *word0) {
+ int i;
+ for (;b; b = b->next) {
+ if (strstr(b->line, word0))
+ return b->strs;
+ }
+ return NULL;
+}
+
+void
+await_key(void)
+{
+ struct termios termios, termios_save;
+ char buf[512];
+
+ tcgetattr(0, &termios);
+ termios_save = termios;
+ cfmakeraw(&termios);
+ tcsetattr(0, TCSAFLUSH, &termios);
+ read(0, buf, sizeof (buf));
+ tcsetattr(0, TCSAFLUSH, &termios_save);
+}
+
+int
+do_cal(struct cc_usb *usb) {
+ struct flash *b;
+ char line[1024];
+ double measured_freq;
+ char **cur_freq_words;
+ char **cur_cal_words;
+ char *line_end;
+ int cur_freq;
+ int cur_cal;
+ int new_cal;
+
+ cc_usb_printf(usb, "E 0\n");
+
+ for(;;) {
+ cc_usb_printf(usb, "C 1\n");
+ cc_usb_sync(usb);
+
+ printf("Generating RF carrier. Please enter measured frequency [enter for done]: ");
+ fflush(stdout);
+ fgets(line, sizeof (line) - 1, stdin);
+ cc_usb_printf(usb, "C 0\n");
+ cc_usb_sync(usb);
+
+ measured_freq = strtod(line, &line_end);
+ if (line_end == line)
+ break;
+
+ b = flash(usb);
+
+ cur_cal_words = find_flash(b, "Radio cal:");
+ cur_freq_words = find_flash(b, "Frequency:");
+
+ if (!cur_cal_words || !cur_freq_words) {
+ printf("no response\n");
+ return 0;
+ }
+
+ cur_cal = atoi(cur_cal_words[2]);
+ cur_freq = atoi(cur_freq_words[1]);
+
+ printf ("Current radio calibration %d\n", cur_cal);
+ printf ("Current radio frequency: %d\n", cur_freq);
+
+
+ new_cal = floor ((((double) cur_freq / 1000.0) / measured_freq) * cur_cal + 0.5);
+
+ printf ("Programming flash with cal value %d\n", new_cal);
+
+ cc_usb_printf (usb, "c f %d\nc w\n", new_cal);
+ cc_usb_sync(usb);
+ }
+ return 1;
+}
+
+int
+main (int argc, char **argv)
+{
+ char *device = NULL;
+ char *filename;
+ Elf *e;
+ unsigned int s;
+ int i;
+ int c;
+ int tries;
+ struct cc_usb *cc = NULL;
+ char *tty = NULL;
+ int success;
+ int verbose = 0;
+ int ret = 0;
+ int expected_size;
+
+ while ((c = getopt_long(argc, argv, "rT:D:c:s:v:", options, NULL)) != -1) {
+ switch (c) {
+ case 'T':
+ tty = optarg;
+ break;
+ case 'D':
+ device = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ ao_verbose = verbose;
+
+ if (verbose > 1)
+ ccdbg_add_debug(CC_DEBUG_BITBANG);
+
+ if (!tty)
+ tty = cc_usbdevs_find_by_arg(device, "AltosFlash");
+ if (!tty)
+ tty = cc_usbdevs_find_by_arg(device, "TeleMega");
+ if (!tty)
+ tty = getenv("ALTOS_TTY");
+ if (!tty)
+ tty="/dev/ttyACM0";
+
+ cc = cc_usb_open(tty);
+
+ if (!cc)
+ exit(1);
+
+ if (!do_cal(cc))
+ ret = 1;
+ done(cc, ret);
+}
static const struct option options[] = {
{ .name = "tty", .has_arg = 1, .val = 'T' },
{ .name = "device", .has_arg = 1, .val = 'D' },
+ { .name = "wait", .has_arg = 0, .val = 'w' },
{ 0, 0, 0, 0},
};
static void usage(char *program)
{
- fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>]\n", program);
+ fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>] [--wait]\n", program);
exit(1);
}
static int find_header(struct cc_usb *cc)
{
for (;;) {
- if (get_nonwhite(cc, 0) == 'M' && get_nonwhite(cc, 1000) == 'P')
+ if (get_nonwhite(cc, -1) == 'M' && get_nonwhite(cc, 1000) == 'P')
return 1;
}
}
int i;
int crc;
int current_crc;
+ int wait = 0;
- while ((c = getopt_long(argc, argv, "T:D:", options, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "wT:D:", options, NULL)) != -1) {
switch (c) {
+ case 'w':
+ wait = 1;
+ break;
case 'T':
tty = optarg;
break;
break;
}
}
- if (!tty)
- tty = cc_usbdevs_find_by_arg(device, "FT230X Basic UART");
+ if (!tty) {
+ for (;;) {
+ tty = cc_usbdevs_find_by_arg(device, "FT230X Basic UART");
+ if (tty) {
+ if (wait) {
+ printf("tty is %s\n", tty);
+ sleep(1);
+ }
+ break;
+ }
+ if (!wait)
+ break;
+ sleep(1);
+ }
+ }
if (!tty)
tty = getenv("ALTOS_TTY");
if (!tty)
-bin_SCRIPTS=ao-flash-stm ao-flash-lpc
+bin_SCRIPTS=ao-flash-stm ao-flash-lpc ao-flash-stm32f0x
-man_MANS = ao-flash-stm.1 ao-flash-lpc.1
\ No newline at end of file
+man_MANS = ao-flash-stm.1 ao-flash-lpc.1 ao-flash-stm32f0x.1
--- /dev/null
+#!/bin/sh
+case "$#" in
+0)
+ echo "usage: $0 <filename> ..."
+ exit 1
+ ;;
+esac
+cmds=/tmp/flash$$
+trap "rm $cmds" 0 1 15
+file="$1"
+echo "program $file verify reset" > $cmds
+openocd \
+ -f interface/stlink-v2.cfg \
+ -f target/stm32f0x_stlink.cfg \
+ -f $cmds \
+ -c shutdown
--- /dev/null
+.\"
+.\" Copyright © 2013 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-FLASH-LPC 1 "ao-flash-stm32f0x" ""
+.SH NAME
+ao-flash-stm32f0x \- flash a program to a STM32F0x-based AltOS device using openocd
+.SH SYNOPSIS
+.B "ao-flash-stm32f0x"
+\fIfile.elf\fP
+.SH DESCRIPTION
+.I ao-flash-stm32f0x
+loads the specified .elf file into the target device flash memory.
+.SH USAGE
+.I ao-flash-stm32f0x
+is a simple script that passes the correct arguments to openocd to
+load a file into the target device via a connected STlink
+debugging dongle.
+.SH "SEE ALSO"
+openocd(1)
+.SH AUTHOR
+Keith Packard
struct cc_usbdev *dev;
int i;
- devs = cc_usbdevs_scan();
+ devs = cc_usbdevs_scan(TRUE);
if (devs) {
for (i = 0; i < devs->ndev; i++) {
dev = devs->dev[i];
printf ("%-20.20s %6d %s\n",
- dev->product, dev->serial, dev->tty);
+ dev->product, dev->serial, dev->tty ? dev->tty : "(none)");
}
cc_usbdevs_free(devs);
}
if (cc_mega_parse(line, &log)) {
if (log.is_config) {
- printf ("kind %d\n", log.u.config_int.kind);
+ printf ("config %2d %s", log.u.config_int.kind, line);
} else {
printf ("tick %5d ", log.tick);
switch (log.type) {
printf (" s%d %6d",
j, log.u.volt.sense[j]);
}
- printf ("pyro %04x\n", log.u.volt.pyro);
- printf ("\n");
+ printf (" pyro %04x\n", log.u.volt.pyro);
break;
default:
printf ("type %c\n", log.type, log.tick);
write(2, cc->in_buf, cc->in_count);
cc->in_count = 0;
}
- } else if (ret < 0)
+ } else if (ret <= 0) {
perror("read");
+ return -1;
+ }
}
if (fds.revents & POLLOUT) {
ret = write(cc->fd, cc->out_buf,
}
struct cc_usbdevs *
-cc_usbdevs_scan(void)
+cc_usbdevs_scan(int non_tty)
{
int e;
struct dirent **ents;
dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
dev = usb_scan_device(dir);
free(dir);
- if (is_am(dev->idVendor, dev->idProduct) && dev->tty) {
+ if (is_am(dev->idVendor, dev->idProduct) && (non_tty || dev->tty)) {
if (devs->dev)
devs->dev = realloc(devs->dev,
(devs->ndev + 1) * sizeof (struct usbdev *));
int i;
char *tty = NULL;
- devs = cc_usbdevs_scan();
+ devs = cc_usbdevs_scan(FALSE);
if (!devs)
return NULL;
for (i = 0; i < devs->ndev; i++) {
cc_usbdevs_free(struct cc_usbdevs *usbdevs);
struct cc_usbdevs *
-cc_usbdevs_scan(void);
+cc_usbdevs_scan(int non_tty);
char *
cc_usbdevs_find_by_arg(char *arg, char *default_product);
dnl Process this file with autoconf to create configure.
AC_PREREQ(2.57)
-AC_INIT([altos], 1.6)
+AC_INIT([altos], 1.6.1)
+ANDROID_VERSION=9
AC_CONFIG_SRCDIR([src/kernel/ao.h])
AM_INIT_AUTOMAKE([foreign dist-bzip2])
AM_MAINTAINER_MODE
VERSION_DASH=`echo $VERSION | sed 's/\./-/g'`
AC_SUBST(VERSION_DASH)
+AC_SUBST(ANDROID_VERSION)
dnl ==========================================================================
dnl Java library versions
-ALTOSUILIB_VERSION=6
-ALTOSLIB_VERSION=6
+ALTOSUILIB_VERSION=7
+ALTOSLIB_VERSION=7
AC_SUBST(ALTOSLIB_VERSION)
AC_DEFINE(ALTOSLIB_VERSION,$ALTOSLIB_VERSION,[Version of the AltosLib package])
Makefile
src/Makedefs
altoslib/Makefile
+altoslib/AltosVersion.java
icon/Makefile
altosuilib/Makefile
-altosuilib/AltosUIVersion.java
altosui/Makefile
altosui/Info.plist
altosui/altos-windows.nsi
telegps/telegps-windows.nsi
altosdroid/Makefile
altosdroid/local.properties
+altosdroid/AndroidManifest.xml
ao-tools/Makefile
ao-tools/lib/Makefile
ao-tools/ao-rawload/Makefile
ao-tools/ao-test-baro/Makefile
ao-tools/ao-test-flash/Makefile
ao-tools/ao-cal-accel/Makefile
+ao-tools/ao-cal-freq/Makefile
ao-tools/ao-test-gps/Makefile
ao-tools/ao-usbtrng/Makefile
ao-utils/Makefile
release-notes-1.4.html \
release-notes-1.4.1.html \
release-notes-1.5.html \
- release-notes-1.6.html
+ release-notes-1.6.html \
+ release-notes-1.6.1.html
PICTURES=\
altosui.png \
micropeak-statistics.png \
MicroPeakUSB-2.0-inuse.jpg \
MicroPeakUSB-2.0.jpg \
+ monitor-idle.png \
scan-channels.png \
site-map.png \
table.png \
</para>
</legalnotice>
<revhistory>
+ <revision>
+ <revnumber>1.6.1</revnumber>
+ <date>15 July 2015</date>
+ <revremark>
+ Minor release adding TeleBT v3.0 support.
+ </revremark>
+ </revision>
<revision>
<revnumber>1.6</revnumber>
<date>8 January 2015</date>
<listitem>
<para>
After Motor. The flight software counts each time the
- rocket starts accelerating (presumably due to a motor or
- motors igniting). Use this value to count ignitions for
- multi-staged or multi-airstart launches.
+ rocket starts accelerating and then decelerating
+ (presumably due to a motor or motors burning). Use this
+ value for multi-staged or multi-airstart launches.
</para>
</listitem>
<listitem>
<para>
Before heading out to a new launch site, you can use this to
load satellite images in case you don't have internet
- connectivity at the site. This loads a fairly large area
- around the launch site, which should cover any flight you're likely to make.
+ connectivity at the site.
</para>
<para>
There's a drop-down menu of launch sites we know about; if
You can specify the range of zoom levels to download; smaller
numbers show more area with less resolution. The default
level, 0, shows about 3m/pixel. One zoom level change
- doubles or halves that number.
+ doubles or halves that number. Larger zoom levels show more
+ detail, smaller zoom levels less.
</para>
<para>
- The Tile Radius value sets how large an area around the center
- point to download. Each tile is 512x512 pixels, and the
- 'radius' value specifies how many tiles away from the center
- will be downloaded. Specify a radius of 0 and you get only the
- center tile. A radius of 1 loads a 3x3 grid, centered on the
- specified location.
+ The Map Radius value sets how large an area around the center
+ point to download. Select a value large enough to cover any
+ plausible flight from that site. Be aware that loading a large
+ area with a high maximum zoom level can attempt to download a
+ lot of data. Loading hybrid maps with a 10km radius at a
+ minimum zoom of -2 and a maximum zoom of 2 consumes about
+ 120MB of space. Terrain and road maps consume about 1/10 as
+ much space as satellite or hybrid maps.
</para>
<para>
Clicking the 'Load Map' button will fetch images from Google
</section>
<section>
<title>Monitor Idle</title>
+ <informalfigure>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="monitor-idle.png" width="5.2in" scalefit="1"/>
+ </imageobject>
+ </mediaobject>
+ </informalfigure>
<para>
This brings up a dialog similar to the Monitor Flight UI,
except it works with the altimeter in “idle” mode by sending
cannot manage to run Monitor Idle, then it's very likely that
your callsigns are different in some way.
</para>
+ <para>
+ You can change the frequency and callsign used to communicate
+ with the flight computer; they must both match the
+ configuration in the flight computer exactly.
+ </para>
</section>
</chapter>
<chapter>
<title>AltosDroid</title>
<para>
AltosDroid provides the same flight monitoring capabilities as
- AltosUI, but runs on Android devices and is designed to connect
- to a TeleBT receiver over Bluetooth™. AltosDroid monitors
+ AltosUI, but runs on Android devices. AltosDroid is designed to connect
+ to a TeleBT receiver over Bluetooth™ and (on Android devices supporting
+ USB On-the-go) TeleDongle and TeleBT devices over USB. AltosDroid monitors
telemetry data, logging it to internal storage in the Android
- device, and presents that data in a UI the same way the 'Monitor
- Flight' window does in AltosUI.
+ device, and presents that data in a UI similar to the 'Monitor
+ Flight' window in AltosUI.
</para>
<para>
- This manual will explain how to configure AltosDroid, connect
- to TeleBT, operate the flight monitoring interface and describe
- what the displayed data means.
+ This manual will explain how to configure AltosDroid, connect to
+ TeleBT or TeleDongle, operate the flight monitoring interface
+ and describe what the displayed data means.
</para>
<section>
<title>Installing AltosDroid</title>
</para>
</section>
<section>
- <title>Connecting to TeleBT</title>
+ <title>Connecting to TeleBT over Bluetooth™</title>
<para>
Press the Android 'Menu' button or soft-key to see the
configuration options available. Select the 'Connect a device'
scanning.
</para>
</section>
+ <section>
+ <title>Connecting to TeleDongle or TeleBT over USB</title>
+ <para>
+ Get a special USB On-the-go adapter cable. These cables have a USB
+ micro-B male connector on one end and a standard A female
+ connector on the other end. Plug in your TeleDongle or TeleBT
+ device to the adapter cable and the adapter cable into your
+ phone and AltosDroid should automatically start up. If it
+ doesn't, the most likely reason is that your Android device
+ doesn't support USB On-the-go.
+ </para>
+ </section>
<section>
<title>Configuring AltosDroid</title>
<para>
- The only configuration option available for AltosDroid is
- which frequency to listen on. Press the Android 'Menu' button
- or soft-key and pick the 'Select radio frequency' entry. That
- brings up a menu of pre-set radio frequencies; pick the one
- which matches your altimeter.
+ There are several configuration and operation parameters
+ available in the AltosDroid menu.
</para>
+ <section>
+ <title>Select radio frequency</title>
+ <para>
+ This selects which frequency to listen on by bringing up a
+ menu of pre-set radio frequencies. Pick the one which matches
+ your altimeter.
+ </para>
+ </section>
+ <section>
+ <title>Select data rate</title>
+ <para>
+ Altus Metrum transmitters can be configured to operate at
+ lower data rates to improve transmission range. If you have
+ configured your device to do this, this menu item allows you
+ to change the receiver to match.
+ </para>
+ </section>
+ <section>
+ <title>Change units</title>
+ <para>
+ This toggles between metric and imperial units.
+ </para>
+ </section>
+ <section>
+ <title>Load maps</title>
+ <para>
+ Brings up a dialog allowing you to download offline map
+ tiles so that you can have maps available even if you have
+ no network connectivity at the launch site.
+ </para>
+ </section>
+ <section>
+ <title>Map type</title>
+ <para>
+ Displays a menu of map types and lets you select one. Hybrid
+ maps include satellite images with a roadmap
+ overlaid. Satellite maps dispense with the roadmap
+ overlay. Roadmap shows just the roads. Terrain includes
+ roads along with shadows indicating changes in elevation,
+ and other geographical features.
+ </para>
+ </section>
+ <section>
+ <title>Toggle Online/Offline maps</title>
+ <para>
+ Switches between online and offline maps. Online maps will
+ show a 'move to current position' icon in the upper right
+ corner, while offline maps will have copyright information
+ all over the map. Otherwise, they're pretty similar.
+ </para>
+ </section>
+ <section>
+ <title>Select Tracker</title>
+ <para>
+ Switches the information displays to show data for a
+ different transmitting device. The map will always show all
+ of the devices in view. Trackers are shown and selected by
+ serial number, so make sure you note the serial number of
+ devices in each airframe.
+ </para>
+ </section>
+ <section>
+ <title>Delete Track</title>
+ <para>
+ Deletes all information about a transmitting device.
+ </para>
+ </section>
</section>
<section>
<title>AltosDroid Flight Monitoring</title>
<section>
<title>Pad</title>
<para>
- The 'Launch Pad' tab shows information used to decide when the
+ The 'Pad' tab shows information used to decide when the
rocket is ready for flight. The first elements include red/green
indicators, if any of these is red, you'll want to evaluate
- whether the rocket is ready to launch:
- <variablelist>
- <varlistentry>
- <term>Battery Voltage</term>
- <listitem>
- <para>
- This indicates whether the Li-Po battery
- powering the TeleMetrum has sufficient charge to last for
- the duration of the flight. A value of more than
- 3.8V is required for a 'GO' status.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>Apogee Igniter Voltage</term>
- <listitem>
- <para>
- This indicates whether the apogee
- igniter has continuity. If the igniter has a low
- resistance, then the voltage measured here will be close
- to the Li-Po battery voltage. A value greater than 3.2V is
- required for a 'GO' status.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>Main Igniter Voltage</term>
- <listitem>
- <para>
- This indicates whether the main
- igniter has continuity. If the igniter has a low
- resistance, then the voltage measured here will be close
- to the Li-Po battery voltage. A value greater than 3.2V is
- required for a 'GO' status.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>On-board Data Logging</term>
- <listitem>
- <para>
- This indicates whether there is
- space remaining on-board to store flight data for the
- upcoming flight. If you've downloaded data, but failed
- to erase flights, there may not be any space
- left. TeleMetrum can store multiple flights, depending
- on the configured maximum flight log size. TeleMini
- stores only a single flight, so it will need to be
- downloaded and erased after each flight to capture
- data. This only affects on-board flight logging; the
- altimeter will still transmit telemetry and fire
- ejection charges at the proper times.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>GPS Locked</term>
- <listitem>
- <para>
- For a TeleMetrum or TeleMega device, this indicates whether the GPS receiver is
- currently able to compute position information. GPS requires
- at least 4 satellites to compute an accurate position.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>GPS Ready</term>
- <listitem>
- <para>
- For a TeleMetrum or TeleMega device, this indicates whether GPS has reported at least
- 10 consecutive positions without losing lock. This ensures
- that the GPS receiver has reliable reception from the
- satellites.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
+ whether the rocket is ready to launch.
</para>
<para>
- The Launchpad tab also shows the computed launch pad position
- and altitude, averaging many reported positions to improve the
- accuracy of the fix.
+ When the pad tab is selected, the voice responses will
+ include status changes to the igniters and GPS reception,
+ letting you know if the rocket is still ready for launch.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>Battery</term>
+ <listitem>
+ <para>
+ This indicates whether the Li-Po battery
+ powering the transmitter has sufficient charge to last for
+ the duration of the flight. A value of more than
+ 3.8V is required for a 'GO' status.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Receiver Battery</term>
+ <listitem>
+ <para>
+ This indicates whether the Li-Po battery
+ powering the TeleBT has sufficient charge to last for
+ the duration of the flight. A value of more than
+ 3.8V is required for a 'GO' status.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Data Logging</term>
+ <listitem>
+ <para>
+ This indicates whether there is space remaining
+ on-board to store flight data for the upcoming
+ flight. If you've downloaded data, but failed to
+ erase flights, there may not be any space
+ left. TeleMetrum and TeleMega can store multiple
+ flights, depending on the configured maximum flight
+ log size. TeleGPS logs data continuously. TeleMini
+ stores only a single flight, so it will need to be
+ downloaded and erased after each flight to capture
+ data. This only affects on-board flight logging; the
+ altimeter will still transmit telemetry and fire
+ ejection charges at the proper times.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>GPS Locked</term>
+ <listitem>
+ <para>
+ For a TeleMetrum or TeleMega device, this indicates whether the GPS receiver is
+ currently able to compute position information. GPS requires
+ at least 4 satellites to compute an accurate position.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>GPS Ready</term>
+ <listitem>
+ <para>
+ For a TeleMetrum or TeleMega device, this indicates whether GPS has reported at least
+ 10 consecutive positions without losing lock. This ensures
+ that the GPS receiver has reliable reception from the
+ satellites.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Apogee Igniter</term>
+ <listitem>
+ <para>
+ This indicates whether the apogee
+ igniter has continuity. If the igniter has a low
+ resistance, then the voltage measured here will be close
+ to the Li-Po battery voltage. A value greater than 3.2V is
+ required for a 'GO' status.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Main Igniter</term>
+ <listitem>
+ <para>
+ This indicates whether the main
+ igniter has continuity. If the igniter has a low
+ resistance, then the voltage measured here will be close
+ to the Li-Po battery voltage. A value greater than 3.2V is
+ required for a 'GO' status.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Igniter A-D</term>
+ <listitem>
+ <para>
+ This indicates whether the indicated additional pyro
+ channel igniter has continuity. If the igniter has a
+ low resistance, then the voltage measured here will
+ be close to the Li-Po battery voltage. A value
+ greater than 3.2V is required for a 'GO' status.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ <para>
+ The Pad tab also shows the location of the Android device.
+ </para>
+ </section>
+ <section>
+ <title>Flight</title>
+ <para>
+ The 'Flight' tab shows information used to evaluate and spot
+ a rocket while in flight. It displays speed and height data
+ to monitor the health of the rocket, along with elevation,
+ range and bearing to help locate the rocket in the sky.
+ </para>
+ <para>
+ While the Flight tab is displayed, the voice announcements
+ will include current speed, height, elevation and bearing
+ information.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>Speed</term>
+ <listitem>
+ <para>
+ Shows current vertical speed. During descent, the
+ speed values are averaged over a fairly long time to
+ try and make them steadier.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Height</term>
+ <listitem>
+ <para>
+ Shows the current height above the launch pad.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Max Speed</term>
+ <listitem>
+ <para>
+ Shows the maximum vertical speed seen during the flight.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Max Height</term>
+ <listitem>
+ <para>
+ Shows the maximum height above launch pad.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Elevation</term>
+ <listitem>
+ <para>
+ This is the angle above the horizon from the android
+ devices current position.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Range</term>
+ <listitem>
+ <para>
+ The total distance from the android device to the
+ rocket, including both ground distance and
+ difference in altitude. Use this to gauge how large
+ the rocket is likely to appear in the sky.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Bearing</term>
+ <listitem>
+ <para>
+ This is the aziumuth from true north for the rocket
+ from the android device. Use this in combination
+ with the Elevation value to help locate the rocket
+ in the sky, or at least to help point the antenna in
+ the general direction. This is provided in both
+ degrees and a compass point (like West South
+ West). You'll want to know which direction is true
+ north before launching your rocket.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Ground Distance</term>
+ <listitem>
+ <para>
+ This shows the distance across the ground to the
+ lat/lon where the rocket is located. Use this to
+ estimate what is currently under the rocket.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Latitude/Longitude</term>
+ <listitem>
+ <para>
+ Displays the last known location of the rocket.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Apogee Igniter</term>
+ <listitem>
+ <para>
+ This indicates whether the apogee
+ igniter has continuity. If the igniter has a low
+ resistance, then the voltage measured here will be close
+ to the Li-Po battery voltage. A value greater than 3.2V is
+ required for a 'GO' status.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Main Igniter</term>
+ <listitem>
+ <para>
+ This indicates whether the main
+ igniter has continuity. If the igniter has a low
+ resistance, then the voltage measured here will be close
+ to the Li-Po battery voltage. A value greater than 3.2V is
+ required for a 'GO' status.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </section>
+ <section>
+ <title>Recover</title>
+ <para>
+ The 'Recover' tab shows information used while recovering the
+ rocket on the ground after flight.
+ </para>
+ <para>
+ While the Recover tab is displayed, the voice announcements
+ will include distance along with either bearing or
+ direction, depending on whether you are moving.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>Bearing</term>
+ <listitem>
+ <para>
+ This is the aziumuth from true north for the rocket
+ from the android device. Use this in combination
+ with the Elevation value to help locate the rocket
+ in the sky, or at least to help point the antenna in
+ the general direction. This is provided in both
+ degrees and a compass point (like West South
+ West). You'll want to know which direction is true
+ north before launching your rocket.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Direction</term>
+ <listitem>
+ <para>
+ When you are in motion, this provides the angle from
+ your current direction of motion towards the rocket.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Distance</term>
+ <listitem>
+ <para>
+ Distance over the ground to the rocket.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Tar Lat/Tar Lon</term>
+ <listitem>
+ <para>
+ Displays the last known location of the rocket.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>My Lat/My Lon</term>
+ <listitem>
+ <para>
+ Displays the location of the Android device.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Max Height</term>
+ <listitem>
+ <para>
+ Shows the maximum height above launch pad.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Max Speed</term>
+ <listitem>
+ <para>
+ Shows the maximum vertical speed seen during the flight.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Max Accel</term>
+ <listitem>
+ <para>
+ Shows the maximum vertical acceleration seen during the flight.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </section>
+ <section>
+ <title>Map</title>
+ <para>
+ The 'Map' tab shows a map of the area around the rocket
+ being tracked along with information needed to recover it.
+ </para>
+ <para>
+ On the map itself, icons showing the location of the android
+ device along with the last known location of each tracker. A
+ blue line is drawn from the android device location to the
+ currently selected tracker.
+ </para>
+ <para>
+ Below the map, the distance and either bearing or direction
+ along with the lat/lon of the target and the android device
+ are shown
+ </para>
+ <para>
+ The Map tab provides the same voice announcements as the
+ Recover tab.
</para>
</section>
</section>
<para>
AltosDroid always saves every bit of telemetry data it
receives. To download that to a computer for use with AltosUI,
- simply remove the SD card from your Android device, or connect
- your device to your computer's USB port and browse the files
- on that device. You will find '.telem' files in the TeleMetrum
+ remove the SD card from your Android device, or connect your
+ device to your computer's USB port and browse the files on
+ that device. You will find '.telem' files in the TeleMetrum
directory that will work with AltosUI directly.
</para>
</section>
</appendix>
<appendix>
<title>Release Notes</title>
+ <simplesect>
+ <title>Version 1.6.1</title>
+ <xi:include
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="release-notes-1.6.1.xsl"
+ xpointer="xpointer(/article/*)"/>
+ </simplesect>
<simplesect>
<title>Version 1.6</title>
<xi:include
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+"/usr/share/xml/docbook/schema/dtd/4.5/docbookx.dtd">
+
+<article>
+ <para>
+ Version 1.6.1 includes support for our updated TeleBT v3.0
+ product and bug fixes in in the flight software for all our boards
+ and ground station interfaces.
+ </para>
+ <para>
+ AltOS New Features
+ <itemizedlist>
+ <listitem>
+ <para>
+ Add support for TeleBT v3.0 boards.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Add support for uncompressed APRS data, providing support
+ for older APRS receivers. Uncompressed APRS data is less
+ precise, takes more bandwidth and doesn't have integrated
+ altitude data.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ AltOS Fixes
+ <itemizedlist>
+ <listitem>
+ <para>
+ Make TeleDongle and TeleBT more tolerant of data rate
+ variations from transmitting devices.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ AltosUI and TeleGPS New Features
+ <itemizedlist>
+ <listitem>
+ <para>
+ Add map to Monitor Idle display. It's nice to be able to
+ verify that maps are working, instead of needing to use
+ Monitor Flight.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ AltosUI Fixes
+ <itemizedlist>
+ <listitem>
+ <para>
+ Fix frequency configuration to round values instead of
+ truncate them, avoiding a common 1kHz error in the setting.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Turn the Windows stub into a more useful program that can
+ launch the application with parameters so that file manager
+ icons work more reliably.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Force KML export to use a C locale so that numbers are
+ formatted with '.' instead of ',' for a decimal separator in
+ non-US locales.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Preload map tiles based on distance rather than number of
+ tiles; this means you get the same resolution covering the
+ entire area, rather than having high resolution near the
+ center and low resolution further away.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Allow configuration of frequency and callsign in Monitor
+ Idle mode.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Fix layout weirdness when resizing windows on
+ Windows. Windows shouldn't have giant blank spaces around
+ the useful content anymore.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Fix layout weirdness when resizing windows on
+ Windows. Windows shouldn't have giant blank spaces around
+ the useful content anymore.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Use a longer filter for descent speed values. This should
+ provide something more useful on the display, although it
+ will take longer to respond to changes now.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Make Replay Flight run in realtime again. It had been set to
+ run at 10x speed by mistake.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ AltosDroid New Features
+ <itemizedlist>
+ <listitem>
+ <para>
+ Add offline map support using mapping code from AltosUI.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Support TeleDongle (and TeleBT via USB) on devices
+ supporting USB On-The-Go.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Display additional TeleMega pyro channel status in Pad tab.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Switch between metric and imperial units.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Monitor TeleBT battery voltage.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Track multiple devices at the same time, selecting between
+ them with a menu or using the map.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Add hybrid, satellite and terrain map types.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ AltosDroid Fixes
+ <itemizedlist>
+ <listitem>
+ <para>
+ Use standard Android display conventions so that a menu
+ button is available in the application title bar.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Adjust layout to work on large and small screens; shrinking
+ the go/no-go lights in smaller environments to try and make
+ everything visible.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Make voice announcements depend on current tab.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Compute adjustment to current travel direction while in
+ motion towards rocket.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+</article>
</para>
</section>
<section>
- <title>Sensor Data</title>
+ <title>TeleMetrum v1.x, TeleMini and TeleNano Sensor Data</title>
<informaltable frame='none' label='' tocentry='0'>
<tgroup cols='2' align='center' colsep='1' rowsep='1'>
<colspec align='center' colwidth='*' colname='Offset'/>
<tbody>
<row>
<entry>0x01</entry>
- <entry>TeleMetrum Sensor Data</entry>
+ <entry>TeleMetrum v1.x Sensor Data</entry>
</row>
<row>
<entry>0x02</entry>
</tgroup>
</informaltable>
<para>
- TeleMetrum, TeleMini and TeleNano share this same packet
+ TeleMetrum v1.x, TeleMini and TeleNano share this same packet
format for sensor data. Each uses a distinct packet type so
that the receiver knows which data values are valid and which
are undefined.
</tgroup>
</table>
</section>
+ <section>
+ <title>TeleMega Sensor Data</title>
+ <informaltable frame='none' label='' tocentry='0'>
+ <tgroup cols='2' align='center' colsep='1' rowsep='1'>
+ <colspec align='center' colwidth='*' colname='Offset'/>
+ <colspec align='left' colwidth='3*' colname='Description'/>
+ <thead>
+ <row>
+ <entry>Type</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>0x08</entry>
+ <entry>TeleMega IMU Sensor Data</entry>
+ </row>
+ <row>
+ <entry>0x09</entry>
+ <entry>TeleMega Kalman and Voltage Data</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ <para>
+ TeleMega has a lot of sensors, and so it splits the sensor
+ data into two packets. The raw IMU data are sent more often;
+ the voltage values don't change very fast, and the Kalman
+ values can be reconstructed from the IMU data.
+ </para>
+ <para>
+ IMU Sensor Data packets are transmitted once per second on the
+ ground, 10 times per second during ascent and once per second
+ during descent and landing
+ </para>
+ <para>
+ Kalman and Voltage Data packets are transmitted once per second on the
+ ground, 5 times per second during ascent and once per second
+ during descent and landing
+ </para>
+ <para>
+ The high-g accelerometer is reported separately from the data
+ for the 9-axis IMU (accel/gyro/mag). The 9-axis IMU is mounted
+ so that the X axis is "across" the board (along the short
+ axis0, the Y axis is "along" the board (along the long axis,
+ with the high-g accelerometer) and the Z axis is "through" the
+ board (perpendicular to the board). Rotation measurements are
+ around the respective axis, so Y rotation measures the spin
+ rate of the rocket while X and Z rotation measure the tilt
+ rate.
+ </para>
+ <para>
+ The overall tilt angle of the rocket is computed by first
+ measuring the orientation of the rocket on the pad using the 3
+ axis accelerometer, and then integrating the overall tilt rate
+ from the 3 axis gyroscope to compute the total orientation
+ change of the airframe since liftoff.
+ </para>
+ <table frame='all'>
+ <title>TeleMega IMU Sensor Packet Contents</title>
+ <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+ <colspec align='center' colwidth='*' colname='Offset'/>
+ <colspec align='center' colwidth='3*' colname='Data Type'/>
+ <colspec align='left' colwidth='3*' colname='Name'/>
+ <colspec align='left' colwidth='9*' colname='Description'/>
+ <thead>
+ <row>
+ <entry align='center'>Offset</entry>
+ <entry align='center'>Data Type</entry>
+ <entry align='center'>Name</entry>
+ <entry align='center'>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>5</entry><entry>uint8_t</entry><entry>orient</entry><entry>Angle from vertical in degrees</entry>
+ </row>
+ <row>
+ <entry>6</entry><entry>int16_t</entry><entry>accel</entry><entry>High G accelerometer</entry>
+ </row>
+ <row>
+ <entry>8</entry><entry>int32_t</entry><entry>pres</entry><entry>pressure (Pa * 10)</entry>
+ </row>
+ <row>
+ <entry>12</entry><entry>int16_t</entry><entry>temp</entry><entry>temperature (°C * 100)</entry>
+ </row>
+ <row>
+ <entry>14</entry><entry>int16_t</entry><entry>accel_x</entry><entry>X axis acceleration (across)</entry>
+ </row>
+ <row>
+ <entry>16</entry><entry>int16_t</entry><entry>accel_y</entry><entry>Y axis acceleration (along)</entry>
+ </row>
+ <row>
+ <entry>18</entry><entry>int16_t</entry><entry>accel_z</entry><entry>Z axis acceleration (through)</entry>
+ </row>
+ <row>
+ <entry>20</entry><entry>int16_t</entry><entry>gyro_x</entry><entry>X axis rotation (across)</entry>
+ </row>
+ <row>
+ <entry>22</entry><entry>int16_t</entry><entry>gyro_y</entry><entry>Y axis rotation (along)</entry>
+ </row>
+ <row>
+ <entry>24</entry><entry>int16_t</entry><entry>gyro_z</entry><entry>Z axis rotation (through)</entry>
+ </row>
+ <row>
+ <entry>26</entry><entry>int16_t</entry><entry>mag_x</entry><entry>X field strength (across)</entry>
+ </row>
+ <row>
+ <entry>28</entry><entry>int16_t</entry><entry>mag_y</entry><entry>Y field strength (along)</entry>
+ </row>
+ <row>
+ <entry>30</entry><entry>int16_t</entry><entry>mag_z</entry><entry>Z field strength (through)</entry>
+ </row>
+ <row>
+ <entry>32</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <table frame='all'>
+ <title>TeleMega Kalman and Voltage Data Packet Contents</title>
+ <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+ <colspec align='center' colwidth='*' colname='Offset'/>
+ <colspec align='center' colwidth='3*' colname='Data Type'/>
+ <colspec align='left' colwidth='3*' colname='Name'/>
+ <colspec align='left' colwidth='9*' colname='Description'/>
+ <thead>
+ <row>
+ <entry align='center'>Offset</entry>
+ <entry align='center'>Data Type</entry>
+ <entry align='center'>Name</entry>
+ <entry align='center'>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>5</entry><entry>uint8_t</entry><entry>state</entry><entry>Flight state</entry>
+ </row>
+ <row>
+ <entry>6</entry><entry>int16_t</entry><entry>v_batt</entry><entry>battery voltage</entry>
+ </row>
+ <row>
+ <entry>8</entry><entry>int16_t</entry><entry>v_pyro</entry><entry>pyro battery voltage</entry>
+ </row>
+ <row>
+ <entry>10</entry><entry>int8_t[6]</entry><entry>sense</entry><entry>pyro continuity sense</entry>
+ </row>
+ <row>
+ <entry>16</entry><entry>int32_t</entry><entry>ground_pres</entry><entry>Average barometer reading on ground</entry>
+ </row>
+ <row>
+ <entry>20</entry><entry>int16_t</entry><entry>ground_accel</entry><entry>Average accelerometer reading on ground</entry>
+ </row>
+ <row>
+ <entry>22</entry><entry>int16_t</entry><entry>accel_plus_g</entry><entry>Accel calibration at +1g</entry>
+ </row>
+ <row>
+ <entry>24</entry><entry>int16_t</entry><entry>accel_minus_g</entry><entry>Accel calibration at -1g</entry>
+ </row>
+ <row>
+ <entry>26</entry><entry>int16_t</entry><entry>acceleration</entry><entry>m/s² * 16</entry>
+ </row>
+ <row>
+ <entry>28</entry><entry>int16_t</entry><entry>speed</entry><entry>m/s * 16</entry>
+ </row>
+ <row>
+ <entry>30</entry><entry>int16_t</entry><entry>height</entry><entry>m</entry>
+ </row>
+ <row>
+ <entry>32</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </section>
+ <section>
+ <title>TeleMetrum v2 Sensor Data</title>
+ <informaltable frame='none' label='' tocentry='0'>
+ <tgroup cols='2' align='center' colsep='1' rowsep='1'>
+ <colspec align='center' colwidth='*' colname='Offset'/>
+ <colspec align='left' colwidth='3*' colname='Description'/>
+ <thead>
+ <row>
+ <entry>Type</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>0x0A</entry>
+ <entry>TeleMetrum v2 Sensor Data</entry>
+ </row>
+ <row>
+ <entry>0x0B</entry>
+ <entry>TeleMetrum v2 Calibration Data</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ <para>
+ TeleMetrum v2 has higher resolution barometric data than
+ TeleMetrum v1, and so the constant calibration data is
+ split out into a separate packet.
+ </para>
+ <para>
+ TeleMetrum v2 Sensor Data packets are transmitted once per second on the
+ ground, 10 times per second during ascent and once per second
+ during descent and landing
+ </para>
+ <para>
+ TeleMetrum v2 Calibration Data packets are always transmitted once per second.
+ </para>
+ <table frame='all'>
+ <title>TeleMetrum v2 Sensor Packet Contents</title>
+ <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+ <colspec align='center' colwidth='*' colname='Offset'/>
+ <colspec align='center' colwidth='3*' colname='Data Type'/>
+ <colspec align='left' colwidth='3*' colname='Name'/>
+ <colspec align='left' colwidth='9*' colname='Description'/>
+ <thead>
+ <row>
+ <entry align='center'>Offset</entry>
+ <entry align='center'>Data Type</entry>
+ <entry align='center'>Name</entry>
+ <entry align='center'>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>5</entry><entry>uint8_t</entry><entry>state</entry><entry>Flight state</entry>
+ </row>
+ <row>
+ <entry>6</entry><entry>int16_t</entry><entry>accel</entry><entry>accelerometer</entry>
+ </row>
+ <row>
+ <entry>8</entry><entry>int32_t</entry><entry>pres</entry><entry>pressure sensor (Pa * 10)</entry>
+ </row>
+ <row>
+ <entry>12</entry><entry>int16_t</entry><entry>temp</entry><entry>temperature sensor (°C * 100)</entry>
+ </row>
+
+ <row>
+ <entry>14</entry><entry>int16_t</entry><entry>acceleration</entry><entry>m/s² * 16</entry>
+ </row>
+ <row>
+ <entry>16</entry><entry>int16_t</entry><entry>speed</entry><entry>m/s * 16</entry>
+ </row>
+ <row>
+ <entry>18</entry><entry>int16_t</entry><entry>height</entry><entry>m</entry>
+ </row>
+
+ <row>
+ <entry>20</entry><entry>int16_t</entry><entry>v_batt</entry><entry>battery voltage</entry>
+ </row>
+ <row>
+ <entry>22</entry><entry>int16_t</entry><entry>sense_d</entry><entry>drogue continuity sense</entry>
+ </row>
+ <row>
+ <entry>24</entry><entry>int16_t</entry><entry>sense_m</entry><entry>main continuity sense</entry>
+ </row>
+ <row>
+ <entry>26</entry><entry>pad[6]</entry><entry>pad bytes</entry><entry></entry>
+ </row>
+ <row>
+ <entry>32</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <table frame='all'>
+ <title>TeleMetrum v2 Calibration Data Packet Contents</title>
+ <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+ <colspec align='center' colwidth='*' colname='Offset'/>
+ <colspec align='center' colwidth='3*' colname='Data Type'/>
+ <colspec align='left' colwidth='3*' colname='Name'/>
+ <colspec align='left' colwidth='9*' colname='Description'/>
+ <thead>
+ <row>
+ <entry align='center'>Offset</entry>
+ <entry align='center'>Data Type</entry>
+ <entry align='center'>Name</entry>
+ <entry align='center'>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>5</entry><entry>pad[3]</entry><entry>pad bytes</entry><entry></entry>
+ </row>
+ <row>
+ <entry>8</entry><entry>int32_t</entry><entry>ground_pres</entry><entry>Average barometer reading on ground</entry>
+ </row>
+ <row>
+ <entry>12</entry><entry>int16_t</entry><entry>ground_accel</entry><entry>Average accelerometer reading on ground</entry>
+ </row>
+ <row>
+ <entry>14</entry><entry>int16_t</entry><entry>accel_plus_g</entry><entry>Accel calibration at +1g</entry>
+ </row>
+ <row>
+ <entry>16</entry><entry>int16_t</entry><entry>accel_minus_g</entry><entry>Accel calibration at -1g</entry>
+ </row>
+ <row>
+ <entry>18</entry><entry>pad[14]</entry><entry>pad bytes</entry><entry></entry>
+ </row>
+ <row>
+ <entry>32</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </section>
<section>
<title>Configuration Data</title>
<informaltable frame='none' label='' tocentry='0'>
</informaltable>
<para>
This packet provides all of the information available from the
- Venus SkyTraq GPS receiver—position, time, speed and precision
+ GPS receiver—position, time, speed and precision
estimates.
</para>
<para>
</tgroup>
</table>
</section>
+ <section>
+ <title>Companion Data Data</title>
+ <informaltable frame='none' label='' tocentry='0'>
+ <tgroup cols='2' align='center' colsep='1' rowsep='1'>
+ <colspec align='center' colwidth='*' colname='Offset'/>
+ <colspec align='left' colwidth='3*' colname='Description'/>
+ <thead>
+ <row>
+ <entry>Type</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>0x07</entry>
+ <entry>Companion Data Data</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ <para>
+ When a companion board is attached to TeleMega or TeleMetrum,
+ it can provide telemetry data to be included in the
+ downlink. The companion board can provide up to 12 16-bit data
+ values.
+ </para>
+ <para>
+ The companion board itself specifies the transmission rate. On
+ the ground and during descent, that rate is limited to one
+ packet per second. During ascent, that rate is limited to 10
+ packets per second.
+ </para>
+ <table frame='all'>
+ <title>Companion Data Contents</title>
+ <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+ <colspec align='right' colwidth='*' colname='Offset'/>
+ <colspec align='center' colwidth='3*' colname='Data Type'/>
+ <colspec align='left' colwidth='3*' colname='Name'/>
+ <colspec align='left' colwidth='9*' colname='Description'/>
+ <thead>
+ <row>
+ <entry align='center'>Offset</entry>
+ <entry align='center'>Data Type</entry>
+ <entry align='center'>Name</entry>
+ <entry align='center'>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>5</entry><entry>uint8_t</entry><entry>board_id</entry>
+ <entry>Type of companion board attached</entry>
+ </row>
+ <row>
+ <entry>6</entry><entry>uint8_t</entry><entry>update_period</entry>
+ <entry>How often telemetry is sent, in 1/100ths of a second</entry>
+ </row>
+ <row>
+ <entry>7</entry><entry>uint8_t</entry><entry>channels</entry>
+ <entry>Number of data channels supplied</entry>
+ </row>
+ <row>
+ <entry>8</entry><entry>uint16_t[12]</entry><entry>companion_data</entry>
+ <entry>Up to 12 channels of 16-bit companion data</entry>
+ </row>
+ <row>
+ <entry>32</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </section>
</section>
<section>
<title>Data Transmission</title>
<para>
- Altus Metrum devices use the Texas Instruments CC1111
- microcontroller which includes an integrated sub-GHz digital
- transceiver. This transceiver is used to both transmit and
- receive the telemetry packets. This section discusses what
- modulation scheme is used and how this device is configured.
+ Altus Metrum devices use Texas Instruments sub-GHz digital radio
+ products. Ground stations use parts with HW FEC while some
+ flight computers perform FEC in software. TeleGPS is
+ transmit-only.
</para>
+ <table>
+ <title>Altus Metrum Radio Parts</title>
+ <tgroup cols='3'>
+ <colspec align="center" colwidth="*" colname="Part Number"/>
+ <colspec align="center" colwidth="*" colname="Description"/>
+ <colspec align="left" colwidth="*" colname="Used in"/>
+ <thead>
+ <row>
+ <entry align="center">Part Number</entry>
+ <entry align="center">Description</entry>
+ <entry align="center">Used in</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>CC1111</entry><entry>10mW transceiver with integrated SoC</entry>
+ <entry>TeleDongle v0.2, TeleBT v1.0, TeleMetrum v1.x, TeleMini</entry>
+ </row>
+ <row>
+ <entry>CC1120</entry><entry>35mW transceiver with SW FEC</entry>
+ <entry>TeleMetrum v2, TeleMega</entry>
+ </row>
+ <row>
+ <entry>CC1200</entry><entry>35mW transceiver with HW FEC</entry>
+ <entry>TeleDongle v3.0, TeleBT v3.0</entry>
+ </row>
+ <row>
+ <entry>CC115L</entry><entry>14mW transmitter with SW FEC</entry>
+ <entry>TeleGPS</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
<section>
<title>Modulation Scheme</title>
<para>
Texas Instruments provides a tool for computing modulation
parameters given a desired modulation format and basic bit
- rate. For AltOS, the basic bit rate was specified as 38 kBaud,
- resulting in the following signal parmeters:
+ rate.
+
+ While we might like to use something with better low-signal
+ performance like BPSK, the radios we use don't support that,
+ but do support Gaussian frequency shift keying (GFSK). Regular
+ frequency shift keying (FSK) encodes the signal by switching
+ the carrier between two frequencies. The Gaussian version is
+ essentially the same, but the shift between frequencies gently
+ follows a gaussian curve, rather than switching
+ immediately. This tames the bandwidth of the signal without
+ affecting the ability to transmit data.
+
+ For AltOS, there are three available bit rates, 38.4kBaud,
+ 9.6kBaud and 2.4kBaud resulting in the following signal
+ parmeters:
+
</para>
<table>
<title>Modulation Scheme</title>
<tgroup cols='3'>
- <colspec align="center" colwidth="*" colname="parameter"/>
- <colspec align="center" colwidth="*" colname="value"/>
- <colspec align="center" colwidth="*" colname="description"/>
+ <colspec align="center" colwidth="*" colname="rate"/>
+ <colspec align="center" colwidth="*" colname="deviation"/>
+ <colspec align="center" colwidth="*" colname="bandwidth"/>
<thead>
<row>
- <entry align='center'>Parameter</entry>
- <entry align='center'>Value</entry>
- <entry align='center'>Description</entry>
+ <entry align='center'>Rate</entry>
+ <entry align='center'>Deviation</entry>
+ <entry align='center'>Receiver Bandwidth</entry>
</row>
</thead>
<tbody>
<row>
- <entry>Modulation</entry>
- <entry>GFSK</entry>
- <entry>Gaussian Frequency Shift Keying</entry>
- </row>
- <row>
- <entry>Deviation</entry>
- <entry>20.507812 kHz</entry>
- <entry>Frequency modulation</entry>
- </row>
- <row>
- <entry>Data rate</entry>
- <entry>38.360596 kBaud</entry>
- <entry>Raw bit rate</entry>
+ <entry>38.4kBaud</entry>
+ <entry>20.5kHz</entry>
+ <entry>100kHz</entry>
</row>
<row>
- <entry>RX Filter Bandwidth</entry>
- <entry>93.75 kHz</entry>
- <entry>Receiver Band pass filter bandwidth</entry>
+ <entry>9.6kBaud</entry>
+ <entry>5.125kHz</entry>
+ <entry>25kHz</entry>
</row>
<row>
- <entry>IF Frequency</entry>
- <entry>140.62 kHz</entry>
- <entry>Receiver intermediate frequency</entry>
+ <entry>2.4kBaud</entry>
+ <entry>1.5kHz</entry>
+ <entry>5kHz</entry>
</row>
</tbody>
</tgroup>
<section>
<title>Error Correction</title>
<para>
- The cc1111 provides forward error correction in hardware,
- which AltOS uses to improve reception of weak signals. The
- overall effect of this is to halve the available bandwidth for
- data from 38 kBaud to 19 kBaud.
+ The cc1111 and cc1200 provide forward error correction in
+ hardware; on the cc1120 and cc115l that's done in
+ software. AltOS uses this to improve reception of weak
+ signals. As it's a rate 1/2 encoding, each bit of data takes
+ two bits when transmitted, so the effective data rate is half
+ of the raw transmitted bit rate.
</para>
<table>
<title>Error Correction</title>
icotool -c -o $@ $(shell for i in $(WIN_RES); do echo $*-$$i.png; done)
.ico.rc:
- echo '101 ICON "$*.ico"' > $@
+ ./make-rc "$*" $(VERSION) > $@
MINGCC32=i686-w64-mingw32-gcc
MINGWINDRES=i686-w64-mingw32-windres
+MINGFLAGS=-Wall -DWINDOWS -mwindows
+MINGLIBS=-lshlwapi
.rc.o:
$(MINGWINDRES) $*.rc $@
.o.exe:
- $(MINGCC32) -o $@ windows-stub.c $*.o
+ $(MINGCC32) -o $@ $(MINGFLAGS) windows-stub.o $*.o $(MINGLIBS)
+
+$(EXE_FILES): windows-stub.o make-rc
+
+windows-stub.o: windows-stub.c
+ $(MINGCC32) -c $(MINGFLAGS) windows-stub.c
--- /dev/null
+#!/bin/sh
+
+COMPANY="Altus Metrum, LLC"
+PRODUCT="Altus Metrum"
+
+case "$1" in
+ *altosui*)
+ PRODUCT="AltosUI"
+ ;;
+ *telegps*)
+ PRODUCT="TeleGPS"
+ ;;
+ *micropeak*)
+ PRODUCT="MicroPeak"
+ ;;
+esac
+
+VERSION="$2"
+VERSION_COMMA=`echo "$VERSION" | sed 's/\./,/g'`
+INTERNAL_NAME=`basename $1`
+EXE_NAME="$INTERNAL_NAME".exe
+YEAR=`date +%Y`
+
+cat <<EOF
+101 ICON "$1.ico"
+1 VERSIONINFO
+FILEVERSION $VERSION_COMMA
+PRODUCTVERSION $VERSION_COMMA
+FILEFLAGSMASK 0
+FILEOS 0x40004
+FILETYPE 1
+{
+ BLOCK "StringFileInfo"
+ {
+ BLOCK "040904E4"
+ {
+ VALUE "Comments", "$COMPANY $PRODUCT"
+ VALUE "CompanyName", "$COMPANY"
+ VALUE "FileDescription", "$PRODUCT"
+ VALUE "FileVersion", "$VERSION"
+ VALUE "InternalName", "$INTERNAL_NAME"
+ VALUE "LegalCopyright", "Copyright $YEAR, $COMPANY"
+ VALUE "OriginalFilename", "$EXE_NAME"
+ VALUE "ProductName", "$PRODUCT"
+ VALUE "ProductVersion", "$VERSION"
+ }
+ }
+ BLOCK "VarFileInfo"
+ {
+ VALUE "Translation", 0x409, 1252
+ }
+}
+EOF
-__stdcall
-WinMain(int a, int b, int c, int d) { return 0; }
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+/* A windows stub program to launch a java program with suitable parameters
+ *
+ * Given that the name of this exe is altusmetrum-foo.exe living in directory bar, and
+ * that it was run with 'args' extra command line parameters, run:
+ *
+ * javaw.exe -Djava.library.path="bar" -jar "bar/foo-fat.jar" args
+ */
+
+#define _UNICODE
+#define UNICODE
+#include <stdlib.h>
+#include <windows.h>
+#include <setupapi.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <shlwapi.h>
+
+/* Concatenate a list of strings together
+ */
+static LPTSTR
+wcsbuild(LPTSTR first, ...)
+{
+ va_list args;
+ int len;
+ LPTSTR buf;
+ LPTSTR arg;
+
+ buf = wcsdup(first);
+ va_start(args, first);
+ while ((arg = va_arg(args, LPTSTR)) != NULL) {
+ len = wcslen(buf) + wcslen(arg) + 1;
+ buf = realloc(buf, len * sizeof (wchar_t));
+ wcscat(buf, arg);
+ }
+ va_end(args);
+ return buf;
+}
+
+/* Quote a single string, taking care to escape embedded quote and
+ * backslashes within
+ */
+static LPTSTR
+quote_arg(LPTSTR arg)
+{
+ LPTSTR result;
+ LPTSTR in, out;
+ int out_len = 3; /* quotes and terminating null */
+
+ /* Find quote and backslashes */
+ for (in = arg; *in; in++) {
+ switch (*in) {
+ case '"':
+ case '\\':
+ out_len += 2;
+ break;
+ default:
+ out_len++;
+ break;
+ }
+ }
+
+ result = malloc ((out_len + 1) * sizeof (wchar_t));
+ out = result;
+ *out++ = '"';
+ for (in = arg; *in; in++) {
+ switch (*in) {
+ case '"':
+ case '\\':
+ *out++ = '\\';
+ break;
+ }
+ *out++ = *in;
+ }
+ *out++ = '"';
+ *out++ = '\0';
+ return result;
+}
+
+/* Construct a single string from a list of arguments
+ */
+static LPTSTR
+quote_args(LPTSTR *argv, int argc)
+{
+ LPTSTR result = NULL, arg;
+ int i;
+
+ result = malloc(1 * sizeof (wchar_t));
+ result[0] = '\0';
+ for (i = 0; i < argc; i++) {
+ arg = quote_arg(argv[i]);
+ result = realloc(result, (wcslen(result) + 1 + wcslen(arg) + 1) * sizeof (wchar_t));
+ wcscat(result, L" ");
+ wcscat(result, arg);
+ free(arg);
+ }
+ return result;
+}
+
+/* Return the directory portion of the provided file
+ */
+static LPTSTR
+get_dir(LPTSTR file)
+{
+ DWORD len = GetFullPathName(file, 0, NULL, NULL);
+ LPTSTR full = malloc (len * sizeof (wchar_t));
+ GetFullPathName(file, len, full, NULL);
+ PathRemoveFileSpec(full);
+ return full;
+}
+
+/* Convert a .exe name into a -fat.jar name, starting
+ * by computing the complete path name of the source filename
+ */
+static LPTSTR
+make_jar(LPTSTR file)
+{
+ DWORD len = GetFullPathName(file, 0, NULL, NULL);
+ LPTSTR full = malloc (len * sizeof (wchar_t));
+ LPTSTR base_part;
+ LPTSTR jar;
+ LPTSTR dot;
+ GetFullPathName(file, len, full, &base_part);
+ static const wchar_t head[] = L"altusmetrum-";
+
+ if (wcsncmp(base_part, head, wcslen(head)) == 0)
+ base_part += wcslen(head);
+ dot = wcsrchr(base_part, '.');
+ if (dot)
+ *dot = '\0';
+ jar = wcsdup(base_part);
+ PathRemoveFileSpec(full);
+ return wcsbuild(full, L"\\", jar, L"-fat.jar", NULL);
+}
+
+/* Build the complete command line from the pieces
+ */
+static LPTSTR
+make_cmd(LPTSTR dir, LPTSTR jar, LPTSTR quote_args)
+{
+ LPTSTR quote_dir = quote_arg(dir);
+ LPTSTR quote_jar = quote_arg(jar);
+ LPTSTR cmd;
+
+ cmd = wcsbuild(L"javaw.exe -Djava.library.path=", quote_dir, L" -jar ", quote_jar, quote_args, NULL);
+ free(quote_jar);
+ free(jar);
+ free(quote_dir);
+ return cmd;
+}
+
+int WINAPI
+WinMain(HINSTANCE instance, HINSTANCE prev_instance, LPSTR cmd_line_a, int cmd_show)
+{
+ STARTUPINFO startup_info;
+ PROCESS_INFORMATION process_information;
+ BOOL result;
+ wchar_t *command_line;
+ int argc;
+ LPTSTR *argv = CommandLineToArgvW(GetCommandLine(), &argc);
+ LPTSTR my_dir;
+ LPTSTR my_jar;
+ LPTSTR args = quote_args(argv + 1, argc - 1);
+
+ my_dir = get_dir(argv[0]);
+ my_jar = make_jar(argv[0]);
+ command_line = make_cmd(my_dir, my_jar, args);
+ memset(&startup_info, '\0', sizeof startup_info);
+ startup_info.cb = sizeof startup_info;
+ result = CreateProcess(NULL,
+ command_line,
+ NULL,
+ NULL,
+ FALSE,
+ CREATE_NO_WINDOW,
+ NULL,
+ NULL,
+ &startup_info,
+ &process_information);
+ if (result) {
+ CloseHandle(process_information.hProcess);
+ CloseHandle(process_information.hThread);
+ }
+ exit(0);
+}
LINUX_FILES=$(FAT_FILES) libaltos.so $(FIRMWARE) $(DOC) $(desktop_file).in $(LINUX_ICONS) $(LINUX_MIMETYPE)
LINUX_EXTRA=micropeak-fat $(desktop_file).in
-MACOSX_DRIVER_URL=http://www.ftdichip.com/Drivers/VCP/MacOSX/FTDIUSBSerialDriver_v2_2_18.dmg
-MACOSX_DRIVER=FTDIUSBSerialDriver_v2_2_18.dmg
+MACOSX_DRIVER_0_URL=http://www.ftdichip.com/Drivers/VCP/MacOSX/FTDIUSBSerialDriver_v2_2_18.dmg
+MACOSX_DRIVER_0=FTDI_v2_2_18.dmg
+
+MACOSX_DRIVER_1_URL=http://www.ftdichip.com/Drivers/VCP/MacOSX/FTDIUSBSerialDriver_v2_3.dmg
+MACOSX_DRIVER_1=FTDI_v2_3.dmg
+
+MACOSX_DRIVERS=$(MACOSX_DRIVER_1) $(MACOSX_DRIVER_0)
+
MACOSX_INFO_PLIST=Info.plist
MACOSX_README=ReadMe-Mac.rtf
-MACOSX_FILES=$(FAT_FILES) libaltos.dylib $(MACOSX_INFO_PLIST) $(MACOSX_DRIVER) $(MACOSX_README) $(DOC) $(MACOSX_ICONS)
+MACOSX_FILES=$(FAT_FILES) libaltos.dylib $(MACOSX_INFO_PLIST) $(MACOSX_DRIVERS) $(MACOSX_README) $(DOC) $(MACOSX_ICONS)
+
+$(MACOSX_DRIVER_0):
+ wget -O $@ $(MACOSX_DRIVER_0_URL)
-$(MACOSX_DRIVER):
- wget $(MACOSX_DRIVER_URL)
+$(MACOSX_DRIVER_1):
+ wget -O $@ $(MACOSX_DRIVER_1_URL)
-WINDOWS_DRIVER_URL=http://www.ftdichip.com/Drivers/CDM/CDM20824_Setup.exe
-WINDOWS_DRIVER=CDM20824_Setup.exe
+WINDOWS_DRIVER_URL=http://www.ftdichip.com/Drivers/CDM/CDM%20v2.12.00%20WHQL%20Certified.exe
+WINDOWS_DRIVER=CDM_v2.12.00_WHQL_Certified.exe
$(WINDOWS_DRIVER):
- wget $(WINDOWS_DRIVER_URL)
+ wget -O "$(WINDOWS_DRIVER)" "$(WINDOWS_DRIVER_URL)"
WINDOWS_FILES=$(FAT_FILES) altos.dll altos64.dll $(DOC) $(WINDOWS_ICONS) $(WINDOWS_DRIVER)
cp -a $(MACOSX_README) macosx/ReadMe.rtf
cp -a $(DOC) macosx
cp -p Info.plist macosx/MicroPeak.app/Contents
- cp -p $(MACOSX_DRIVER) macosx
+ cp -p $(MACOSX_DRIVERS) macosx
mkdir -p macosx/MicroPeak.app/Contents/Resources/Java
cp -p $(MACOSX_ICONS) macosx/MicroPeak.app/Contents/Resources
cp -p $(FATJAR) macosx/MicroPeak.app/Contents/Resources/Java/micropeak.jar
import java.lang.*;
import java.io.*;
import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
class MicroIterator implements Iterator<MicroDataPoint> {
int i;
package org.altusmetrum.micropeak;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroDataPoint implements AltosUIDataPoint {
public double time;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroDeviceDialog extends AltosDeviceDialog {
import java.io.*;
import java.util.concurrent.*;
import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroDownload extends AltosUIDialog implements Runnable, ActionListener, MicroSerialLog, WindowListener {
MicroPeak owner;
import java.awt.*;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroExport extends JFileChooser {
import java.io.*;
import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroFile {
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.io.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroFileChooser extends JFileChooser {
JFrame frame;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroFrame extends AltosUIFrame {
static String[] micro_icon_names = {
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
import java.io.*;
import java.util.concurrent.*;
import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroPeak extends MicroFrame implements ActionListener, ItemListener {
import java.awt.*;
import java.io.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroRaw extends JTextArea {
import java.io.*;
import java.util.concurrent.*;
import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroSave extends JFileChooser {
import java.util.*;
import java.io.*;
import libaltosJNI.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroSerial extends InputStream {
SWIGTYPE_p_altos_file file;
import java.util.*;
import java.io.*;
import libaltosJNI.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_7.*;
public interface MicroSerialLog {
package org.altusmetrum.micropeak;
import java.io.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroStats {
double coast_height;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroStatsTable extends JComponent implements AltosFontListener {
GridBagLayout layout;
import java.util.*;
import libaltosJNI.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class MicroUSB extends altos_device implements AltosDevice {
Section "FTDI USB Driver"
SetOutPath $INSTDIR
- File "CDM20824_Setup.exe"
+ File "CDM_v2.12.00_WHQL_Certified.exe"
- StrCpy $2 "$INSTDIR\CDM20824_Setup.exe"
+ StrCpy $2 "$INSTDIR\CDM_v2.12.00_WHQL_Certified.exe"
ExecWait $2
SectionEnd
File "altosuilib_@ALTOSUILIB_VERSION@.jar"
File "jfreechart.jar"
File "jcommon.jar"
+ File "../icon/${WIN_APP_EXE}"
File "*.dll"
File "../icon/${WIN_APP_ICON}"
- CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}"
+ CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}"
SectionEnd
Section "${REG_NAME} Desktop Shortcut"
- CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}"
+ CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}"
SectionEnd
Section "Documentation"
SetOutPath $INSTDIR
- File "../icon/${WIN_APP_EXE}"
File "../icon/${WIN_MPD_EXE}"
- SearchPath $1 "javaw.exe"
-
; application elements
DeleteRegKey HKCR "${PROG_ID}"
WriteRegStr HKCR "${PROG_ID_MPD}" "FriendlyTypeName" "MicroPeak Data File"
WriteRegStr HKCR "${PROG_ID_MPD}\CurVer" "" "${PROG_ID_MPD}"
WriteRegStr HKCR "${PROG_ID_MPD}\DefaultIcon" "" '"$INSTDIR\${WIN_MPD_EXE}",-101'
- WriteRegExpandStr HKCR "${PROG_ID_MPD}\shell\play\command" "" '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"'
+ WriteRegExpandStr HKCR "${PROG_ID_MPD}\shell\play\command" "" '"$INSTDIR\${WIN_APP_EXE}" "%1"'
; .mpd elements
telegps-v0.3 telegps-v0.3/flash-loader \
telegps-v1.0 telegps-v1.0/flash-loader \
telelco-v0.2 telelco-v0.2/flash-loader \
+ telelco-v0.3 telelco-v0.3/flash-loader \
telescience-v0.2 telescience-v0.2/flash-loader \
teledongle-v3.0 teledongle-v3.0/flash-loader \
teleballoon-v2.0 \
telebt-v3.0 telebt-v3.0/flash-loader
ARMM0DIRS=\
- easymini-v1.0 easymini-v1.0/flash-loader
+ easymini-v1.0 easymini-v1.0/flash-loader \
+ chaoskey-v0.1 chaoskey-v0.1/flash-loader
AVRDIRS=\
telescience-v0.1 telescience-pwm micropeak nanopeak-v0.1 microkite
--- /dev/null
+ao_product.h
+chaoskey-*
--- /dev/null
+#
+# AltOS build
+#
+#
+
+include ../stmf0/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_task.h \
+ ao_adc_fast.h \
+ stm32f0.h
+
+#
+# Common AltOS sources
+#
+ALTOS_SRC = \
+ ao_interrupt.c \
+ ao_timer.c \
+ ao_panic.c \
+ ao_mutex.c \
+ ao_dma_stm.c \
+ ao_adc_fast.c \
+ ao_crc_stm.c \
+ ao_stdio.c \
+ ao_led.c \
+ ao_romconfig.c \
+ ao_boot_chain.c \
+ ao_usb_stm.c \
+ ao_trng_send.c \
+ ao_task.c \
+ ao_product.c
+
+PRODUCT=ChaosKey-v0.1
+PRODUCT_DEF=-DCHAOSKEY_V_0_1
+IDVENDOR=0x1d50
+IDPRODUCT=0x60c6
+
+CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS) -g -Os
+
+PROGNAME=chaoskey-v0.1
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_chaoskey.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -V $(IDVENDOR) -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) -o $@
+
+$(OBJ): $(INC)
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f ao_product.h
+
+install:
+
+uninstall:
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_adc_fast.h>
+#include <ao_crc.h>
+#include <ao_trng_send.h>
+
+void main(void)
+{
+ ao_led_init(LEDS_AVAILABLE);
+ ao_led_on(AO_LED_RED);
+ ao_clock_init();
+ ao_task_init();
+ ao_timer_init();
+ ao_dma_init();
+ ao_adc_init();
+ ao_crc_init();
+
+ ao_usb_init();
+
+ ao_trng_send_init();
+
+ ao_led_off(AO_LED_RED);
+
+ ao_start_scheduler();
+}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define LED_PORT_ENABLE STM_RCC_AHBENR_IOPAEN
+#define LED_PORT (&stm_gpioa)
+#define LED_PIN_RED 2
+#define LED_PIN_GREEN 3
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+
+#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN)
+
+#define HAS_BEEP 0
+
+/* 48MHz clock based on USB */
+#define AO_HSI48 1
+
+/* HCLK = 48MHz */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* APB = 48MHz */
+#define AO_APB_PRESCALER 1
+#define AO_RCC_CFGR_PPRE_DIV STM_RCC_CFGR_PPRE_DIV_1
+
+#define HAS_USB 1
+#define AO_USB_DIRECTIO 1
+#define AO_PA11_PA12_RMP 0
+#define AO_USB_INTERFACE_CLASS 0xff
+
+#define IS_FLASH_LOADER 0
+
+/* ADC */
+
+#define AO_ADC_PIN0_PORT (&stm_gpioa)
+#define AO_ADC_PIN0_PIN 6
+#define AO_ADC_PIN0_CH 6
+
+#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_IOPAEN))
+
+#define AO_NUM_ADC 1
+
+/* CRC */
+#define AO_CRC_WIDTH 32
+#define AO_CRC_INIT 0xffffffff
+
+/* TRNG */
+#define AO_LED_TRNG_ACTIVE AO_LED_GREEN
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+ao_product.h
+chaoskey*
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=chaoskey-v0.1
+include $(TOPDIR)/stmf0/Makefile-flash.defs
--- /dev/null
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#include <ao_flash_stm_pins.h>
+
+/* Pin 5 on debug connector */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpioa
+#define AO_BOOT_APPLICATION_PIN 15
+#define AO_BOOT_APPLICATION_VALUE 1
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP
+
+/* USB */
+#define HAS_USB 1
+#define AO_USB_DIRECTIO 0
+#define AO_PA11_PA12_RMP 0
+
+#endif /* _AO_PINS_H_ */
static int32_t latitude;
static int32_t longitude;
static int32_t altitude;
- int32_t lat, lon, alt;
- uint8_t *buf;
+ uint8_t *buf;
if (ao_gps_data.flags & AO_GPS_VALID) {
latitude = ao_gps_data.latitude;
}
buf = tncBuffer;
- *buf++ = '!';
- /* Symbol table ID */
- *buf++ = '/';
+#ifdef AO_APRS_TEST
+#define AO_APRS_FORMAT_COMPRESSED 0
+#define AO_APRS_FORMAT_UNCOMPRESSED 1
+ switch (AO_APRS_FORMAT_COMPRESSED) {
+#else
+ switch (ao_config.aprs_format) {
+#endif
+ case AO_APRS_FORMAT_COMPRESSED:
+ default:
+ {
+ int32_t lat, lon, alt;
+
+ *buf++ = '!';
+
+ /* Symbol table ID */
+ *buf++ = '/';
+
+ lat = ((uint64_t) 380926 * (900000000 - latitude)) / 10000000;
+ lon = ((uint64_t) 190463 * (1800000000 + longitude)) / 10000000;
+
+ alt = ao_aprs_encode_altitude(altitude);
- lat = ((uint64_t) 380926 * (900000000 - latitude)) / 10000000;
- lon = ((uint64_t) 190463 * (1800000000 + longitude)) / 10000000;
+ tncCompressInt(buf, lat, 4);
+ buf += 4;
+ tncCompressInt(buf, lon, 4);
+ buf += 4;
- alt = ao_aprs_encode_altitude(altitude);
+ /* Symbol code */
+ *buf++ = '\'';
- tncCompressInt(buf, lat, 4);
- buf += 4;
- tncCompressInt(buf, lon, 4);
- buf += 4;
+ tncCompressInt(buf, alt, 2);
+ buf += 2;
- /* Symbol code */
- *buf++ = '\'';
+ *buf++ = 33 + ((1 << 5) | (2 << 3));
- tncCompressInt(buf, alt, 2);
- buf += 2;
+ break;
+ }
+ case AO_APRS_FORMAT_UNCOMPRESSED:
+ {
+ char lat_sign = 'N', lon_sign = 'E';
+ int32_t lat = latitude;
+ int32_t lon = longitude;
+ int32_t alt = altitude;
+ uint16_t lat_deg;
+ uint16_t lon_deg;
+ uint16_t lat_min;
+ uint16_t lat_frac;
+ uint16_t lon_min;
+ uint16_t lon_frac;
+
+ if (lat < 0) {
+ lat_sign = 'S';
+ lat = -lat;
+ }
- *buf++ = 33 + ((1 << 5) | (2 << 3));
+ if (lon < 0) {
+ lon_sign = 'W';
+ lon = -lon;
+ }
+
+ /* Round latitude and longitude by 0.005 minutes */
+ lat = lat + 833;
+ if (lat > 900000000)
+ lat = 900000000;
+ lon = lon + 833;
+ if (lon > 1800000000)
+ lon = 1800000000;
+
+ lat_deg = lat / 10000000;
+ lat -= lat_deg * 10000000;
+ lat *= 60;
+ lat_min = lat / 10000000;
+ lat -= lat_min * 10000000;
+ lat_frac = lat / 100000;
+
+ lon_deg = lon / 10000000;
+ lon -= lon_deg * 10000000;
+ lon *= 60;
+ lon_min = lon / 10000000;
+ lon -= lon_min * 10000000;
+ lon_frac = lon / 100000;
+
+ /* Convert from meters to feet */
+ alt = (alt * 328 + 50) / 100;
+
+ buf += sprintf((char *) tncBuffer, "!%02u%02u.%02u%c/%03u%02u.%02u%c'/A=%06u ",
+ lat_deg, lat_min, lat_frac, lat_sign,
+ lon_deg, lon_min, lon_frac, lon_sign,
+ alt);
+ break;
+ }
+ }
buf += tncComment(buf);
ao_btm_cmd(__code char *cmd)
{
ao_btm_drain();
+
+#ifdef AO_BTM_INT_PORT
+ /* Trust that AltosDroid will eventually disconnect and let us
+ * get things set up. The BTM module doesn't appear to listen
+ * for +++, so we have no way to force a disconnect.
+ */
+ while (ao_btm_connected)
+ ao_sleep(&ao_btm_connected);
+#endif
ao_btm_string(cmd);
return ao_btm_wait_reply();
}
void
ao_btm(void)
{
+#ifdef AO_BTM_INT_PORT
+ ao_exti_enable(AO_BTM_INT_PORT, AO_BTM_INT_PIN);
+#endif
+
/*
* Wait for the bluetooth device to boot
*/
/* Turn off status reporting */
ao_btm_cmd("ATQ1\r");
+ ao_btm_drain();
+
ao_btm_stdio = ao_add_stdio(_ao_serial_btm_pollchar,
ao_serial_btm_putchar,
NULL);
/* Check current pin state */
ao_btm_check_link();
-#ifdef AO_BTM_INT_PORT
- ao_exti_enable(AO_BTM_INT_PORT, AO_BTM_INT_PIN);
-#endif
-
for (;;) {
while (!ao_btm_connected)
ao_sleep(&ao_btm_connected);
extern const uint32_t ao_radio_cal;
+#ifdef AO_CC1200_FOSC
+#define FOSC AO_CC1200_FOSC
+#else
#define FOSC 40000000
+#endif
#define ao_radio_select() ao_spi_get_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS,AO_SPI_SPEED_FAST)
#define ao_radio_deselect() ao_spi_put_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS)
* CHANBW = 5.0 (round to 9.5)
*/
+#if FOSC == 40000000
#define PACKET_SYMBOL_RATE_M 1013008
-
#define PACKET_SYMBOL_RATE_E_384 8
+#define PACKET_SYMBOL_RATE_E_96 6
+#define PACKET_SYMBOL_RATE_E_24 4
+#endif
+
+#if FOSC == 32000000
+#define PACKET_SYMBOL_RATE_M 239914
+#define PACKET_SYMBOL_RATE_E_384 9
+#define PACKET_SYMBOL_RATE_E_96 7
+#define PACKET_SYMBOL_RATE_E_24 5
+#endif
/* 200 / 2 = 100 */
#define PACKET_CHAN_BW_384 ((CC1200_CHAN_BW_ADC_CIC_DECFACT_12 << CC1200_CHAN_BW_ADC_CIC_DECFACT) | \
(16 << CC1200_CHAN_BW_BB_CIC_DECFACT))
-#define PACKET_SYMBOL_RATE_E_96 6
/* 200 / 10 = 20 */
#define PACKET_CHAN_BW_96 ((CC1200_CHAN_BW_ADC_CIC_DECFACT_48 << CC1200_CHAN_BW_ADC_CIC_DECFACT) | \
(16 << CC1200_CHAN_BW_BB_CIC_DECFACT))
-#define PACKET_SYMBOL_RATE_E_24 4
/* 200 / 25 = 8 */
#define PACKET_CHAN_BW_24 ((CC1200_CHAN_BW_ADC_CIC_DECFACT_48 << CC1200_CHAN_BW_ADC_CIC_DECFACT) | \
(44 << CC1200_CHAN_BW_BB_CIC_DECFACT))
--- /dev/null
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_lco.h>
+#include <ao_event.h>
+#include <ao_seven_segment.h>
+#include <ao_quadrature.h>
+#include <ao_lco_func.h>
+#include <ao_radio_cmac.h>
+
+#define DEBUG 1
+
+#if DEBUG
+static uint8_t ao_lco_debug;
+#define PRINTD(...) do { if (!ao_lco_debug) break; printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
+#else
+#define PRINTD(...)
+#endif
+
+#define AO_LCO_PAD_DIGIT 0
+#define AO_LCO_BOX_DIGIT_1 1
+#define AO_LCO_BOX_DIGIT_10 2
+
+static uint8_t ao_lco_min_box, ao_lco_max_box;
+static uint8_t ao_lco_pad;
+static uint8_t ao_lco_box;
+static uint8_t ao_lco_armed;
+static uint8_t ao_lco_firing;
+static uint8_t ao_lco_valid;
+static uint8_t ao_lco_got_channels;
+static uint16_t ao_lco_tick_offset;
+
+static struct ao_pad_query ao_pad_query;
+
+static void
+ao_lco_set_pad(uint8_t pad)
+{
+ ao_seven_segment_set(AO_LCO_PAD_DIGIT, pad);
+}
+
+static void
+ao_lco_set_box(uint8_t box)
+{
+ ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, box % 10);
+ ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, box / 10);
+}
+
+static void
+ao_lco_set_voltage(uint16_t decivolts)
+{
+ uint8_t tens, ones, tenths;
+
+ tenths = decivolts % 10;
+ ones = (decivolts / 10) % 10;
+ tens = (decivolts / 100) % 10;
+ ao_seven_segment_set(AO_LCO_PAD_DIGIT, tenths);
+ ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, ones | 0x10);
+ ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, tens);
+}
+
+static void
+ao_lco_set_display(void)
+{
+ if (ao_lco_pad == 0) {
+ ao_lco_set_voltage(ao_pad_query.battery);
+ } else {
+ ao_lco_set_pad(ao_lco_pad);
+ ao_lco_set_box(ao_lco_box);
+ }
+}
+
+#define MASK_SIZE(n) (((n) + 7) >> 3)
+#define MASK_ID(n) ((n) >> 3)
+#define MASK_SHIFT(n) ((n) & 7)
+
+static uint8_t ao_lco_box_mask[MASK_SIZE(AO_PAD_MAX_BOXES)];
+
+static uint8_t
+ao_lco_box_present(uint8_t box)
+{
+ if (box >= AO_PAD_MAX_BOXES)
+ return 0;
+ return (ao_lco_box_mask[MASK_ID(box)] >> MASK_SHIFT(box)) & 1;
+}
+
+static uint8_t
+ao_lco_pad_present(uint8_t pad)
+{
+ if (!ao_lco_got_channels || !ao_pad_query.channels)
+ return pad == 0;
+ /* voltage measurement is always valid */
+ if (pad == 0)
+ return 1;
+ if (pad > AO_PAD_MAX_CHANNELS)
+ return 0;
+ return (ao_pad_query.channels >> (pad - 1)) & 1;
+}
+
+static uint8_t
+ao_lco_pad_first(void)
+{
+ uint8_t pad;
+
+ for (pad = 1; pad <= AO_PAD_MAX_CHANNELS; pad++)
+ if (ao_lco_pad_present(pad))
+ return pad;
+ return 0;
+}
+
+static void
+ao_lco_input(void)
+{
+ static struct ao_event event;
+ int8_t dir, new_box, new_pad;
+
+ ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
+ for (;;) {
+ ao_event_get(&event);
+ PRINTD("event type %d unit %d value %d\n",
+ event.type, event.unit, event.value);
+ switch (event.type) {
+ case AO_EVENT_QUADRATURE:
+ switch (event.unit) {
+ case AO_QUADRATURE_PAD:
+ if (!ao_lco_armed) {
+ dir = (int8_t) event.value;
+ new_pad = ao_lco_pad;
+ do {
+ new_pad += dir;
+ if (new_pad > AO_PAD_MAX_CHANNELS)
+ new_pad = 0;
+ if (new_pad < 0)
+ new_pad = AO_PAD_MAX_CHANNELS;
+ if (new_pad == ao_lco_pad)
+ break;
+ } while (!ao_lco_pad_present(new_pad));
+ if (new_pad != ao_lco_pad) {
+ ao_lco_pad = new_pad;
+ ao_lco_set_display();
+ }
+ }
+ break;
+ case AO_QUADRATURE_BOX:
+ if (!ao_lco_armed) {
+ dir = (int8_t) event.value;
+ new_box = ao_lco_box;
+ do {
+ new_box += dir;
+ if (new_box > ao_lco_max_box)
+ new_box = ao_lco_min_box;
+ else if (new_box < ao_lco_min_box)
+ new_box = ao_lco_max_box;
+ if (new_box == ao_lco_box)
+ break;
+ } while (!ao_lco_box_present(new_box));
+ if (ao_lco_box != new_box) {
+ ao_lco_box = new_box;
+ ao_lco_pad = 1;
+ ao_lco_got_channels = 0;
+ ao_lco_set_display();
+ }
+ }
+ break;
+ }
+ break;
+ case AO_EVENT_BUTTON:
+ switch (event.unit) {
+ case AO_BUTTON_ARM:
+ ao_lco_armed = event.value;
+ PRINTD("Armed %d\n", ao_lco_armed);
+ ao_wakeup(&ao_lco_armed);
+ break;
+ case AO_BUTTON_FIRE:
+ if (ao_lco_armed) {
+ ao_lco_firing = event.value;
+ PRINTD("Firing %d\n", ao_lco_firing);
+ ao_wakeup(&ao_lco_armed);
+ }
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static AO_LED_TYPE continuity_led[AO_LED_CONTINUITY_NUM] = {
+#ifdef AO_LED_CONTINUITY_0
+ AO_LED_CONTINUITY_0,
+#endif
+#ifdef AO_LED_CONTINUITY_1
+ AO_LED_CONTINUITY_1,
+#endif
+#ifdef AO_LED_CONTINUITY_2
+ AO_LED_CONTINUITY_2,
+#endif
+#ifdef AO_LED_CONTINUITY_3
+ AO_LED_CONTINUITY_3,
+#endif
+#ifdef AO_LED_CONTINUITY_4
+ AO_LED_CONTINUITY_4,
+#endif
+#ifdef AO_LED_CONTINUITY_5
+ AO_LED_CONTINUITY_5,
+#endif
+#ifdef AO_LED_CONTINUITY_6
+ AO_LED_CONTINUITY_6,
+#endif
+#ifdef AO_LED_CONTINUITY_7
+ AO_LED_CONTINUITY_7,
+#endif
+};
+
+static void
+ao_lco_update(void)
+{
+ int8_t r;
+ uint8_t c;
+
+ r = ao_lco_query(ao_lco_box, &ao_pad_query, &ao_lco_tick_offset);
+ if (r == AO_RADIO_CMAC_OK) {
+ c = ao_lco_got_channels;
+ ao_lco_got_channels = 1;
+ ao_lco_valid = 1;
+ if (!c) {
+ if (ao_lco_pad != 0)
+ ao_lco_pad = ao_lco_pad_first();
+ ao_lco_set_display();
+ }
+ if (ao_lco_pad == 0)
+ ao_lco_set_display();
+ } else
+ ao_lco_valid = 0;
+
+#if 0
+ PRINTD("lco_query success arm_status %d i0 %d i1 %d i2 %d i3 %d\n",
+ query.arm_status,
+ query.igniter_status[0],
+ query.igniter_status[1],
+ query.igniter_status[2],
+ query.igniter_status[3]);
+#endif
+ PRINTD("ao_lco_update valid %d\n", ao_lco_valid);
+ ao_wakeup(&ao_pad_query);
+}
+
+static void
+ao_lco_box_reset_present(void)
+{
+ ao_lco_min_box = 0xff;
+ ao_lco_max_box = 0x00;
+ memset(ao_lco_box_mask, 0, sizeof (ao_lco_box_mask));
+}
+
+static void
+ao_lco_box_set_present(uint8_t box)
+{
+ if (box < ao_lco_min_box)
+ ao_lco_min_box = box;
+ if (box > ao_lco_max_box)
+ ao_lco_max_box = box;
+ if (box >= AO_PAD_MAX_BOXES)
+ return;
+ ao_lco_box_mask[MASK_ID(box)] |= 1 << MASK_SHIFT(box);
+}
+
+static void
+ao_lco_search(void)
+{
+ uint16_t tick_offset;
+ int8_t r;
+ int8_t try;
+ uint8_t box;
+ uint8_t boxes = 0;
+
+ ao_lco_box_reset_present();
+ ao_lco_set_pad(0);
+ for (box = 0; box < AO_PAD_MAX_BOXES; box++) {
+ if ((box % 10) == 0)
+ ao_lco_set_box(box);
+ for (try = 0; try < 3; try++) {
+ tick_offset = 0;
+ r = ao_lco_query(box, &ao_pad_query, &tick_offset);
+ PRINTD("box %d result %d\n", box, r);
+ if (r == AO_RADIO_CMAC_OK) {
+ ++boxes;
+ ao_lco_box_set_present(box);
+ ao_lco_set_pad(boxes % 10);
+ ao_delay(AO_MS_TO_TICKS(30));
+ break;
+ }
+ }
+ }
+ if (ao_lco_min_box <= ao_lco_max_box)
+ ao_lco_box = ao_lco_min_box;
+ else
+ ao_lco_min_box = ao_lco_max_box = ao_lco_box = 0;
+ ao_lco_valid = 0;
+ ao_lco_got_channels = 0;
+ ao_lco_pad = 1;
+ ao_lco_set_display();
+}
+
+static void
+ao_lco_igniter_status(void)
+{
+ uint8_t c;
+
+ for (;;) {
+ ao_sleep(&ao_pad_query);
+ PRINTD("RSSI %d VALID %d\n", ao_radio_cmac_rssi, ao_lco_valid);
+ if (!ao_lco_valid) {
+ ao_led_on(AO_LED_RED);
+ ao_led_off(AO_LED_GREEN|AO_LED_AMBER);
+ continue;
+ }
+ if (ao_radio_cmac_rssi < -90) {
+ ao_led_on(AO_LED_AMBER);
+ ao_led_off(AO_LED_RED|AO_LED_GREEN);
+ } else {
+ ao_led_on(AO_LED_GREEN);
+ ao_led_off(AO_LED_RED|AO_LED_AMBER);
+ }
+ if (ao_pad_query.arm_status)
+ ao_led_on(AO_LED_REMOTE_ARM);
+ else
+ ao_led_off(AO_LED_REMOTE_ARM);
+ for (c = 0; c < AO_LED_CONTINUITY_NUM; c++) {
+ uint8_t status;
+
+ if (ao_pad_query.channels & (1 << c))
+ status = ao_pad_query.igniter_status[c];
+ else
+ status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN;
+ if (status == AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN)
+ ao_led_on(continuity_led[c]);
+ else
+ ao_led_off(continuity_led[c]);
+ }
+ }
+}
+
+static void
+ao_lco_arm_warn(void)
+{
+ for (;;) {
+ while (!ao_lco_armed)
+ ao_sleep(&ao_lco_armed);
+ ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
+ ao_delay(AO_MS_TO_TICKS(200));
+ }
+}
+
+static struct ao_task ao_lco_input_task;
+static struct ao_task ao_lco_monitor_task;
+static struct ao_task ao_lco_arm_warn_task;
+static struct ao_task ao_lco_igniter_status_task;
+
+static void
+ao_lco_monitor(void)
+{
+ uint16_t delay;
+
+ ao_lco_search();
+ ao_add_task(&ao_lco_input_task, ao_lco_input, "lco input");
+ ao_add_task(&ao_lco_arm_warn_task, ao_lco_arm_warn, "lco arm warn");
+ ao_add_task(&ao_lco_igniter_status_task, ao_lco_igniter_status, "lco igniter status");
+ for (;;) {
+ PRINTD("monitor armed %d firing %d offset %d\n",
+ ao_lco_armed, ao_lco_firing, ao_lco_tick_offset);
+
+ if (ao_lco_armed && ao_lco_firing) {
+ PRINTD("Firing box %d pad %d: valid %d\n",
+ ao_lco_box, ao_lco_pad, ao_lco_valid);
+ if (!ao_lco_valid)
+ ao_lco_update();
+ if (ao_lco_valid && ao_lco_pad)
+ ao_lco_ignite(ao_lco_box, 1 << (ao_lco_pad - 1), ao_lco_tick_offset);
+ } else if (ao_lco_armed) {
+ PRINTD("Arming box %d pad %d\n",
+ ao_lco_box, ao_lco_pad);
+ if (!ao_lco_valid)
+ ao_lco_update();
+ if (ao_lco_pad) {
+ ao_lco_arm(ao_lco_box, 1 << (ao_lco_pad - 1), ao_lco_tick_offset);
+ ao_delay(AO_MS_TO_TICKS(30));
+ ao_lco_update();
+ }
+ } else {
+ ao_lco_update();
+ }
+ if (ao_lco_armed && ao_lco_firing)
+ delay = AO_MS_TO_TICKS(100);
+ else
+ delay = AO_SEC_TO_TICKS(1);
+ ao_sleep_for(&ao_lco_armed, delay);
+ }
+}
+
+#if DEBUG
+void
+ao_lco_set_debug(void)
+{
+ ao_cmd_decimal();
+ if (ao_cmd_status == ao_cmd_success)
+ ao_lco_debug = ao_cmd_lex_i != 0;
+}
+
+__code struct ao_cmds ao_lco_cmds[] = {
+ { ao_lco_set_debug, "D <0 off, 1 on>\0Debug" },
+ { ao_lco_search, "s\0Search for pad boxes" },
+ { 0, NULL }
+};
+#endif
+
+void
+ao_lco_init(void)
+{
+ ao_add_task(&ao_lco_monitor_task, ao_lco_monitor, "lco monitor");
+#if DEBUG
+ ao_cmd_register(&ao_lco_cmds[0]);
+#endif
+}
--- /dev/null
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_LCO_H_
+#define _AO_LCO_H_
+
+void
+ao_lco_init(void);
+
+#endif /* _AO_LCO_H_ */
static __xdata uint8_t ao_pad_disabled;
static __pdata uint16_t ao_pad_packet_time;
+#ifndef AO_PAD_RSSI_MINIMUM
+#define AO_PAD_RSSI_MINIMUM -90
+#endif
+
#define DEBUG 1
#if DEBUG
#define PRINTD(...) (ao_pad_debug ? (printf(__VA_ARGS__), 0) : 0)
#define FLUSHD() (ao_pad_debug ? (flush(), 0) : 0)
#else
-#define PRINTD(...)
-#define FLUSHD()
+#define PRINTD(...)
+#define FLUSHD()
#endif
static void
#define VOLTS_TO_PYRO(x) ((int16_t) ((x) * 27.0 / 127.0 / 3.3 * 32767.0))
+ /* convert ADC value to voltage in tenths, then add .2 for the diode drop */
+ query.battery = (packet->adc.batt + 96) / 192 + 2;
cur = 0;
if (pyro > VOLTS_TO_PYRO(10)) {
query.arm_status = AO_PAD_ARM_STATUS_ARMED;
}
if ((ao_time() - ao_pad_packet_time) > AO_SEC_TO_TICKS(2))
cur |= AO_LED_RED;
- else if (ao_radio_cmac_rssi < -90)
+ else if (ao_radio_cmac_rssi < AO_PAD_RSSI_MINIMUM)
cur |= AO_LED_AMBER;
else
cur |= AO_LED_GREEN;
if (ret != AO_RADIO_CMAC_OK)
continue;
ao_pad_packet_time = ao_time();
-
+
ao_pad_box = ao_pad_read_box();
PRINTD ("tick %d box %d (me %d) cmd %d channels %02x\n",
uint8_t channels; /* which chanels are present */
uint8_t armed; /* which channels are armed */
uint8_t arm_status; /* status of arming switch */
+ uint8_t battery; /* battery voltage in decivolts */
uint8_t igniter_status[AO_PAD_MAX_CHANNELS]; /* status for each igniter */
};
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_adc_fast.h>
+#include <ao_crc.h>
+#include <ao_trng.h>
+
+static void
+ao_trng_fetch(void)
+{
+ static uint16_t *buffer[2];
+ uint32_t kbytes = 1;
+ uint32_t count;
+ int usb_buf_id;
+ uint16_t i;
+ uint16_t *buf;
+ uint16_t t;
+ uint32_t *rnd = (uint32_t *) ao_adc_ring;
+
+ if (!buffer[0]) {
+ buffer[0] = ao_usb_alloc();
+ buffer[1] = ao_usb_alloc();
+ if (!buffer[0])
+ return;
+ }
+
+ ao_cmd_decimal();
+ if (ao_cmd_status == ao_cmd_success)
+ kbytes = ao_cmd_lex_u32;
+ else
+ ao_cmd_status = ao_cmd_success;
+ usb_buf_id = 0;
+ count = kbytes * (1024/AO_USB_IN_SIZE);
+
+ ao_crc_reset();
+
+ ao_led_on(AO_LED_TRNG_READ);
+ while (count--) {
+ t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* one 16-bit value per output byte */
+ buf = buffer[usb_buf_id];
+ for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) {
+ *buf++ = ao_crc_in_32_out_16(rnd[t]);
+ t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1);
+ }
+ ao_adc_ack(AO_USB_IN_SIZE);
+ ao_led_toggle(AO_LED_TRNG_READ|AO_LED_TRNG_WRITE);
+ ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE);
+ ao_led_toggle(AO_LED_TRNG_READ|AO_LED_TRNG_WRITE);
+ usb_buf_id = 1-usb_buf_id;
+ }
+ ao_led_off(AO_LED_TRNG_READ|AO_LED_TRNG_WRITE);
+ flush();
+}
+
+static const struct ao_cmds ao_trng_cmds[] = {
+ { ao_trng_fetch, "f <kbytes>\0Fetch a block of numbers" },
+ { 0, NULL },
+};
+
+void
+ao_trng_init(void)
+{
+ ao_cmd_register(ao_trng_cmds);
+}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_TRNG_H_
+#define _AO_TRNG_H_
+
+void
+ao_trng_init(void);
+
+#endif /* _AO_TRNG_H_ */
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_adc_fast.h>
+#include <ao_crc.h>
+#include <ao_trng_send.h>
+
+static void
+ao_trng_send(void)
+{
+ static uint16_t *buffer[2];
+ int usb_buf_id;
+ uint16_t i;
+ uint16_t *buf;
+ uint16_t t;
+ uint32_t *rnd = (uint32_t *) ao_adc_ring;
+
+ if (!buffer[0]) {
+ buffer[0] = ao_usb_alloc();
+ buffer[1] = ao_usb_alloc();
+ if (!buffer[0])
+ return;
+ }
+
+ usb_buf_id = 0;
+
+ ao_crc_reset();
+
+ for (;;) {
+ ao_led_on(AO_LED_TRNG_ACTIVE);
+ t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* one 16-bit value per output byte */
+ buf = buffer[usb_buf_id];
+ for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) {
+ *buf++ = ao_crc_in_32_out_16(rnd[t]);
+ t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1);
+ }
+ ao_adc_ack(AO_USB_IN_SIZE);
+ ao_led_off(AO_LED_TRNG_ACTIVE);
+ ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE);
+ usb_buf_id = 1-usb_buf_id;
+ }
+}
+
+static struct ao_task ao_trng_send_task;
+
+void
+ao_trng_send_init(void)
+{
+ ao_add_task(&ao_trng_send_task, ao_trng_send, "trng_send");
+}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_TRNG_SEND_H_
+#define _AO_TRNG_SEND_H_
+
+void
+ao_trng_send_init(void);
+
+#endif /* _AO_TRNG_SEND_H_ */
#if HAS_RADIO_FORWARD
if (minor < 21)
ao_config.send_frequency = 434550;
+#endif
+#if HAS_APRS
+ if (minor < 22)
+ ao_config.aprs_format = AO_CONFIG_DEFAULT_APRS_FORMAT;
#endif
ao_config.minor = AO_CONFIG_MINOR;
ao_config_dirty = 1;
ao_config.aprs_ssid = ao_cmd_lex_i;
_ao_config_edit_finish();
}
+
+void
+ao_config_aprs_format_set(void)
+{
+ ao_cmd_decimal();
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ _ao_config_edit_start();
+ ao_config.aprs_format = ao_cmd_lex_i != 0;
+ _ao_config_edit_finish();
+}
+
+void
+ao_config_aprs_format_show(void)
+{
+ printf ("APRS format: %d\n", ao_config.aprs_format);
+}
#endif /* HAS_APRS */
struct ao_config_var {
#if HAS_APRS
{ "S <ssid>\0Set APRS SSID (0-15)",
ao_config_aprs_ssid_set, ao_config_aprs_ssid_show },
+ { "C <0 compressed, 1 uncompressed>\0APRS format",
+ ao_config_aprs_format_set, ao_config_aprs_format_show },
#endif
{ "s\0Show",
ao_config_show, 0 },
#endif
#define AO_CONFIG_MAJOR 1
-#define AO_CONFIG_MINOR 21
+#define AO_CONFIG_MINOR 22
#define AO_AES_LEN 16
#if HAS_RADIO_FORWARD
uint32_t send_frequency; /* minor version 21 */
#endif
+#if HAS_APRS
+ uint8_t aprs_format; /* minor version 22 */
+#endif
};
+#define AO_APRS_FORMAT_COMPRESSED 0
+#define AO_APRS_FORMAT_UNCOMPRESSED 1
+#define AO_CONFIG_DEFAULT_APRS_FORMAT AO_APRS_FORMAT_COMPRESSED
+
#if HAS_RADIO_FORWARD
extern __xdata uint32_t ao_send_radio_setting;
#endif
#define AO_USB_MAX_POWER 100
#endif
+#ifndef AO_USB_INTERFACE_CLASS
+#define AO_USB_INTERFACE_CLASS 0x02
+#endif
+
#include "ao_usb.h"
/* USB descriptors in one giant block of bytes */
AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] =
0x00, /* bDeviceSubClass */
0x00, /* bDeviceProtocol */
AO_USB_CONTROL_SIZE, /* bMaxPacketSize */
- LE_WORD(0xFFFE), /* idVendor */
+ LE_WORD(AO_idVendor_NUMBER), /* idVendor */
LE_WORD(AO_idProduct_NUMBER), /* idProduct */
LE_WORD(0x0100), /* bcdDevice */
0x01, /* iManufacturer */
0x00, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x01, /* bNumEndPoints */
- 0x02, /* bInterfaceClass */
+ AO_USB_INTERFACE_CLASS, /* bInterfaceClass */
0x02, /* bInterfaceSubClass */
0x01, /* bInterfaceProtocol, linux requires value of 1 for the cdc_acm module */
0x00, /* iInterface */
return AO_RADIO_CMAC_TIMEOUT;
}
- ao_radio_cmac_rssi = ao_radio_rssi;
if (!(cmac_data[len + AO_CMAC_KEY_LEN +1] & AO_RADIO_STATUS_CRC_OK))
return AO_RADIO_CMAC_CRC_ERROR;
/* Check the packet signature against the signature provided
* over the link
*/
-
+
if (memcmp(&cmac_data[len],
&cmac_data[len + AO_CMAC_KEY_LEN + 2],
AO_CMAC_KEY_LEN) != 0) {
return AO_RADIO_CMAC_MAC_ERROR;
}
+ ao_radio_cmac_rssi = ao_radio_rssi;
+
return AO_RADIO_CMAC_OK;
}
ao_mutex_put(&ao_radio_cmac_mutex);
return i;
}
-
uint16_t serial; /* 0 */
uint16_t tick; /* 2 */
uint8_t type; /* 4 */
+ uint8_t pad5[3]; /* 5 */
- int32_t ground_pres; /* 8 average pres on pad */
+ int32_t ground_pres; /* 8 average pres on pad */
int16_t ground_accel; /* 12 average accel on pad */
int16_t accel_plus_g; /* 14 accel calibration at +1g */
int16_t accel_minus_g; /* 16 accel calibration at -1g */
- uint8_t pad[14]; /* 18 */
+ uint8_t pad18[14]; /* 18 */
/* 32 */
};
struct ao_telemetry_baro baro;
};
+typedef char ao_check_telemetry_size[sizeof(union ao_telemetry_all) == 32 ? 1 : -1];
+
struct ao_telemetry_all_recv {
union ao_telemetry_all telemetry;
int8_t rssi;
LOADFAST=""
case "$1" in
-fast)
- LOADSPEED="$LOADFAST"
+slow)
+ LOADSPEED="$LOADSLOW"
;;
*)
- LOADSPEED="$LOADSLOW"
+ LOADSPEED="$LOADFAST"
;;
esac
echo ${LOADCMD} ${LOADSPEED} ${LOADARG}${HEX}
${LOADCMD} ${LOADSPEED} ${LOADARG}${HEX}
+/usr/games/xcowsay --cow-size=large --at=1000,500 "${HEX} finished"
ao_product.h
-microsplash-*
+microsplash-v*
+microsplash-load
include ../avr/Makefile.defs
+PROGNAME=microsplash-v1.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SCRIPT=microsplash-load
+
PUBLISH_DIR=$(HOME)/altusmetrumllc/Binaries
-PUBLISH_FILE=$(PUBLISH_DIR)/$(PROG)-$(VERSION).hex
+PUBLISH_HEX=$(PUBLISH_DIR)/$(HEX)
+PUBLISH_SCRIPT=$(PUBLISH_DIR)/$(SCRIPT)
MCU=attiny85
DUDECPUTYPE=t85
altitude-pa.h
IDPRODUCT=0
-PRODUCT=MicroSplash-v0.1
+PRODUCT=MicroSplash-v1.0
PRODUCT_DEF=-DMICROPEAK
CFLAGS = $(PRODUCT_DEF) -I. -I../attiny -I../kernel -I.. -I../drivers -I../product
CFLAGS += -g -mmcu=$(MCU) -Wall -Wstrict-prototypes -O2 -mcall-prologues -DATTINY
NICKLE=nickle
-PROG=microsplash-v1.0
-
SRC=$(ALTOS_SRC)
OBJ=$(SRC:.c=.o)
# Otherwise, print the full command line.
quiet ?= $($1)
-all: $(PROG) $(PROG).hex
+all: $(PROG) $(HEX) $(SCRIPT)
CHECK=sh ../util/check-avr-mem
$(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ)
$(call quiet,CHECK) $(PROG) || ($(RM) -f $(PROG); exit 1)
-$(PROG).hex: $(PROG)
+$(HEX): $(PROG)
avr-size $(PROG)
$(OBJCOPY) -R .eeprom -O ihex $(PROG) $@
-load: $(PROG).hex
- $(LOADCMD) $(LOADARG)$(PROG).hex
+load: $(HEX)
+ $(LOADCMD) $(LOADARG)$(HEX)
-load-slow: $(PROG).hex
- $(LOADCMD) $(LOADSLOW) $(LOADARG)$(PROG).hex
+load-slow: $(HEX)
+ $(LOADCMD) $(LOADSLOW) $(LOADARG)$(HEX)
ao_product.h: ao-make-product.5c ../Version
$(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
distclean: clean
clean:
- rm -f *.o $(PROG) $(PROG).hex
+ rm -f *.o $(PROG) $(HEX) $(SCRIPT)
rm -f ao_product.h
+publish: $(PUBLISH_HEX) $(PUBLISH_SCRIPT)
-publish: $(PROG).hex
- cp -a $(PROG).hex $(PUBLISH_FILE)
+$(PUBLISH_HEX): $(HEX)
+ cp -a $(HEX) $@
+
+$(PUBLISH_SCRIPT): $(SCRIPT)
+ cp -a $(SCRIPT) $@
load-product:
- $(LOADCMD) $(LOADARG)$(PUBLISH_FILE)
+ ./$(SCRIPT) fast
load-product-slow:
- $(LOADCMD) $(LOADSLOW) $(LOADARG)$(PUBLISH_FILE)
+ ./$(SCRIPT) slow
../altitude-pa.h: make-altitude-pa
nickle $< > $@
+$(SCRIPT): $(SCRIPT).tmpl Makefile ../Version
+ sed -e 's/%HEX%/$(HEX)/' -e 's/%LOADCMD%/$(LOADCMD)/' -e 's/%LOADARG%/$(LOADARG)/' -e 's/%LOADSLOW%/$(LOADSLOW)/' $(SCRIPT).tmpl > $@ || (rm $@ && exit 1)
+ chmod +x $@
+
install:
uninstall:
--- /dev/null
+#!/bin/sh
+dir=`dirname $0`
+
+HEX="$dir"/"%HEX%"
+LOADCMD="%LOADCMD%"
+LOADARG="%LOADARG%"
+LOADSLOW="%LOADSLOW%"
+LOADFAST=""
+
+case "$1" in
+slow)
+ LOADSPEED="$LOADSLOW"
+ ;;
+*)
+ LOADSPEED="$LOADFAST"
+ ;;
+esac
+
+echo ${LOADCMD} ${LOADSPEED} ${LOADARG}${HEX}
+${LOADCMD} ${LOADSPEED} ${LOADARG}${HEX}
+/usr/games/xcowsay --cow-size=large --at=1000,500 "${HEX} finished"
#include <ao.h>
#include <ao_adc_fast.h>
-uint16_t ao_adc_ring[AO_ADC_RING_SIZE];
+uint16_t ao_adc_ring[AO_ADC_RING_SIZE] __attribute__((aligned(4)));
-uint16_t ao_adc_ring_head, ao_adc_ring_tail;
-uint8_t ao_adc_running;
+/* Maximum number of samples fetched per _ao_adc_start call */
+#define AO_ADC_RING_CHUNK (AO_ADC_RING_SIZE >> 1)
+
+uint16_t ao_adc_ring_head, ao_adc_ring_remain;
+uint16_t ao_adc_running;
/*
* Callback from DMA ISR
*
- * Mark time in ring, shut down DMA engine
+ * Wakeup any waiting processes, mark the DMA as done, start the ADC
+ * if there's still lots of space in the ring
*/
static void ao_adc_dma_done(int index)
{
(void) index;
- ao_adc_ring_head += AO_ADC_RING_CHUNK;
+ ao_adc_ring_head += ao_adc_running;
+ ao_adc_ring_remain += ao_adc_running;
if (ao_adc_ring_head == AO_ADC_RING_SIZE)
ao_adc_ring_head = 0;
ao_adc_running = 0;
ao_wakeup(&ao_adc_ring_head);
ao_dma_done_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1));
+ _ao_adc_start();
}
void
_ao_adc_start(void)
{
uint16_t *buf;
+ uint16_t count;
if (ao_adc_running)
return;
- if (_ao_adc_space() < AO_ADC_RING_CHUNK)
+ count = _ao_adc_space();
+ if (count == 0)
return;
- ao_adc_running = 1;
+ if (count > AO_ADC_RING_CHUNK)
+ count = AO_ADC_RING_CHUNK;
+ ao_adc_running = count;
buf = ao_adc_ring + ao_adc_ring_head;
stm_adc.isr = 0;
ao_dma_set_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1),
&stm_adc.dr,
buf,
- AO_ADC_RING_CHUNK,
+ count,
(0 << STM_DMA_CCR_MEM2MEM) |
(STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
(STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) |
#if AO_NUM_ADC > 8
#error Need more ADC defines
#endif
- stm_adc.chselr = chselr;
/* Set the clock */
stm_adc.cfgr2 = STM_ADC_CFGR2_CKMODE_PCLK_2 << STM_ADC_CFGR2_CKMODE;
while ((stm_adc.isr & (1 << STM_ADC_ISR_ADRDY)) == 0)
;
+ stm_adc.chselr = chselr;
+
stm_adc.cfgr1 = ((0 << STM_ADC_CFGR1_AWDCH) |
(0 << STM_ADC_CFGR1_AWDEN) |
(0 << STM_ADC_CFGR1_AWDSGL) |
(0 << STM_ADC_CFGR1_DISCEN) |
(0 << STM_ADC_CFGR1_AUTOOFF) |
- (1 << STM_ADC_CFGR1_WAIT) |
+ (0 << STM_ADC_CFGR1_WAIT) |
(1 << STM_ADC_CFGR1_CONT) |
- (0 << STM_ADC_CFGR1_OVRMOD) |
+ (1 << STM_ADC_CFGR1_OVRMOD) |
(STM_ADC_CFGR1_EXTEN_DISABLE << STM_ADC_CFGR1_EXTEN) |
(0 << STM_ADC_CFGR1_ALIGN) |
(STM_ADC_CFGR1_RES_12 << STM_ADC_CFGR1_RES) |
stm_syscfg.cfgr1 &= ~(1 << STM_SYSCFG_CFGR1_ADC_DMA_RMP);
ao_dma_alloc(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1));
- ao_dma_set_isr(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1), ao_adc_dma_done);
}
/* Total ring size in samples */
#define AO_ADC_RING_SIZE 256
-/* Number of samples fetched per ao_adc_start call */
-#define AO_ADC_RING_CHUNK (AO_ADC_RING_SIZE >> 1)
extern uint16_t ao_adc_ring[AO_ADC_RING_SIZE];
#define ao_adc_ring_step(pos,inc) (((pos) + (inc)) & (AO_ADC_RING_SIZE - 1))
-extern uint16_t ao_adc_ring_head, ao_adc_ring_tail;
-extern uint8_t ao_adc_running;
-
-void
-_ao_adc_start(void);
+extern uint16_t ao_adc_ring_head, ao_adc_ring_remain;
+extern uint16_t ao_adc_running;
+/*
+ * Place to start fetching values from
+ */
static inline uint16_t
-_ao_adc_remain(void)
+ao_adc_ring_tail(void)
{
- if (ao_adc_ring_tail > ao_adc_ring_head)
- return AO_ADC_RING_SIZE - ao_adc_ring_tail;
- return ao_adc_ring_head - ao_adc_ring_tail;
+ return (ao_adc_ring_head - ao_adc_ring_remain) & (AO_ADC_RING_SIZE - 1);
}
+void
+_ao_adc_start(void);
+
+/*
+ * Space available to write ADC values into
+ */
static inline uint16_t
_ao_adc_space(void)
{
- if (ao_adc_ring_head == ao_adc_ring_tail)
- return AO_ADC_RING_SIZE;
- if (ao_adc_ring_head > ao_adc_ring_tail)
+ /* Free to end of buffer? */
+ if (ao_adc_ring_remain <= ao_adc_ring_head)
return AO_ADC_RING_SIZE - ao_adc_ring_head;
- return ao_adc_ring_tail - ao_adc_ring_head;
+
+ /* no, return just the unused entries beyond head */
+ return AO_ADC_RING_SIZE - ao_adc_ring_remain;
}
-static inline uint16_t *
+static inline uint16_t
ao_adc_get(uint16_t n)
{
- if (ao_adc_ring_tail + n > AO_ADC_RING_SIZE)
- ao_panic(AO_PANIC_ADC);
ao_arch_block_interrupts();
- while (_ao_adc_remain() < n) {
+ while (ao_adc_ring_remain < n) {
if (!ao_adc_running)
_ao_adc_start();
ao_sleep(&ao_adc_ring_head);
}
ao_arch_release_interrupts();
- return &ao_adc_ring[ao_adc_ring_tail];
+ return ao_adc_ring_tail();
}
static inline void
ao_adc_ack(uint16_t n)
{
- if (ao_adc_ring_tail + n > AO_ADC_RING_SIZE)
- ao_panic(AO_PANIC_ADC);
ao_arch_block_interrupts();
- ao_adc_ring_tail += n;
- if (ao_adc_ring_tail == AO_ADC_RING_SIZE)
- ao_adc_ring_tail = 0;
- if (!ao_adc_running && _ao_adc_space() >= AO_ADC_RING_CHUNK)
+ ao_adc_ring_remain -= n;
+ if (!ao_adc_running)
_ao_adc_start();
ao_arch_release_interrupts();
}
#endif
#ifndef AO_CRC_INIT
-#define AO_CRC_INIT 0xffffffff;
+#define AO_CRC_INIT 0xffffffff
#endif
void
--- /dev/null
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_EXTI_H_
+#define _AO_EXTI_H_
+
+#define AO_EXTI_MODE_RISING 1
+#define AO_EXTI_MODE_FALLING 2
+#define AO_EXTI_MODE_PULL_UP 4
+#define AO_EXTI_MODE_PULL_DOWN 8
+#define AO_EXTI_PRIORITY_LOW 16
+#define AO_EXTI_PRIORITY_MED 0
+#define AO_EXTI_PRIORITY_HIGH 32
+#define AO_EXTI_PIN_NOCONFIGURE 64
+
+void
+ao_exti_setup(struct stm_gpio *gpio, uint8_t pin, uint8_t mode, void (*callback)());
+
+void
+ao_exti_set_mode(struct stm_gpio *gpio, uint8_t pin, uint8_t mode);
+
+void
+ao_exti_set_callback(struct stm_gpio *gpio, uint8_t pin, void (*callback)());
+
+void
+ao_exti_enable(struct stm_gpio *gpio, uint8_t pin);
+
+void
+ao_exti_disable(struct stm_gpio *gpio, uint8_t pin);
+
+void
+ao_exti_init(void);
+
+#endif /* _AO_EXTI_H_ */
static uint16_t *ao_usb_ep0_tx_buffer;
static uint16_t *ao_usb_ep0_rx_buffer;
+/* Pointer to interrupt buffer in USB memory */
+static uint16_t ao_usb_int_tx_offset;
+
/* Pointer to bulk data tx/rx buffers in USB memory */
static uint16_t ao_usb_in_tx_offset;
static uint16_t *ao_usb_in_tx_buffer;
+static uint16_t ao_usb_out_rx_offset;
static uint16_t *ao_usb_out_rx_buffer;
/* System ram shadow of USB buffer; writing individual bytes is
return (uint16_t *) (stm_usb_sram + sram_addr);
}
-#if AO_USB_DIRECTIO
static inline uint16_t ao_usb_packet_buffer_offset(uint16_t *addr)
{
return (uint16_t) ((uint8_t *) addr - stm_usb_sram);
}
-#endif
static inline uint32_t ao_usb_epr_stat_rx(uint32_t epr) {
return (epr >> STM_USB_EPR_STAT_RX) & STM_USB_EPR_STAT_RX_MASK;
}
static void
-ao_usb_init_btable(void)
+ao_usb_alloc_buffers(void)
{
ao_usb_sram_addr = 0;
ao_usb_bdt = (void *) stm_usb_sram;
-
ao_usb_sram_addr += 8 * STM_USB_BDT_SIZE;
- /* Set up EP 0 - a Control end point with 32 bytes of in and out buffers */
-
- ao_usb_bdt[0].single.addr_tx = ao_usb_sram_addr;
- ao_usb_bdt[0].single.count_tx = 0;
ao_usb_ep0_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
ao_usb_sram_addr += AO_USB_CONTROL_SIZE;
- ao_usb_bdt[0].single.addr_rx = ao_usb_sram_addr;
- ao_usb_bdt[0].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
- (((AO_USB_CONTROL_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
ao_usb_ep0_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
ao_usb_sram_addr += AO_USB_CONTROL_SIZE;
+ ao_usb_int_tx_offset = ao_usb_sram_addr;
+ ao_usb_sram_addr += AO_USB_INT_SIZE;
+
+ ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_out_rx_offset = ao_usb_sram_addr;
+ ao_usb_sram_addr += AO_USB_OUT_SIZE;
+
+ ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_in_tx_offset = ao_usb_sram_addr;
+ ao_usb_sram_addr += AO_USB_IN_SIZE;
+}
+
+static void
+ao_usb_init_btable(void)
+{
+ /* Set up EP 0 - a Control end point with 32 bytes of in and out buffers */
+
+ ao_usb_bdt[0].single.addr_tx = ao_usb_packet_buffer_offset(ao_usb_ep0_tx_buffer);
+ ao_usb_bdt[0].single.count_tx = 0;
+
+ ao_usb_bdt[0].single.addr_rx = ao_usb_packet_buffer_offset(ao_usb_ep0_rx_buffer);
+ ao_usb_bdt[0].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
+ (((AO_USB_CONTROL_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
}
static void
}
ao_usb_set_address(0);
+
+ ao_usb_running = 0;
}
static void
debug ("ao_usb_set_configuration\n");
/* Set up the INT end point */
- ao_usb_bdt[AO_USB_INT_EPR].single.addr_tx = ao_usb_sram_addr;
+ ao_usb_bdt[AO_USB_INT_EPR].single.addr_tx = ao_usb_int_tx_offset;
ao_usb_bdt[AO_USB_INT_EPR].single.count_tx = 0;
- ao_usb_sram_addr += AO_USB_INT_SIZE;
ao_usb_init_ep(AO_USB_INT_EPR,
AO_USB_INT_EP,
STM_USB_EPR_STAT_TX_NAK);
/* Set up the OUT end point */
- ao_usb_bdt[AO_USB_OUT_EPR].single.addr_rx = ao_usb_sram_addr;
+ ao_usb_bdt[AO_USB_OUT_EPR].single.addr_rx = ao_usb_out_rx_offset;
ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
(((AO_USB_OUT_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
- ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
- ao_usb_sram_addr += AO_USB_OUT_SIZE;
ao_usb_init_ep(AO_USB_OUT_EPR,
AO_USB_OUT_EP,
STM_USB_EPR_STAT_TX_DISABLED);
/* Set up the IN end point */
- ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_sram_addr;
+ ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_in_tx_offset;
ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = 0;
- ao_usb_in_tx_offset = ao_usb_sram_addr;
- ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_in_tx_offset);
- ao_usb_sram_addr += AO_USB_IN_SIZE;
ao_usb_init_ep(AO_USB_IN_EPR,
AO_USB_IN_EP,
STM_USB_EPR_STAT_TX_NAK);
ao_usb_running = 1;
+#if AO_USB_DIRECTIO
+ ao_wakeup(&ao_usb_running);
+#endif
}
static uint16_t control_count;
{
uint16_t *buffer;
- if (!ao_usb_running)
- return NULL;
buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
ao_usb_sram_addr += AO_USB_IN_SIZE;
return buffer;
{
ao_arch_block_interrupts();
- /* Flush any pending regular */
- if (ao_usb_tx_count)
- _ao_usb_in_send();
+ /* Wait for everything to be ready at the same time */
+ for (;;) {
+ /* Make sure USB is connected */
+ if (!ao_usb_running) {
+ ao_sleep(&ao_usb_running);
+ continue;
+ }
+
+ /* Flush any pending regular I/O */
+ if (ao_usb_tx_count) {
+ _ao_usb_in_send();
+ continue;
+ }
+
+ /* Wait for an idle IN buffer */
+ if (ao_usb_in_pending) {
+ ao_sleep(&ao_usb_in_pending);
+ continue;
+ }
+ break;
+ }
- while (ao_usb_in_pending)
- ao_sleep(&ao_usb_in_pending);
ao_usb_in_pending = 1;
ao_usb_in_flushed = (len != AO_USB_IN_SIZE);
ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_packet_buffer_offset(buffer);
debug ("ao_usb_init\n");
ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+ ao_usb_alloc_buffers();
+
#if USB_ECHO
ao_add_task(&ao_usb_echo_task, ao_usb_echo, "usb echo");
#endif
};
#define AO_ADC_DUMP(p) \
- printf("tick: %5u %5d batt: %5d\n", \
+ printf("tick: %5u batt %5d\n", \
(p)->tick, \
(p)->adc.v_batt);
+++ /dev/null
-/*
- * Copyright © 2012 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include <ao.h>
-#include <ao_lco.h>
-#include <ao_event.h>
-#include <ao_seven_segment.h>
-#include <ao_quadrature.h>
-#include <ao_lco_func.h>
-#include <ao_radio_cmac.h>
-
-#define DEBUG 1
-
-#if DEBUG
-static uint8_t ao_lco_debug;
-#define PRINTD(...) do { if (!ao_lco_debug) break; printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
-#else
-#define PRINTD(...)
-#endif
-
-#define AO_LCO_PAD_DIGIT 0
-#define AO_LCO_BOX_DIGIT_1 1
-#define AO_LCO_BOX_DIGIT_10 2
-
-static uint8_t ao_lco_min_box, ao_lco_max_box;
-static uint8_t ao_lco_pad;
-static uint8_t ao_lco_box;
-static uint8_t ao_lco_armed;
-static uint8_t ao_lco_firing;
-static uint8_t ao_lco_valid;
-static uint8_t ao_lco_got_channels;
-static uint16_t ao_lco_tick_offset;
-
-static struct ao_pad_query ao_pad_query;
-
-static void
-ao_lco_set_pad(uint8_t pad)
-{
- ao_seven_segment_set(AO_LCO_PAD_DIGIT, pad + 1);
-}
-
-static void
-ao_lco_set_box(uint8_t box)
-{
- ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, box % 10);
- ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, box / 10);
-}
-
-#define MASK_SIZE(n) (((n) + 7) >> 3)
-#define MASK_ID(n) ((n) >> 3)
-#define MASK_SHIFT(n) ((n) & 7)
-
-static uint8_t ao_lco_box_mask[MASK_SIZE(AO_PAD_MAX_BOXES)];
-
-static uint8_t
-ao_lco_box_present(uint8_t box)
-{
- if (box >= AO_PAD_MAX_BOXES)
- return 0;
- return (ao_lco_box_mask[MASK_ID(box)] >> MASK_SHIFT(box)) & 1;
-}
-
-static uint8_t
-ao_lco_pad_present(uint8_t pad)
-{
- if (!ao_lco_got_channels || !ao_pad_query.channels)
- return pad == 0;
- if (pad >= AO_PAD_MAX_CHANNELS)
- return 0;
- return (ao_pad_query.channels >> pad) & 1;
-}
-
-static uint8_t
-ao_lco_pad_first(void)
-{
- uint8_t pad;
-
- for (pad = 0; pad < AO_PAD_MAX_CHANNELS; pad++)
- if (ao_lco_pad_present(pad))
- return pad;
- return 0;
-}
-
-static void
-ao_lco_input(void)
-{
- static struct ao_event event;
- int8_t dir, new_box, new_pad;
-
- ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
- for (;;) {
- ao_event_get(&event);
- PRINTD("event type %d unit %d value %d\n",
- event.type, event.unit, event.value);
- switch (event.type) {
- case AO_EVENT_QUADRATURE:
- switch (event.unit) {
- case AO_QUADRATURE_PAD:
- if (!ao_lco_armed) {
- dir = (int8_t) event.value;
- new_pad = ao_lco_pad;
- do {
- new_pad += dir;
- if (new_pad > AO_PAD_MAX_CHANNELS)
- new_pad = 0;
- else if (new_pad < 0)
- new_pad = AO_PAD_MAX_CHANNELS - 1;
- if (new_pad == ao_lco_pad)
- break;
- } while (!ao_lco_pad_present(new_pad));
- if (new_pad != ao_lco_pad) {
- ao_lco_pad = new_pad;
- ao_lco_set_pad(ao_lco_pad);
- }
- }
- break;
- case AO_QUADRATURE_BOX:
- if (!ao_lco_armed) {
- dir = (int8_t) event.value;
- new_box = ao_lco_box;
- do {
- new_box += dir;
- if (new_box > ao_lco_max_box)
- new_box = ao_lco_min_box;
- else if (new_box < ao_lco_min_box)
- new_box = ao_lco_max_box;
- if (new_box == ao_lco_box)
- break;
- } while (!ao_lco_box_present(new_box));
- if (ao_lco_box != new_box) {
- ao_lco_box = new_box;
- ao_lco_got_channels = 0;
- ao_lco_set_box(ao_lco_box);
- }
- }
- break;
- }
- break;
- case AO_EVENT_BUTTON:
- switch (event.unit) {
- case AO_BUTTON_ARM:
- ao_lco_armed = event.value;
- PRINTD("Armed %d\n", ao_lco_armed);
- ao_wakeup(&ao_lco_armed);
- break;
- case AO_BUTTON_FIRE:
- if (ao_lco_armed) {
- ao_lco_firing = event.value;
- PRINTD("Firing %d\n", ao_lco_firing);
- ao_wakeup(&ao_lco_armed);
- }
- break;
- }
- break;
- }
- }
-}
-
-static AO_LED_TYPE continuity_led[AO_LED_CONTINUITY_NUM] = {
-#ifdef AO_LED_CONTINUITY_0
- AO_LED_CONTINUITY_0,
-#endif
-#ifdef AO_LED_CONTINUITY_1
- AO_LED_CONTINUITY_1,
-#endif
-#ifdef AO_LED_CONTINUITY_2
- AO_LED_CONTINUITY_2,
-#endif
-#ifdef AO_LED_CONTINUITY_3
- AO_LED_CONTINUITY_3,
-#endif
-#ifdef AO_LED_CONTINUITY_4
- AO_LED_CONTINUITY_4,
-#endif
-#ifdef AO_LED_CONTINUITY_5
- AO_LED_CONTINUITY_5,
-#endif
-#ifdef AO_LED_CONTINUITY_6
- AO_LED_CONTINUITY_6,
-#endif
-#ifdef AO_LED_CONTINUITY_7
- AO_LED_CONTINUITY_7,
-#endif
-};
-
-static void
-ao_lco_update(void)
-{
- int8_t r;
- uint8_t c;
-
- r = ao_lco_query(ao_lco_box, &ao_pad_query, &ao_lco_tick_offset);
- if (r == AO_RADIO_CMAC_OK) {
- c = ao_lco_got_channels;
- ao_lco_got_channels = 1;
- ao_lco_valid = 1;
- if (!c) {
- ao_lco_pad = ao_lco_pad_first();
- ao_lco_set_pad(ao_lco_pad);
- }
- } else
- ao_lco_valid = 0;
-
-#if 0
- PRINTD("lco_query success arm_status %d i0 %d i1 %d i2 %d i3 %d\n",
- query.arm_status,
- query.igniter_status[0],
- query.igniter_status[1],
- query.igniter_status[2],
- query.igniter_status[3]);
-#endif
- ao_wakeup(&ao_pad_query);
-}
-
-static void
-ao_lco_box_reset_present(void)
-{
- ao_lco_min_box = 0xff;
- ao_lco_max_box = 0x00;
- memset(ao_lco_box_mask, 0, sizeof (ao_lco_box_mask));
-}
-
-static void
-ao_lco_box_set_present(uint8_t box)
-{
- if (box < ao_lco_min_box)
- ao_lco_min_box = box;
- if (box > ao_lco_max_box)
- ao_lco_max_box = box;
- if (box >= AO_PAD_MAX_BOXES)
- return;
- ao_lco_box_mask[MASK_ID(box)] |= 1 << MASK_SHIFT(box);
-}
-
-static void
-ao_lco_search(void)
-{
- uint16_t tick_offset;
- int8_t r;
- int8_t try;
- uint8_t box;
-
- ao_lco_box_reset_present();
- for (box = 0; box < AO_PAD_MAX_BOXES; box++) {
- if ((box % 10) == 0)
- ao_lco_set_box(box);
- for (try = 0; try < 3; try++) {
- tick_offset = 0;
- r = ao_lco_query(box, &ao_pad_query, &tick_offset);
- PRINTD("box %d result %d\n", box, r);
- if (r == AO_RADIO_CMAC_OK) {
- ao_lco_box_set_present(box);
- ao_delay(AO_MS_TO_TICKS(30));
- break;
- }
- }
- }
- if (ao_lco_min_box <= ao_lco_max_box)
- ao_lco_box = ao_lco_min_box;
- else
- ao_lco_min_box = ao_lco_max_box = ao_lco_box = 0;
- ao_lco_valid = 0;
- ao_lco_got_channels = 0;
- ao_lco_pad = 0;
- ao_lco_set_pad(ao_lco_pad);
- ao_lco_set_box(ao_lco_box);
-}
-
-static void
-ao_lco_igniter_status(void)
-{
- uint8_t c;
-
- for (;;) {
- ao_sleep(&ao_pad_query);
- if (!ao_lco_valid) {
- ao_led_on(AO_LED_RED);
- ao_led_off(AO_LED_GREEN|AO_LED_AMBER);
- continue;
- }
- PRINTD("RSSI %d\n", ao_radio_cmac_rssi);
- if (ao_radio_cmac_rssi < -90) {
- ao_led_on(AO_LED_AMBER);
- ao_led_off(AO_LED_RED|AO_LED_GREEN);
- } else {
- ao_led_on(AO_LED_GREEN);
- ao_led_off(AO_LED_RED|AO_LED_AMBER);
- }
- if (ao_pad_query.arm_status)
- ao_led_on(AO_LED_REMOTE_ARM);
- else
- ao_led_off(AO_LED_REMOTE_ARM);
- for (c = 0; c < AO_LED_CONTINUITY_NUM; c++) {
- uint8_t status;
-
- if (ao_pad_query.channels & (1 << c))
- status = ao_pad_query.igniter_status[c];
- else
- status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN;
- if (status == AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN)
- ao_led_on(continuity_led[c]);
- else
- ao_led_off(continuity_led[c]);
- }
- }
-}
-
-static void
-ao_lco_arm_warn(void)
-{
- for (;;) {
- while (!ao_lco_armed)
- ao_sleep(&ao_lco_armed);
- ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
- ao_delay(AO_MS_TO_TICKS(200));
- }
-}
-
-static struct ao_task ao_lco_input_task;
-static struct ao_task ao_lco_monitor_task;
-static struct ao_task ao_lco_arm_warn_task;
-static struct ao_task ao_lco_igniter_status_task;
-
-static void
-ao_lco_monitor(void)
-{
- uint16_t delay;
-
- ao_lco_search();
- ao_add_task(&ao_lco_input_task, ao_lco_input, "lco input");
- ao_add_task(&ao_lco_arm_warn_task, ao_lco_arm_warn, "lco arm warn");
- ao_add_task(&ao_lco_igniter_status_task, ao_lco_igniter_status, "lco igniter status");
- for (;;) {
- PRINTD("monitor armed %d firing %d offset %d\n",
- ao_lco_armed, ao_lco_firing, ao_lco_tick_offset);
-
- if (ao_lco_armed && ao_lco_firing) {
- PRINTD("Firing box %d pad %d: valid %d\n",
- ao_lco_box, ao_lco_pad, ao_lco_valid);
- if (!ao_lco_valid)
- ao_lco_update();
- if (ao_lco_valid)
- ao_lco_ignite(ao_lco_box, 1 << ao_lco_pad, ao_lco_tick_offset);
- } else if (ao_lco_armed) {
- PRINTD("Arming box %d pad %d\n",
- ao_lco_box, ao_lco_pad);
- if (!ao_lco_valid)
- ao_lco_update();
- ao_lco_arm(ao_lco_box, 1 << ao_lco_pad, ao_lco_tick_offset);
- ao_lco_update();
- } else {
- ao_lco_update();
- }
- if (ao_lco_armed && ao_lco_firing)
- delay = AO_MS_TO_TICKS(100);
- else
- delay = AO_SEC_TO_TICKS(1);
- ao_sleep_for(&ao_lco_armed, delay);
- }
-}
-
-#if DEBUG
-void
-ao_lco_set_debug(void)
-{
- ao_cmd_decimal();
- if (ao_cmd_status == ao_cmd_success)
- ao_lco_debug = ao_cmd_lex_i != 0;
-}
-
-__code struct ao_cmds ao_lco_cmds[] = {
- { ao_lco_set_debug, "D <0 off, 1 on>\0Debug" },
- { ao_lco_search, "s\0Search for pad boxes" },
- { 0, NULL }
-};
-#endif
-
-void
-ao_lco_init(void)
-{
- ao_add_task(&ao_lco_monitor_task, ao_lco_monitor, "lco monitor");
-#if DEBUG
- ao_cmd_register(&ao_lco_cmds[0]);
-#endif
-}
+++ /dev/null
-/*
- * Copyright © 2012 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#ifndef _AO_LCO_H_
-#define _AO_LCO_H_
-
-void
-ao_lco_init(void);
-
-#endif /* _AO_LCO_H_ */
--- /dev/null
+ao_product.h
+telelco*.elf
--- /dev/null
+#
+# AltOS build for TeleLCO
+#
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_boot.h \
+ ao_companion.h \
+ ao_data.h \
+ ao_sample.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_seven_segment.h \
+ ao_lco.h \
+ ao_lco_cmd.h \
+ ao_lco_func.h \
+ ao_radio_spi.h \
+ ao_radio_cmac.h \
+ ao_cc1200_CC1200.h \
+ ao_cc1200.h \
+ ao_debounce.h \
+ stm32l.h
+
+#
+# Common AltOS sources
+#
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+ALTOS_SRC = \
+ ao_boot_chain.c \
+ ao_interrupt.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cmd.c \
+ ao_config.c \
+ ao_task.c \
+ ao_led.c \
+ ao_stdio.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_mutex.c \
+ ao_freq.c \
+ ao_dma_stm.c \
+ ao_spi_stm.c \
+ ao_beep_stm.c \
+ ao_eeprom_stm.c \
+ ao_fast_timer.c \
+ ao_lcd_stm.c \
+ ao_usb_stm.c \
+ ao_exti_stm.c \
+ ao_cc1200.c \
+ ao_radio_cmac.c \
+ ao_aes.c \
+ ao_aes_tables.c \
+ ao_fec_tx.c \
+ ao_fec_rx.c \
+ ao_seven_segment.c \
+ ao_debounce.c \
+ ao_quadrature.c \
+ ao_button.c \
+ ao_event.c \
+ ao_lco.c \
+ ao_lco_cmd.c \
+ ao_lco_func.c \
+ ao_radio_cmac_cmd.c
+
+PRODUCT=TeleLCO-v0.3
+PRODUCT_DEF=-DTELELCO
+IDPRODUCT=0x0023
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) -Os -g
+
+PROGNAME=telelco-v0.3
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_telelco.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+../altitude.h: make-altitude
+ nickle $< > $@
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f ao_product.h
+
+install:
+
+uninstall:
--- /dev/null
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* 8MHz High speed external crystal */
+#define AO_HSE 8000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL 12
+#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12)
+
+#define AO_CC1200_FOSC 40000000
+
+/* SYSCLK = 32MHz (no need to go faster than CPU) */
+#define AO_PLLDIV 3
+#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHz (CPU clock) */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at 16MHz (HCLK/2) */
+#define AO_APB1_PRESCALER 2
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+/* Run APB2 at 16MHz (HCLK/2) */
+#define AO_APB2_PRESCALER 2
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+#define HAS_EEPROM 1
+#define USE_INTERNAL_FLASH 1
+#define USE_EEPROM_CONFIG 1
+#define USE_STORAGE_CONFIG 0
+#define HAS_USB 1
+#define HAS_BEEP 1
+#define HAS_RADIO 1
+#define HAS_RADIO_RATE 1
+#define HAS_TELEMETRY 0
+#define HAS_AES 1
+
+#define HAS_SPI_1 0
+#define SPI_1_PA5_PA6_PA7 0
+#define SPI_1_PB3_PB4_PB5 0
+#define SPI_1_PE13_PE14_PE15 0
+
+#define HAS_SPI_2 1 /* CC1120 */
+#define SPI_2_PB13_PB14_PB15 0
+#define SPI_2_PD1_PD3_PD4 1
+#define SPI_2_GPIO (&stm_gpiod)
+#define SPI_2_SCK 1
+#define SPI_2_MISO 3
+#define SPI_2_MOSI 4
+#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz
+
+#define HAS_I2C_1 0
+
+#define HAS_I2C_2 0
+
+#define PACKET_HAS_SLAVE 0
+#define PACKET_HAS_MASTER 0
+
+#define FAST_TIMER_FREQ 10000 /* .1ms for debouncing */
+
+/*
+ * Radio is a cc1200 connected via SPI
+ */
+
+#define AO_RADIO_CAL_DEFAULT 5695733
+
+#define AO_FEC_DEBUG 0
+#define AO_CC1200_SPI_CS_PORT (&stm_gpiod)
+#define AO_CC1200_SPI_CS_PIN 0
+#define AO_CC1200_SPI_BUS AO_SPI_2_PD1_PD3_PD4
+#define AO_CC1200_SPI stm_spi2
+
+#define AO_CC1200_INT_PORT (&stm_gpioc)
+#define AO_CC1200_INT_PIN (15)
+
+#define AO_CC1200_INT_GPIO 2
+#define AO_CC1200_INT_GPIO_IOCFG CC1200_IOCFG2
+
+#define LOW_LEVEL_DEBUG 0
+
+#define LED_PORT_ENABLE STM_RCC_AHBENR_GPIOCEN
+#define LED_PORT (&stm_gpioc)
+#define LED_PIN_RED 7
+#define LED_PIN_AMBER 8
+#define LED_PIN_GREEN 9
+#define LED_PIN_CONTINUITY_3 10
+#define LED_PIN_CONTINUITY_2 11
+#define LED_PIN_CONTINUITY_1 12
+#define LED_PIN_CONTINUITY_0 13
+#define LED_PIN_REMOTE_ARM 14
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_AMBER (1 << LED_PIN_AMBER)
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+#define AO_LED_CONTINUITY_3 (1 << LED_PIN_CONTINUITY_3)
+#define AO_LED_CONTINUITY_2 (1 << LED_PIN_CONTINUITY_2)
+#define AO_LED_CONTINUITY_1 (1 << LED_PIN_CONTINUITY_1)
+#define AO_LED_CONTINUITY_0 (1 << LED_PIN_CONTINUITY_0)
+
+#define AO_LED_CONTINUITY_NUM 4
+
+#define AO_LED_REMOTE_ARM (1 << LED_PIN_REMOTE_ARM)
+
+#define LEDS_AVAILABLE (AO_LED_RED | \
+ AO_LED_AMBER | \
+ AO_LED_GREEN | \
+ AO_LED_CONTINUITY_3 | \
+ AO_LED_CONTINUITY_2 | \
+ AO_LED_CONTINUITY_1 | \
+ AO_LED_CONTINUITY_0 | \
+ AO_LED_REMOTE_ARM)
+
+/* LCD displays */
+
+#define LCD_DEBUG 0
+#define SEVEN_SEGMENT_DEBUG 0
+
+#define AO_LCD_STM_SEG_ENABLED_0 ( \
+ (1 << 0) | /* PA1 */ \
+ (1 << 1) | /* PA2 */ \
+ (1 << 2) | /* PA3 */ \
+ (1 << 3) | /* PA6 */ \
+ (1 << 4) | /* PA7 */ \
+ (1 << 5) | /* PB0 */ \
+ (1 << 6) | /* PB1 */ \
+ (1 << 7) | /* PB3 */ \
+ (1 << 8) | /* PB4 */ \
+ (1 << 9) | /* PB5 */ \
+ (1 << 10) | /* PB10 */ \
+ (1 << 11) | /* PB11 */ \
+ (1 << 12) | /* PB12 */ \
+ (1 << 13) | /* PB13 */ \
+ (1 << 14) | /* PB14 */ \
+ (1 << 15) | /* PB15 */ \
+ (1 << 16) | /* PB8 */ \
+ (1 << 17) | /* PA15 */ \
+ (1 << 18) | /* PC0 */ \
+ (1 << 19) | /* PC1 */ \
+ (1 << 20) | /* PC2 */ \
+ (1 << 21) | /* PC3 */ \
+ (1 << 22) | /* PC4 */ \
+ (1 << 23) | /* PC5 */ \
+ (0 << 24) | /* PC6 */ \
+ (0 << 25) | /* PC7 */ \
+ (0 << 26) | /* PC8 */ \
+ (0 << 27) | /* PC9 */ \
+ (0 << 28) | /* PC10 or PD8 */ \
+ (0 << 29) | /* PC11 or PD9 */ \
+ (0 << 30) | /* PC12 or PD10 */ \
+ (0 << 31)) /* PD2 or PD11 */
+
+#define AO_LCD_STM_SEG_ENABLED_1 ( \
+ (0 << 0) | /* PD12 */ \
+ (0 << 1) | /* PD13 */ \
+ (0 << 2) | /* PD14 */ \
+ (0 << 3) | /* PD15 */ \
+ (0 << 4) | /* PE0 */ \
+ (0 << 5) | /* PE1 */ \
+ (0 << 6) | /* PE2 */ \
+ (0 << 7)) /* PE3 */
+
+#define AO_LCD_STM_COM_ENABLED ( \
+ (1 << 0) | /* PA8 */ \
+ (0 << 1) | /* PA9 */ \
+ (0 << 2) | /* PA10 */ \
+ (0 << 3) | /* PB9 */ \
+ (0 << 4) | /* PC10 */ \
+ (0 << 5) | /* PC11 */ \
+ (0 << 6)) /* PC12 */
+
+#define AO_LCD_28_ON_C 0
+
+#define AO_LCD_DUTY STM_LCD_CR_DUTY_STATIC
+
+#define AO_LCD_PER_DIGIT 1
+
+#define AO_LCD_DIGITS 3
+#define AO_LCD_SEGMENTS 8
+
+#define AO_SEGMENT_MAP { \
+ /* pad segments */ \
+ { 0, 14 }, \
+ { 0, 13 }, \
+ { 0, 15 }, \
+ { 0, 17 }, \
+ { 0, 16 }, \
+ { 0, 8 }, \
+ { 0, 9 }, \
+ { 0, 7 }, \
+ /* box1 segments */ \
+ { 0, 10 }, \
+ { 0, 6 }, \
+ { 0, 11 }, \
+ { 0, 12 }, \
+ { 0, 21 }, \
+ { 0, 19 }, \
+ { 0, 20 }, \
+ { 0, 18 }, \
+ /* box0 segments */ \
+ { 0, 22 }, \
+ { 0, 4 }, \
+ { 0, 23 }, \
+ { 0, 5 }, \
+ { 0, 3 }, \
+ { 0, 1 }, \
+ { 0, 2 }, \
+ { 0, 0 }, \
+}
+
+/*
+ * Use event queue for input devices
+ */
+
+#define AO_EVENT 1
+
+/*
+ * Knobs
+ */
+
+#define AO_QUADRATURE_COUNT 2
+
+#define AO_QUADRATURE_0_PORT &stm_gpioe
+#define AO_QUADRATURE_0_A 3
+#define AO_QUADRATURE_0_B 2
+
+#define AO_QUADRATURE_PAD 0
+
+#define AO_QUADRATURE_1_PORT &stm_gpioe
+#define AO_QUADRATURE_1_A 1
+#define AO_QUADRATURE_1_B 0
+
+#define AO_QUADRATURE_BOX 1
+
+/*
+ * Buttons
+ */
+
+#define AO_BUTTON_COUNT 2
+#define AO_BUTTON_MODE AO_EXTI_MODE_PULL_UP
+
+#define AO_BUTTON_0_PORT &stm_gpioe
+#define AO_BUTTON_0 4
+
+#define AO_BUTTON_ARM 0
+
+#define AO_BUTTON_1_PORT &stm_gpioe
+#define AO_BUTTON_1 5
+
+#define AO_BUTTON_FIRE 1
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_profile.h>
+#include <ao_pyro.h>
+#include <ao_aes.h>
+#include <ao_seven_segment.h>
+#include <ao_quadrature.h>
+#include <ao_button.h>
+#include <ao_lco.h>
+#include <ao_lco_cmd.h>
+#include <ao_radio_cmac_cmd.h>
+#include <ao_eeprom.h>
+
+int
+main(void)
+{
+ ao_clock_init();
+
+ ao_led_init(LEDS_AVAILABLE);
+ ao_led_on(AO_LED_GREEN);
+ ao_task_init();
+
+ ao_timer_init();
+
+ ao_spi_init();
+ ao_dma_init();
+ ao_exti_init();
+
+ ao_beep_init();
+ ao_cmd_init();
+
+ ao_lcd_stm_init();
+ ao_seven_segment_init();
+ ao_quadrature_init();
+ ao_button_init();
+
+ ao_eeprom_init();
+
+ ao_radio_init();
+
+ ao_usb_init();
+
+ ao_config_init();
+
+ ao_lco_init();
+ ao_lco_cmd_init();
+// ao_radio_cmac_cmd_init();
+
+ ao_start_scheduler();
+ return 0;
+}
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telelco-v0.2
+include $(TOPDIR)/stm/Makefile-flash.defs
--- /dev/null
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* External crystal at 8MHz */
+#define AO_HSE 8000000
+
+#include <ao_flash_stm_pins.h>
+
+/* Arm switch. Press at power on to get boot loader */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpioe
+#define AO_BOOT_APPLICATION_PIN 4
+#define AO_BOOT_APPLICATION_VALUE 1
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP
+
+#endif /* _AO_PINS_H_ */
ao_quaternion_rotate(&ao_out, &ao_x, &ao_rotation);
+#if 0
int out = floor (atan2(ao_out.y, ao_out.x) * 180 / M_PI);
-#if 0
printf ("%7.2f state %-8.8s height %8.4f tilt %4d rot %4d mag_tilt %4d mag_rot %4d\n",
time,
ao_state_names[ao_flight_state],
break;
}
#if TELEMEGA
- if (log_format == AO_LOG_FORMAT_TELEMEGA && nword == 30 && strlen(words[0]) == 1) {
+ if ((log_format == AO_LOG_FORMAT_TELEMEGA_OLD || log_format == AO_LOG_FORMAT_TELEMEGA) && nword == 30 && strlen(words[0]) == 1) {
int i;
struct ao_ms5607_value value;
ao_config.accel_zero_along = atoi(words[3]);
ao_config.accel_zero_across = atoi(words[5]);
ao_config.accel_zero_through = atoi(words[7]);
- printf ("%d %d %d\n", ao_config.accel_zero_along, ao_config.accel_zero_across, ao_config.accel_zero_through);
#endif
} else if (nword >= 4 && strcmp(words[0], "Main") == 0) {
ao_config.main_deploy = atoi(words[2]);
ao_pins.h \
ao_product.h \
ao_task.h \
+ ao_adc_fast.h \
stm32f0.h
#
ao_boot_chain.c \
ao_cmd.c \
ao_usb_stm.c \
+ ao_trng.c \
ao_task.c \
ao_product.c
#define AO_CRC_WIDTH 32
#define AO_CRC_INIT 0xffffffff
+/* TRNG */
+#define AO_LED_TRNG_READ AO_LED_RED
+#define AO_LED_TRNG_WRITE AO_LED_GREEN
+
#endif /* _AO_PINS_H_ */
#include <ao.h>
#include <ao_adc_fast.h>
#include <ao_crc.h>
-
-static void
-ao_trng_fetch(void)
-{
- static uint16_t *buffer[2];
- uint32_t kbytes = 1;
- uint32_t count;
- int usb_buf_id;
- int i;
- uint16_t *buf;
- uint32_t *rnd;
-
- if (!buffer[0]) {
- buffer[0] = ao_usb_alloc();
- buffer[1] = ao_usb_alloc();
- if (!buffer[0])
- return;
- }
-
- ao_cmd_decimal();
- if (ao_cmd_status == ao_cmd_success)
- kbytes = ao_cmd_lex_u32;
- else
- ao_cmd_status = ao_cmd_success;
- usb_buf_id = 0;
- count = kbytes * (1024/AO_USB_IN_SIZE);
-
- ao_crc_reset();
-
- ao_led_on(AO_LED_GREEN);
- while (count--) {
- buf = buffer[usb_buf_id];
-// printf ("before get: head %3d tail %3d running %d\n", ao_adc_ring_head, ao_adc_ring_tail, ao_adc_running); flush();
- rnd = (uint32_t *) ao_adc_get(AO_USB_IN_SIZE); /* one 16-bit value per output byte */
-// printf ("after get: head %3d tail %3d running %d\n", ao_adc_ring_head, ao_adc_ring_tail, ao_adc_running); flush();
- for (i = 0; i < 32; i++)
- *buf++ = ao_crc_in_32_out_16(*rnd++);
- ao_adc_ack(AO_USB_IN_SIZE);
-// printf ("after ack: head %3d tail %3d running %d\n", ao_adc_ring_head, ao_adc_ring_tail, ao_adc_running); flush();
- ao_led_toggle(AO_LED_GREEN|AO_LED_RED);
- ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE);
- ao_led_toggle(AO_LED_GREEN|AO_LED_RED);
- usb_buf_id = 1-usb_buf_id;
- }
- ao_led_off(AO_LED_GREEN|AO_LED_RED);
- flush();
-}
-
-static const struct ao_cmds usbtrng_cmds[] = {
- { ao_trng_fetch, "f <kbytes>\0Fetch a block of numbers" },
- { 0, NULL },
-};
+#include <ao_trng.h>
void main(void)
{
ao_usb_init();
- ao_cmd_register(usbtrng_cmds);
+ ao_trng_init();
+
ao_led_off(AO_LED_RED);
ao_start_scheduler();
-#!/bin/sh
+#!/usr/bin/nickle
autoimport ParseArgs;
+file out = stdout;
+
void
write_ucs2(string a, string description)
{
int len = String::length(a);
- printf("/* %s */\n", description);
- printf("#define AO_%s_LEN 0x%02x\n", description, len * 2 + 2);
- printf("#define AO_%s_STRING \"%s\"\n", description, a);
- printf("#define AO_%s_UCS2", description);
+ File::fprintf(out, "/* %s */\n", description);
+ File::fprintf(out, "#define AO_%s_LEN 0x%02x\n", description, len * 2 + 2);
+ File::fprintf(out, "#define AO_%s_STRING \"%s\"\n", description, a);
+ File::fprintf(out, "#define AO_%s_UCS2", description);
for (int i = 0; i < len; i++) {
int c = a[i];
if (i > 0)
- printf(",");
+ File::fprintf(out, ",");
if (0x20 <= c && c < 128)
- printf(" '%c', 0", c);
+ File::fprintf(out, " '%c', 0", c);
else
- printf(" LE_WORD(0x%04x),", c);
+ File::fprintf(out, " LE_WORD(0x%04x),", c);
}
- printf("\n\n");
+ File::fprintf(out, "\n\n");
}
void
write_string(string a, string description)
{
- printf ("/* %s */\n", description);
- printf ("#define AO_%s_STRING \"%s\"\n", description, a);
+ File::fprintf(out, "/* %s */\n", description);
+ File::fprintf(out, "#define AO_%s_STRING \"%s\"\n", description, a);
}
void
write_int(int a, string description)
{
- printf ("/* %s */\n", description);
- printf ("#define AO_%s_NUMBER %d\n\n", description, a);
+ File::fprintf(out, "/* %s */\n", description);
+ File::fprintf(out, "#define AO_%s_NUMBER %d\n\n", description, a);
}
void
write_hex(int a, string description)
{
- printf ("/* %s */\n", description);
- printf ("#define AO_%s_NUMBER 0x%04x\n\n", description, a);
+ File::fprintf(out, "/* %s */\n", description);
+ File::fprintf(out, "#define AO_%s_NUMBER 0x%04x\n\n", description, a);
}
string manufacturer = "altusmetrum.org";
string product = "TeleMetrum";
string version = "0.0";
+string output = "";
int serial = 1;
int user_argind = 0;
+int id_vendor = 0xfffe;
int id_product = 0x000a;
argdesc argd = {
.name = "product",
.expr_name = "prod",
.desc = "Product name." },
+ {
+ .var = { .arg_int = &id_vendor },
+ .abbr = 'V',
+ .name = "id_vendor",
+ .expr_name = "id_v",
+ .desc = "Vendor ID." },
{
.var = { .arg_int = &id_product },
.abbr = 'i',
.name = "version",
.expr_name = "string",
.desc = "Program version." },
+ {
+ .var = { .arg_string = &output },
+ .abbr = 'o',
+ .name = "output",
+ .expr_name = "out",
+ .desc = "Output file." },
},
.prog_name = "usb descriptors",
};
{
string[dim(argv)-1] nargv = {[n] = argv[n+1]};
parseargs(&argd, &nargv);
+ if (output != "")
+ out = File::open(output, "w");
write_ucs2(manufacturer, "iManufacturer");
write_ucs2(product, "iProduct");
write_ucs2(sprintf("%06d", serial), "iSerial");
write_int(serial, "iSerial");
write_hex(id_product, "idProduct");
+ write_hex(id_vendor, "idVendor");
write_string(version, "iVersion");
}
import java.io.*;
import java.util.concurrent.*;
import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import java.text.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class TeleGPS
extends AltosUIFrame
JTabbedPane pane;
- AltosUIMap map;
+ AltosUIMapNew map;
TeleGPSInfo gps_info;
TeleGPSState gps_state;
AltosInfoTable info_table;
}
void load_maps() {
- new AltosUIMapPreload(this);
+ new AltosUIMapPreloadNew(this);
}
void disconnect() {
public void actionPerformed(ActionEvent e) {
int rate = rates.rate();
try {
- System.out.printf("set rate %d\n", rate);
reader.set_telemetry_rate(rate);
} catch (TimeoutException te) {
} catch (InterruptedException ie) {
bag = getContentPane();
bag.setLayout(new GridBagLayout());
- GridBagConstraints c = new GridBagConstraints();
-
setTitle("TeleGPS");
menu_bar = new JMenuBar();
monitor_menu = make_menu("Monitor", monitor_menu_entries);
device_menu = make_menu("Device", device_menu_entries);
+ set_inset(3);
frequencies = new AltosUIFreqList();
frequencies.setEnabled(false);
- c.gridx = 0;
- c.gridy = 0;
- c.fill = GridBagConstraints.NONE;
- c.anchor = GridBagConstraints.WEST;
- c.weightx = 0;
- c.gridwidth = 1;
- bag.add(frequencies, c);
+ bag.add(frequencies, constraints (0, 1));
rates = new AltosUIRateList();
rates.setEnabled(false);
- c.gridx = 1;
- c.gridy = 0;
- c.fill = GridBagConstraints.NONE;
- c.anchor = GridBagConstraints.WEST;
- c.weightx = 0;
- c.gridwidth = 1;
- bag.add(rates, c);
+ bag.add(rates, constraints(1, 1));
+ next_row();
+ set_inset(0);
displays = new LinkedList<AltosFlightDisplay>();
/* TeleGPS status is always visible */
telegps_status = new TeleGPSStatus();
- c.gridx = 0;
- c.gridy = 1;
- c.fill = GridBagConstraints.HORIZONTAL;
- c.weightx = 1;
- c.gridwidth = 2;
- bag.add(telegps_status, c);
- c.gridwidth = 1;
+ bag.add(telegps_status, constraints(0, 3, GridBagConstraints.HORIZONTAL));
+ next_row();
+
displays.add(telegps_status);
pane = new JTabbedPane();
/* Make the tabbed pane use the rest of the window space */
- c.gridx = 0;
- c.gridy = 2;
- c.fill = GridBagConstraints.BOTH;
- c.weightx = 1;
- c.weighty = 1;
- c.gridwidth = 2;
- bag.add(pane, c);
-
- map = new AltosUIMap();
+ bag.add(pane, constraints(0, 3, GridBagConstraints.BOTH));
+
+ map = new AltosUIMapNew();
pane.add(map.getName(), map);
displays.add(map);
public static void help(int code) {
System.out.printf("Usage: altosui [OPTION]... [FILE]...\n");
System.out.printf(" Options:\n");
- System.out.printf(" --fetchmaps <lat> <lon>\tpre-fetch maps for site map view\n");
System.out.printf(" --replay <filename>\t\trelive the glory of past flights \n");
System.out.printf(" --graph <filename>\t\tgraph a flight\n");
System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n");
for (int i = 0; i < args.length; i++) {
if (args[i].equals("--help"))
help(0);
- else if (args[i].equals("--fetchmaps")) {
- if (args.length < i + 3) {
- help(1);
- } else {
- double lat = Double.parseDouble(args[i+1]);
- double lon = Double.parseDouble(args[i+2]);
- AltosUIMap.prefetch_maps(lat, lon);
- i += 2;
- }
- } else if (args[i].equals("--replay"))
+ else if (args[i].equals("--replay"))
process = process_replay;
else if (args[i].equals("--kml"))
process = process_kml;
import java.io.*;
import java.util.concurrent.*;
import java.text.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class TeleGPSConfig implements ActionListener {
package org.altusmetrum.telegps;
+import java.text.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class TeleGPSConfigUI
extends AltosUIDialog
JLabel rate_label;
JLabel aprs_interval_label;
JLabel aprs_ssid_label;
+ JLabel aprs_format_label;
JLabel flight_log_max_label;
JLabel callsign_label;
JLabel tracker_motion_label;
AltosUIRateList rate_value;
JComboBox<String> aprs_interval_value;
JComboBox<Integer> aprs_ssid_value;
+ JComboBox<String> aprs_format_value;
JComboBox<String> flight_log_max_value;
JTextField callsign_value;
JComboBox<String> tracker_motion_value;
void set_aprs_ssid_tool_tip() {
if (aprs_ssid_value.isEnabled())
- aprs_interval_value.setToolTipText("Set the APRS SSID (secondary station identifier)");
- else if (aprs_interval_value.isEnabled())
- aprs_interval_value.setToolTipText("Software version doesn't support setting the APRS SSID");
+ aprs_ssid_value.setToolTipText("Set the APRS SSID (secondary station identifier)");
+ else if (aprs_ssid_value.isEnabled())
+ aprs_ssid_value.setToolTipText("Software version doesn't support setting the APRS SSID");
else
- aprs_interval_value.setToolTipText("Hardware doesn't support APRS");
+ aprs_ssid_value.setToolTipText("Hardware doesn't support APRS");
+ }
+
+ void set_aprs_format_tool_tip() {
+ if (aprs_format_value.isEnabled())
+ aprs_format_value.setToolTipText("Set the APRS format (compressed/uncompressed)");
+ else if (aprs_format_value.isEnabled())
+ aprs_format_value.setToolTipText("Software version doesn't support setting the APRS format");
+ else
+ aprs_format_value.setToolTipText("Hardware doesn't support APRS");
}
void set_flight_log_max_tool_tip() {
set_aprs_ssid_tool_tip();
row++;
+ /* APRS format */
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = row;
+ c.gridwidth = 4;
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ c.ipady = 5;
+ aprs_format_label = new JLabel("APRS format:");
+ pane.add(aprs_format_label, c);
+
+ c = new GridBagConstraints();
+ c.gridx = 4; c.gridy = row;
+ c.gridwidth = 4;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ c.ipady = 5;
+ aprs_format_value = new JComboBox<String>(AltosLib.ao_aprs_format_name);
+ aprs_format_value.setEditable(false);
+ aprs_format_value.addItemListener(this);
+ aprs_format_value.setMaximumRowCount(AltosLib.ao_aprs_format_name.length);
+ pane.add(aprs_format_value, c);
+ set_aprs_format_tool_tip();
+ row++;
+
/* Callsign */
c = new GridBagConstraints();
c.gridx = 0; c.gridy = row;
String motion = tracker_motion_value.getSelectedItem().toString();
tracker_motion_label.setText(get_tracker_motion_label());
set_tracker_motion_values();
- set_tracker_motion((int) (AltosConvert.height.parse(motion, !imperial_units) + 0.5));
+ try {
+ int m = (int) (AltosConvert.height.parse_locale(motion, !imperial_units) + 0.5);
+ set_tracker_motion(m);
+ } catch (ParseException pe) {
+ }
}
if (!was_dirty)
set_clean();
}
public int tracker_motion() throws AltosConfigDataException {
- return (int) AltosConvert.height.parse(tracker_motion_value.getSelectedItem().toString());
+ String str = tracker_motion_value.getSelectedItem().toString();
+ try {
+ return (int) (AltosConvert.height.parse_locale(str) + 0.5);
+ } catch (ParseException pe) {
+ throw new AltosConfigDataException("invalid tracker motion %s", str);
+ }
}
public void set_tracker_interval(int tracker_interval) {
Integer i = (Integer) aprs_ssid_value.getSelectedItem();
return i;
}
+
+ public void set_aprs_format(int new_aprs_format) {
+ aprs_format_value.setVisible(new_aprs_format >= 0);
+ aprs_format_label.setVisible(new_aprs_format >= 0);
+
+ aprs_format_value.setSelectedIndex(Math.max(0,new_aprs_format));
+ set_aprs_format_tool_tip();
+ }
+
+ public int aprs_format() throws AltosConfigDataException {
+ return aprs_format_value.getSelectedIndex();
+ }
}
import javax.swing.*;
import java.io.*;
import java.text.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class TeleGPSDisplayThread extends Thread {
import java.io.*;
import java.util.concurrent.*;
import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
JTabbedPane pane;
AltosGraph graph;
AltosUIEnable enable;
- AltosUIMap map;
+ AltosUIMapNew map;
AltosState state;
AltosFlightStats stats;
AltosGraphDataSet graphDataSet;
graph = new AltosGraph(enable, stats, graphDataSet);
statsTable = new AltosFlightStatsTable(stats);
- map = new AltosUIMap();
+ map = new AltosUIMapNew();
pane.add("Graph", graph.panel);
pane.add("Configure Graph", enable);
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class TeleGPSInfo extends AltosUIFlightTab {
import java.beans.*;
import javax.swing.*;
import javax.swing.event.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_7.*;
public class TeleGPSPreferences
extends AltosUIConfigure
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class TeleGPSState extends AltosUIFlightTab {
}
}
+ class ReceiverBattery extends AltosUIVoltageIndicator {
+
+ public double voltage(AltosState state) { return AltosLib.MISSING; }
+
+ public double good() { return AltosLib.ao_battery_good; }
+
+ public boolean hide(AltosState state, AltosListenerState listener_state, int i) {
+ return value(state, listener_state, i) == AltosLib.MISSING;
+ }
+
+ public double value(AltosState state, AltosListenerState listener_state, int i) {
+ if (listener_state == null)
+ return AltosLib.MISSING;
+ return listener_state.battery;
+ }
+
+ public ReceiverBattery (AltosUIFlightTab container, int y) {
+ super(container, y, "Receiver Battery", 2);
+ }
+ }
public void labels(Container container, int y) {
GridBagLayout layout = (GridBagLayout)(container.getLayout());
add(new FirmwareVersion(this, y++));
add(new FlightLogMax(this, y++));
add(new BatteryVoltage(this, y++));
+ add(new ReceiverBattery(this, y++));
}
}
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_7.*;
+import org.altusmetrum.altosuilib_7.*;
public class TeleGPSStatus extends JComponent implements AltosFlightDisplay {
GridBagLayout layout;
package org.altusmetrum.telegps;
import java.awt.event.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_7.*;
public class TeleGPSStatusUpdate implements ActionListener {
File "freetts.jar"
File "jfreechart.jar"
File "jcommon.jar"
+ File "../icon/${WIN_APP_EXE}"
File "*.dll"
File "../icon/${WIN_APP_ICON}"
- CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}"
+ CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}"
SectionEnd
Section "${REG_NAME} Desktop Shortcut"
- CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}"
+ CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}"
SectionEnd
Section "TeleGPS, TeleDongle and TeleBT Firmware"
SetOutPath $INSTDIR
- File "../icon/${WIN_APP_EXE}"
File "../icon/${WIN_TELEM_EXE}"
File "../icon/${WIN_EEPROM_EXE}"
DeleteRegKey HKCR ".telem\${PROG_ID_EEPROM}"
DeleteRegValue HKCR ".telem\OpenWithProgids" "${PROG_ID_EEPROM}"
- SearchPath $1 "javaw.exe"
-
; .eeprom elements
WriteRegStr HKCR "${PROG_ID_EEPROM}" "" "Altus Metrum Log File"
WriteRegStr HKCR "${PROG_ID_EEPROM}" "FriendlyTypeName" "Altus Metrum Log File"
WriteRegStr HKCR "${PROG_ID_EEPROM}\CurVer" "" "${PROG_ID_EEPROM}"
WriteRegStr HKCR "${PROG_ID_EEPROM}\DefaultIcon" "" '"$INSTDIR\${WIN_EEPROM_EXE}",-101'
- WriteRegExpandStr HKCR "${PROG_ID_EEPROM}\shell\open\command" "" '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"'
+ WriteRegExpandStr HKCR "${PROG_ID_EEPROM}\shell\open\command" "" '"$INSTDIR\${WIN_APP_EXE}" "%1"'
WriteRegStr HKCR ".eeprom" "" "${PROG_ID_EEPROM}"
WriteRegStr HKCR ".eeprom" "PerceivedType" "Altus Metrum Log File"
WriteRegStr HKCR "${PROG_ID_TELEM}" "FriendlyTypeName" "Altus Metrum Telemetry File"
WriteRegStr HKCR "${PROG_ID_TELEM}\CurVer" "" "${PROG_ID_TELEM}"
WriteRegStr HKCR "${PROG_ID_TELEM}\DefaultIcon" "" '"$INSTDIR\${WIN_TELEM_EXE}",-101'
- WriteRegExpandStr HKCR "${PROG_ID_TELEM}\shell\open\command" "" '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"'
+ WriteRegExpandStr HKCR "${PROG_ID_TELEM}\shell\open\command" "" '"$INSTDIR\${WIN_APP_EXE}" "%1"'
WriteRegStr HKCR ".telem" "" "${PROG_ID_TELEM}"
WriteRegStr HKCR ".telem" "PerceivedType" "Altus Metrum Telemetry File"