diff options
| author | Bdale Garbee <bdale@gag.com> | 2015-07-15 16:43:50 -0600 | 
|---|---|---|
| committer | Bdale Garbee <bdale@gag.com> | 2015-07-15 16:43:50 -0600 | 
| commit | 643c2fb03833d658320f476ef731bbb06fe3cc31 (patch) | |
| tree | 878c9df5dbd9bab9169becea4e06e8bae3529541 | |
| parent | e41786fb384892961a6547e17812a24314ce9623 (diff) | |
| parent | 271f56a41c7e785b0fab7e572325df842d104277 (diff) | |
Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
382 files changed, 11921 insertions, 5383 deletions
diff --git a/altosdroid/AndroidManifest.xml b/altosdroid/AndroidManifest.xml.in index 19e5a6dc..24035796 100644 --- a/altosdroid/AndroidManifest.xml +++ b/altosdroid/AndroidManifest.xml.in @@ -17,9 +17,9 @@  -->  <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"/> +          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"/> @@ -38,23 +38,50 @@                  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:allowBackup="true" +		 android:theme="@style/CustomTheme">          <activity android:name="org.altusmetrum.AltosDroid.AltosDroid"                    android:label="@string/app_name" -                  android:configChanges="orientation|keyboardHidden" > +                  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" diff --git a/altosdroid/Makefile.am b/altosdroid/Makefile.am index 361de13c..26e14ee7 100644 --- a/altosdroid/Makefile.am +++ b/altosdroid/Makefile.am @@ -14,11 +14,16 @@ DX=$(SDK)/platform-tools/dx  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 @@ -39,19 +44,25 @@ DRAWABLES=\      $(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) @@ -83,9 +94,15 @@ bin/AltosDroid-release.apk: bin/AltosDroid-release-unsigned.apk  	   -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 diff --git a/altosdroid/Notebook b/altosdroid/Notebook index 6a246df7..73b5ed27 100644 --- a/altosdroid/Notebook +++ b/altosdroid/Notebook @@ -15,17 +15,9 @@ Desired AltosDroid feature list   *) 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 @@ -47,3 +39,70 @@ 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. diff --git a/altosdroid/default.properties b/altosdroid/default.properties index 66db0d15..3ac25234 100644 --- a/altosdroid/default.properties +++ b/altosdroid/default.properties @@ -8,4 +8,4 @@  # project structure.  # Project target. -target=android-10 +target=android-12 diff --git a/altosdroid/project.properties b/altosdroid/project.properties index 96b9551c..d178f98a 100644 --- a/altosdroid/project.properties +++ b/altosdroid/project.properties @@ -8,5 +8,5 @@  # project structure.  # Project target. -target=android-10 +target=android-12  android.library.reference.1=google-play-services_lib/ diff --git a/altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png b/altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png Binary files differnew file mode 100644 index 00000000..bc9160df --- /dev/null +++ b/altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png diff --git a/altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png b/altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png Binary files differnew file mode 100644 index 00000000..4e427d89 --- /dev/null +++ b/altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png diff --git a/altosdroid/res/layout/device_list.xml b/altosdroid/res/layout/device_list.xml index 93d65517..bf295e4c 100644 --- a/altosdroid/res/layout/device_list.xml +++ b/altosdroid/res/layout/device_list.xml @@ -32,6 +32,13 @@          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" @@ -44,13 +51,8 @@      <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> diff --git a/altosdroid/res/layout/map_preload.xml b/altosdroid/res/layout/map_preload.xml new file mode 100644 index 00000000..dc613bf2 --- /dev/null +++ b/altosdroid/res/layout/map_preload.xml @@ -0,0 +1,129 @@ +<?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> diff --git a/altosdroid/res/layout/map_type.xml b/altosdroid/res/layout/map_type.xml new file mode 100644 index 00000000..610e6bbf --- /dev/null +++ b/altosdroid/res/layout/map_type.xml @@ -0,0 +1,47 @@ +<?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> diff --git a/altosdroid/res/layout/tab_ascent.xml b/altosdroid/res/layout/tab_ascent.xml deleted file mode 100644 index b21ec426..00000000 --- a/altosdroid/res/layout/tab_ascent.xml +++ /dev/null @@ -1,299 +0,0 @@ -<?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 diff --git a/altosdroid/res/layout/tab_descent.xml b/altosdroid/res/layout/tab_descent.xml deleted file mode 100644 index 9e1fc820..00000000 --- a/altosdroid/res/layout/tab_descent.xml +++ /dev/null @@ -1,339 +0,0 @@ -<?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> diff --git a/altosdroid/res/layout/tab_flight.xml b/altosdroid/res/layout/tab_flight.xml new file mode 100644 index 00000000..85c171b2 --- /dev/null +++ b/altosdroid/res/layout/tab_flight.xml @@ -0,0 +1,402 @@ +<?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> diff --git a/altosdroid/res/layout/tab_landed.xml b/altosdroid/res/layout/tab_landed.xml deleted file mode 100644 index f27baa9e..00000000 --- a/altosdroid/res/layout/tab_landed.xml +++ /dev/null @@ -1,211 +0,0 @@ -<?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 diff --git a/altosdroid/res/layout/tab_layout.xml b/altosdroid/res/layout/tab_layout.xml new file mode 100644 index 00000000..2c21c648 --- /dev/null +++ b/altosdroid/res/layout/tab_layout.xml @@ -0,0 +1,16 @@ +<?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> diff --git a/altosdroid/res/layout/tab_map.xml b/altosdroid/res/layout/tab_map.xml index f611ae48..952abd49 100644 --- a/altosdroid/res/layout/tab_map.xml +++ b/altosdroid/res/layout/tab_map.xml @@ -20,169 +20,182 @@  	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> diff --git a/altosdroid/res/layout/tab_pad.xml b/altosdroid/res/layout/tab_pad.xml index 38e61f83..88648c3b 100644 --- a/altosdroid/res/layout/tab_pad.xml +++ b/altosdroid/res/layout/tab_pad.xml @@ -15,306 +15,481 @@       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> diff --git a/altosdroid/res/layout/tab_recover.xml b/altosdroid/res/layout/tab_recover.xml new file mode 100644 index 00000000..201f45ed --- /dev/null +++ b/altosdroid/res/layout/tab_recover.xml @@ -0,0 +1,251 @@ +<?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> diff --git a/altosdroid/res/menu/option_menu.xml b/altosdroid/res/menu/option_menu.xml index f005e881..7e08c803 100644 --- a/altosdroid/res/menu/option_menu.xml +++ b/altosdroid/res/menu/option_menu.xml @@ -1,17 +1,19 @@  <?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" @@ -20,9 +22,6 @@      <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" /> @@ -32,4 +31,22 @@      <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> diff --git a/altosdroid/res/values/Colors.xml b/altosdroid/res/values/Colors.xml new file mode 100644 index 00000000..055c6df2 --- /dev/null +++ b/altosdroid/res/values/Colors.xml @@ -0,0 +1,20 @@ +<?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> diff --git a/altosdroid/res/values/CustomTheme.xml b/altosdroid/res/values/CustomTheme.xml new file mode 100644 index 00000000..6c701ea2 --- /dev/null +++ b/altosdroid/res/values/CustomTheme.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> +  <style name="CustomTheme" parent="android:Theme.Holo"> +  </style> +</resources> diff --git a/altosdroid/res/values/strings.xml b/altosdroid/res/values/strings.xml index 8a5b29b4..e7014fc9 100644 --- a/altosdroid/res/values/strings.xml +++ b/altosdroid/res/values/strings.xml @@ -32,6 +32,14 @@  	<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> @@ -61,6 +69,7 @@  	<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> @@ -68,10 +77,15 @@  	<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> @@ -80,8 +94,21 @@  	<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> diff --git a/altosdroid/res/xml/device_filter.xml b/altosdroid/res/xml/device_filter.xml new file mode 100644 index 00000000..84b09c06 --- /dev/null +++ b/altosdroid/res/xml/device_filter.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> +  <usb-device vendor-id="65534" /> +  <usb-device vendor-id="1027" /> +</resources> diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java index 973250a5..03ae5cb8 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java @@ -29,294 +29,192 @@ import android.bluetooth.BluetoothSocket;  //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; } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java new file mode 100644 index 00000000..cee6e56e --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java @@ -0,0 +1,77 @@ +/* + * 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"); +		} +	} +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java index 41045f03..71ac298e 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java @@ -18,11 +18,12 @@  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; @@ -36,28 +37,26 @@ import android.os.Handler;  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 @@ -67,14 +66,15 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  	// 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; @@ -83,6 +83,14 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  	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; @@ -100,6 +108,11 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  	// 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; @@ -120,17 +133,13 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  			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;  			} @@ -148,6 +157,13 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  			} 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) { @@ -201,20 +217,20 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  				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;  		}  	} @@ -234,19 +250,73 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  		}  	} +	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) { @@ -261,12 +331,55 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  		}  	} + +	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; @@ -276,6 +389,8 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  			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; @@ -287,9 +402,9 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  				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 { @@ -297,16 +412,13 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  					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;  					}  				} @@ -350,10 +462,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  		}  		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;  	} @@ -390,26 +502,32 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  		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); @@ -420,37 +538,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  		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); @@ -465,62 +556,158 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  		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 @@ -535,12 +722,30 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  				//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"); +			}  		}  	} @@ -550,10 +755,12 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  			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");  		}  	} @@ -564,6 +771,17 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  		}  	} +	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(); @@ -574,20 +792,22 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  	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) {  		}  	} @@ -612,22 +832,76 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  		}  	} +	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; @@ -682,8 +956,92 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  			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); +	}  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java new file mode 100644 index 00000000..c7230512 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java @@ -0,0 +1,219 @@ +/* + * 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; +	} +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java new file mode 100644 index 00000000..7aff1341 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java @@ -0,0 +1,33 @@ +/* + * 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); +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java index 372500c1..6f74014a 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java @@ -17,7 +17,7 @@  package org.altusmetrum.AltosDroid;  import android.content.Context; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_7.*;  public class AltosDroidPreferences extends AltosPreferences { @@ -27,6 +27,14 @@ 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; @@ -38,6 +46,8 @@ public class AltosDroidPreferences extends AltosPreferences {  		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) { @@ -54,4 +64,18 @@ public class AltosDroidPreferences extends AltosPreferences {  			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; +		} +	}  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java index bc5300fd..dfc37153 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java @@ -24,7 +24,7 @@ import android.content.SharedPreferences;  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"; @@ -44,7 +44,12 @@ public class AltosDroidPreferencesBackend implements AltosPreferencesBackend {  	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) { @@ -104,6 +109,7 @@ public class AltosDroidPreferencesBackend implements AltosPreferencesBackend {  	}  	public void remove(String key) { +		AltosDebug.debug("remove preference %s\n", key);  		editor.remove(key);  	} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java index 0896b3a3..f75035d4 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java @@ -17,7 +17,7 @@  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; @@ -26,21 +26,28 @@ import android.support.v4.app.Fragment;  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, @@ -55,24 +62,45 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen  	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;  	} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java new file mode 100644 index 00000000..0bf6ab20 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java @@ -0,0 +1,517 @@ +/* + * 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); +	} +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java new file mode 100644 index 00000000..3b56fb54 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java @@ -0,0 +1,327 @@ +/* + * 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; +	} +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java new file mode 100644 index 00000000..e559f814 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java @@ -0,0 +1,230 @@ +/* + * 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()    { } + +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java index 223ae75a..f22298d7 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java @@ -34,14 +34,19 @@ public class AltosViewPager extends ViewPager {      @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);      }  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java index 2d32dc07..adf52dd9 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java @@ -20,201 +20,305 @@ package org.altusmetrum.AltosDroid;  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; +		} +	}  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java index fd6abe0f..f36ef267 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java @@ -27,7 +27,6 @@ import android.content.Context;  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; @@ -45,9 +44,6 @@ import android.widget.AdapterView.OnItemClickListener;   * 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"; @@ -137,7 +133,7 @@ public class DeviceListActivity extends Activity {  	* 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); @@ -173,7 +169,7 @@ public class DeviceListActivity extends Activity {  			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(); @@ -195,14 +191,22 @@ public class DeviceListActivity extends Activity {  			// 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); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java b/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java index 267c90f8..6cecbdf1 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java @@ -52,27 +52,14 @@ public class GoNoGoLights {  		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); -	}  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java new file mode 100644 index 00000000..e43841a8 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java @@ -0,0 +1,84 @@ +/* + * 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); +	} +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java new file mode 100644 index 00000000..498b208e --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java @@ -0,0 +1,418 @@ +/* + * 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); +	} +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java deleted file mode 100644 index 23de9622..00000000 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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); -		} -	} -} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java deleted file mode 100644 index 4ec6f409..00000000 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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); -		} -	} - -} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java new file mode 100644 index 00000000..a02ae3a2 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java @@ -0,0 +1,127 @@ +/* + * 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); +			} +		} +	} + +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java index 871b94a1..81b6fe54 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java @@ -17,186 +17,159 @@  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() { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java index 0ac78219..5ff9d12b 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java @@ -17,155 +17,222 @@  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);  		}  	} -  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java index 4c69d869..523ddb61 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java @@ -17,7 +17,7 @@  package org.altusmetrum.AltosDroid; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_7.*;  import android.app.Activity;  import android.os.Bundle; @@ -28,10 +28,9 @@ import android.view.ViewGroup;  import android.widget.TextView;  import android.location.Location; -public class TabLanded extends AltosDroidTab { -	AltosDroid mAltosDroid; - +public class TabRecover extends AltosDroidTab {  	private TextView mBearingView; +	private TextView mDirectionView;  	private TextView mDistanceView;  	private TextView mTargetLatitudeView;  	private TextView mTargetLongitudeView; @@ -41,19 +40,12 @@ public class TabLanded extends AltosDroidTab {  	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); +		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); @@ -66,19 +58,17 @@ public class TabLanded extends AltosDroidTab {  		return v;  	} -	@Override -	public void onDestroy() { -		super.onDestroy(); -		mAltosDroid.unregisterTab(this); -		mAltosDroid = null; -	} - -	public String tab_name() { return "landed"; } +	public String tab_name() { return AltosDroid.tab_recover_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 (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")); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabsAdapter.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabsAdapter.java index 1ac34f9d..b34a25b6 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabsAdapter.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabsAdapter.java @@ -29,7 +29,6 @@ import android.view.View;  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 @@ -106,7 +105,7 @@ public class TabsAdapter extends FragmentPagerAdapter  	@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;  	} @@ -131,7 +130,7 @@ public class TabsAdapter extends FragmentPagerAdapter  		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);  	} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java index 9764ab72..28bab401 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java @@ -1,18 +1,14 @@  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; @@ -33,21 +29,21 @@ public class TelemetryLogger {  	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();  		}  	} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java index 4e4408d5..4d4f0ed1 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java @@ -21,42 +21,32 @@ package org.altusmetrum.AltosDroid;  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(); @@ -64,16 +54,14 @@ public class TelemetryReader extends Thread {  	}  	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(); @@ -81,21 +69,22 @@ public class TelemetryReader extends Thread {  			}  		} 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");  	}  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java index 5f138972..d7fa704d 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java @@ -18,10 +18,8 @@  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; @@ -29,6 +27,7 @@ import android.app.PendingIntent;  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; @@ -38,32 +37,29 @@ import android.os.Message;  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. @@ -76,7 +72,7 @@ public class TelemetryService extends Service implements LocationListener {  	// 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; @@ -94,6 +90,7 @@ public class TelemetryService extends Service implements LocationListener {  		@Override  		public void handleMessage(Message msg) {  			TelemetryService s = service.get(); +			AltosDroidLink bt = null;  			if (s == null)  				return;  			switch (msg.what) { @@ -106,23 +103,32 @@ public class TelemetryService extends Service implements LocationListener {  				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) {  					} @@ -130,11 +136,11 @@ public class TelemetryService extends Service implements LocationListener {  				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; @@ -143,27 +149,49 @@ public class TelemetryService extends Service implements LocationListener {  				 *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; @@ -171,18 +199,11 @@ public class TelemetryService extends Service implements LocationListener {  				 * 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: @@ -191,13 +212,30 @@ public class TelemetryService extends Service implements LocationListener {  		}  	} +	/* 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);  	} @@ -206,7 +244,7 @@ public class TelemetryService extends Service implements LocationListener {  	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  		 */ @@ -216,8 +254,8 @@ public class TelemetryService extends Service implements LocationListener {  		 * 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);  		}  	} @@ -225,45 +263,46 @@ public class TelemetryService extends Service implements LocationListener {  	 */  	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(); @@ -272,66 +311,123 @@ public class TelemetryService extends Service implements LocationListener {  			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();  	} @@ -339,6 +435,12 @@ public class TelemetryService extends Service implements LocationListener {  	@Override  	public void onCreate() { + +		AltosDebug.init(this); + +		// Initialise preferences +		AltosDroidPreferences.init(this); +  		// Get local Bluetooth adapter  		bluetooth_adapter = BluetoothAdapter.getDefaultAdapter(); @@ -347,9 +449,6 @@ public class TelemetryService extends Service implements LocationListener {  			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 @@ -358,26 +457,43 @@ public class TelemetryService extends Service implements LocationListener {  		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); @@ -397,6 +513,20 @@ public class TelemetryService extends Service implements LocationListener {  		// 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; @@ -409,7 +539,7 @@ public class TelemetryService extends Service implements LocationListener {  		((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); @@ -426,7 +556,7 @@ public class TelemetryService extends Service implements LocationListener {  	public void onLocationChanged(Location location) {  		telemetry_state.location = location; -		if (D) Log.d(TAG, "location changed"); +		AltosDebug.debug("location changed");  		send_to_clients();  	} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java index ca066fc2..c40df648 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java @@ -17,7 +17,8 @@  package org.altusmetrum.AltosDroid; -import org.altusmetrum.altoslib_6.*; +import java.util.*; +import org.altusmetrum.altoslib_7.*;  import android.location.Location;  public class TelemetryState { @@ -29,18 +30,23 @@ 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);  	} diff --git a/altoslib/.gitignore b/altoslib/.gitignore index ff0fd710..dc8b7e5e 100644 --- a/altoslib/.gitignore +++ b/altoslib/.gitignore @@ -1,3 +1,4 @@  bin  classaltoslib.stamp  altoslib*.jar +AltosVersion.java diff --git a/altoslib/AltosAccel.java b/altoslib/AltosAccel.java index b11dc3a1..a358eda9 100644 --- a/altoslib/AltosAccel.java +++ b/altoslib/AltosAccel.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.io.*; diff --git a/altoslib/AltosCRCException.java b/altoslib/AltosCRCException.java index 4167aecf..8100f4f6 100644 --- a/altoslib/AltosCRCException.java +++ b/altoslib/AltosCRCException.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosCSV.java b/altoslib/AltosCSV.java index 02f7806f..f4e50ff1 100644 --- a/altoslib/AltosCSV.java +++ b/altoslib/AltosCSV.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosCompanion.java b/altoslib/AltosCompanion.java index 8992b018..f4221c4b 100644 --- a/altoslib/AltosCompanion.java +++ b/altoslib/AltosCompanion.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.io.*; diff --git a/altoslib/AltosConfigData.java b/altoslib/AltosConfigData.java index d8f4d945..a722a689 100644 --- a/altoslib/AltosConfigData.java +++ b/altoslib/AltosConfigData.java @@ -15,7 +15,7 @@   * 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.*; @@ -75,6 +75,7 @@ public class AltosConfigData implements Iterable<String> {  	/* HAS_APRS */  	public int		aprs_interval;  	public int		aprs_ssid; +	public int		aprs_format;  	/* HAS_BEEP */  	public int		beep; @@ -270,6 +271,7 @@ public class AltosConfigData implements Iterable<String> {  		aprs_interval = -1;  		aprs_ssid = -1; +		aprs_format = -1;  		beep = -1; @@ -370,6 +372,7 @@ public class AltosConfigData implements Iterable<String> {  		/* 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) {} @@ -518,6 +521,8 @@ public class AltosConfigData implements Iterable<String> {  			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) @@ -572,6 +577,7 @@ public class AltosConfigData implements Iterable<String> {  		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); @@ -674,6 +680,8 @@ public class AltosConfigData implements Iterable<String> {  			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) diff --git a/altoslib/AltosConfigDataException.java b/altoslib/AltosConfigDataException.java index 11aa4d24..32f1ab51 100644 --- a/altoslib/AltosConfigDataException.java +++ b/altoslib/AltosConfigDataException.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosConfigValues.java b/altoslib/AltosConfigValues.java index cfe9fc8b..d11bcde1 100644 --- a/altoslib/AltosConfigValues.java +++ b/altoslib/AltosConfigValues.java @@ -15,7 +15,7 @@   * 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 */ @@ -91,6 +91,10 @@ public interface AltosConfigValues {  	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); diff --git a/altoslib/AltosConvert.java b/altoslib/AltosConvert.java index a5eb7ff8..3e0f0e1a 100644 --- a/altoslib/AltosConvert.java +++ b/altoslib/AltosConvert.java @@ -18,7 +18,7 @@  /*   * Sensor data conversion functions   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  public class AltosConvert {  	/* @@ -230,6 +230,12 @@ 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; diff --git a/altoslib/AltosDebug.java b/altoslib/AltosDebug.java index ef5edc6a..004ab566 100644 --- a/altoslib/AltosDebug.java +++ b/altoslib/AltosDebug.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.io.*; diff --git a/altoslib/AltosDistance.java b/altoslib/AltosDistance.java index b0666f33..e00f9500 100644 --- a/altoslib/AltosDistance.java +++ b/altoslib/AltosDistance.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosEeprom.java b/altoslib/AltosEeprom.java index 777988e7..4aed6ba2 100644 --- a/altoslib/AltosEeprom.java +++ b/altoslib/AltosEeprom.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosEepromChunk.java b/altoslib/AltosEepromChunk.java index 19a8807d..75dd2998 100644 --- a/altoslib/AltosEepromChunk.java +++ b/altoslib/AltosEepromChunk.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosEepromDownload.java b/altoslib/AltosEepromDownload.java index 9598bd93..868571e4 100644 --- a/altoslib/AltosEepromDownload.java +++ b/altoslib/AltosEepromDownload.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosEepromFile.java b/altoslib/AltosEepromFile.java index c8443549..ac537856 100644 --- a/altoslib/AltosEepromFile.java +++ b/altoslib/AltosEepromFile.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosEepromGPS.java b/altoslib/AltosEepromGPS.java index 96cfc0e1..564cd70c 100644 --- a/altoslib/AltosEepromGPS.java +++ b/altoslib/AltosEepromGPS.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosEepromHeader.java b/altoslib/AltosEepromHeader.java index fc4e9caa..5483ebb7 100644 --- a/altoslib/AltosEepromHeader.java +++ b/altoslib/AltosEepromHeader.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosEepromIterable.java b/altoslib/AltosEepromIterable.java index 94487ab5..515efda7 100644 --- a/altoslib/AltosEepromIterable.java +++ b/altoslib/AltosEepromIterable.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosEepromList.java b/altoslib/AltosEepromList.java index 39768a21..086483e3 100644 --- a/altoslib/AltosEepromList.java +++ b/altoslib/AltosEepromList.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosEepromLog.java b/altoslib/AltosEepromLog.java index 0fc82ab4..fe1c25dc 100644 --- a/altoslib/AltosEepromLog.java +++ b/altoslib/AltosEepromLog.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosEepromMega.java b/altoslib/AltosEepromMega.java index c2edcf23..49ea3b5d 100644 --- a/altoslib/AltosEepromMega.java +++ b/altoslib/AltosEepromMega.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosEepromMetrum2.java b/altoslib/AltosEepromMetrum2.java index 39425e10..4dacb7cb 100644 --- a/altoslib/AltosEepromMetrum2.java +++ b/altoslib/AltosEepromMetrum2.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosEepromMini.java b/altoslib/AltosEepromMini.java index ef13d0a7..c9b41004 100644 --- a/altoslib/AltosEepromMini.java +++ b/altoslib/AltosEepromMini.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosEepromMonitor.java b/altoslib/AltosEepromMonitor.java index 35ed5a6e..c1a7fdf8 100644 --- a/altoslib/AltosEepromMonitor.java +++ b/altoslib/AltosEepromMonitor.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  public interface AltosEepromMonitor { diff --git a/altoslib/AltosEepromTM.java b/altoslib/AltosEepromTM.java index 2bdd64f0..39bbb336 100644 --- a/altoslib/AltosEepromTM.java +++ b/altoslib/AltosEepromTM.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosEepromTm.java b/altoslib/AltosEepromTm.java index 36500393..2f857667 100644 --- a/altoslib/AltosEepromTm.java +++ b/altoslib/AltosEepromTm.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosFile.java b/altoslib/AltosFile.java index a79216a2..80468079 100644 --- a/altoslib/AltosFile.java +++ b/altoslib/AltosFile.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosFlash.java b/altoslib/AltosFlash.java index e58b652e..95ea73b5 100644 --- a/altoslib/AltosFlash.java +++ b/altoslib/AltosFlash.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.io.*; diff --git a/altoslib/AltosFlashListener.java b/altoslib/AltosFlashListener.java index 1c7cd77d..03404a4e 100644 --- a/altoslib/AltosFlashListener.java +++ b/altoslib/AltosFlashListener.java @@ -15,7 +15,7 @@   * 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); diff --git a/altosuilib/AltosFlightDisplay.java b/altoslib/AltosFlightDisplay.java index ac65c49e..e87865bd 100644 --- a/altosuilib/AltosFlightDisplay.java +++ b/altoslib/AltosFlightDisplay.java @@ -15,9 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altosuilib_6; - -import org.altusmetrum.altoslib_6.*; +package org.altusmetrum.altoslib_7;  public interface AltosFlightDisplay extends AltosUnitsListener, AltosFontListener {  	void reset(); diff --git a/altoslib/AltosFlightReader.java b/altoslib/AltosFlightReader.java index 03c53ff4..9c4b7351 100644 --- a/altoslib/AltosFlightReader.java +++ b/altoslib/AltosFlightReader.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosFlightStats.java b/altoslib/AltosFlightStats.java index 82e477f8..8bdd43cf 100644 --- a/altoslib/AltosFlightStats.java +++ b/altoslib/AltosFlightStats.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.io.*; diff --git a/altosuilib/AltosFontListener.java b/altoslib/AltosFontListener.java index 93625278..0d8effa3 100644 --- a/altosuilib/AltosFontListener.java +++ b/altoslib/AltosFontListener.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_7;  public interface AltosFontListener {  	void font_size_changed(int font_size); diff --git a/altoslib/AltosFrequency.java b/altoslib/AltosFrequency.java index 1dd4819d..6d42fd69 100644 --- a/altoslib/AltosFrequency.java +++ b/altoslib/AltosFrequency.java @@ -15,12 +15,25 @@   * 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); diff --git a/altoslib/AltosGPS.java b/altoslib/AltosGPS.java index 2139efb2..6793adf1 100644 --- a/altoslib/AltosGPS.java +++ b/altoslib/AltosGPS.java @@ -15,7 +15,7 @@   * 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.*; @@ -195,10 +195,10 @@ public class AltosGPS implements Cloneable, Serializable {  			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++]);  			} diff --git a/altoslib/AltosGPSSat.java b/altoslib/AltosGPSSat.java index 57491f4d..3cbdcd9b 100644 --- a/altoslib/AltosGPSSat.java +++ b/altoslib/AltosGPSSat.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosGreatCircle.java b/altoslib/AltosGreatCircle.java index c48755be..4f934517 100644 --- a/altoslib/AltosGreatCircle.java +++ b/altoslib/AltosGreatCircle.java @@ -15,7 +15,7 @@   * 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.*; @@ -28,8 +28,8 @@ public class AltosGreatCircle implements Cloneable, Serializable {  	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; diff --git a/altoslib/AltosHeight.java b/altoslib/AltosHeight.java index c4419ae6..b7b3529b 100644 --- a/altoslib/AltosHeight.java +++ b/altoslib/AltosHeight.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosHexfile.java b/altoslib/AltosHexfile.java index 9f45d65a..6a66a869 100644 --- a/altoslib/AltosHexfile.java +++ b/altoslib/AltosHexfile.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosHexsym.java b/altoslib/AltosHexsym.java index 0c61fd02..fba50527 100644 --- a/altoslib/AltosHexsym.java +++ b/altoslib/AltosHexsym.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosIMU.java b/altoslib/AltosIMU.java index 8c219d9f..fe264d3c 100644 --- a/altoslib/AltosIMU.java +++ b/altoslib/AltosIMU.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosIdle.java b/altoslib/AltosIdle.java index 82b18ca2..89f8ed64 100644 --- a/altoslib/AltosIdle.java +++ b/altoslib/AltosIdle.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosIdleFetch.java b/altoslib/AltosIdleFetch.java index 4c5e8285..9ff5a53e 100644 --- a/altoslib/AltosIdleFetch.java +++ b/altoslib/AltosIdleFetch.java @@ -15,7 +15,7 @@   * 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.*; @@ -146,6 +146,7 @@ public class AltosIdleFetch implements AltosStateUpdate {  			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); diff --git a/altoslib/AltosIdleMonitor.java b/altoslib/AltosIdleMonitor.java index 4d0968bf..74536204 100644 --- a/altoslib/AltosIdleMonitor.java +++ b/altoslib/AltosIdleMonitor.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosIdleMonitorListener.java b/altoslib/AltosIdleMonitorListener.java index fdf4be9d..86135a2e 100644 --- a/altoslib/AltosIdleMonitorListener.java +++ b/altoslib/AltosIdleMonitorListener.java @@ -15,7 +15,7 @@   * 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); diff --git a/altoslib/AltosIgnite.java b/altoslib/AltosIgnite.java index ffb6ed15..8701aec9 100644 --- a/altoslib/AltosIgnite.java +++ b/altoslib/AltosIgnite.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosImage.java b/altoslib/AltosImage.java new file mode 100644 index 00000000..6360ecea --- /dev/null +++ b/altoslib/AltosImage.java @@ -0,0 +1,25 @@ +/* + * 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(); +} diff --git a/altoslib/AltosKML.java b/altoslib/AltosKML.java index aa80fc21..0c9f46c9 100644 --- a/altoslib/AltosKML.java +++ b/altoslib/AltosKML.java @@ -15,14 +15,25 @@   * 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; @@ -137,6 +148,10 @@ public class AltosKML implements AltosWriter {  			end();  			prev = null;  		} +		if (out != null) { +			out.close(); +			out = null; +		}  	}  	public void write(AltosState state) { @@ -177,7 +192,7 @@ public class AltosKML implements AltosWriter {  	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 { diff --git a/altosuilib/AltosUILatLon.java b/altoslib/AltosLatLon.java index 72ff74d8..1bcf6fd8 100644 --- a/altosuilib/AltosUILatLon.java +++ b/altoslib/AltosLatLon.java @@ -15,29 +15,31 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_7; -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 class AltosLatLon {  	public double	lat;  	public double	lon; -	public boolean equals(AltosUILatLon other) { -		if (other == null) +	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 AltosUILatLon(double lat, double lon) { +	public String toString() { +		return String.format("%f/%f", lat, lon); +	} + +	public AltosLatLon(double lat, double lon) {  		this.lat = lat;  		this.lon = lon;  	} diff --git a/altoslib/AltosLatitude.java b/altoslib/AltosLatitude.java index 191a46ee..f4580cde 100644 --- a/altoslib/AltosLatitude.java +++ b/altoslib/AltosLatitude.java @@ -15,7 +15,7 @@   * 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"; } diff --git a/altoslib/AltosLaunchSite.java b/altoslib/AltosLaunchSite.java new file mode 100644 index 00000000..70a4bb58 --- /dev/null +++ b/altoslib/AltosLaunchSite.java @@ -0,0 +1,57 @@ +/* + * 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); +		} +	} +} + diff --git a/altoslib/AltosLaunchSiteListener.java b/altoslib/AltosLaunchSiteListener.java new file mode 100644 index 00000000..21015909 --- /dev/null +++ b/altoslib/AltosLaunchSiteListener.java @@ -0,0 +1,26 @@ +/* + * 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); +} diff --git a/altoslib/AltosLaunchSites.java b/altoslib/AltosLaunchSites.java new file mode 100644 index 00000000..0922bcea --- /dev/null +++ b/altoslib/AltosLaunchSites.java @@ -0,0 +1,73 @@ +/* + * 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(); +	} +} diff --git a/altoslib/AltosLib.java b/altoslib/AltosLib.java index b19f9f52..a4afd269 100644 --- a/altoslib/AltosLib.java +++ b/altoslib/AltosLib.java @@ -15,7 +15,7 @@   * 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.*; @@ -190,6 +190,13 @@ public class AltosLib {  		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"; diff --git a/altoslib/AltosLine.java b/altoslib/AltosLine.java index a65ba593..33737e95 100644 --- a/altoslib/AltosLine.java +++ b/altoslib/AltosLine.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosLink.java b/altoslib/AltosLink.java index 01c37864..e33f7c77 100644 --- a/altoslib/AltosLink.java +++ b/altoslib/AltosLink.java @@ -15,7 +15,7 @@   * 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.*; @@ -86,10 +86,17 @@ public abstract class AltosLink implements Runnable {  	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; @@ -354,7 +361,7 @@ public abstract class AltosLink implements Runnable {  		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 @@ -546,7 +553,15 @@ public abstract class AltosLink implements Runnable {  		}  		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() { diff --git a/altoslib/AltosListenerState.java b/altoslib/AltosListenerState.java index d7e18008..ac31e198 100644 --- a/altoslib/AltosListenerState.java +++ b/altoslib/AltosListenerState.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.io.*; diff --git a/altoslib/AltosLocation.java b/altoslib/AltosLocation.java index b21014db..5fc4a368 100644 --- a/altoslib/AltosLocation.java +++ b/altoslib/AltosLocation.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosLog.java b/altoslib/AltosLog.java index 9241581a..704dfc68 100644 --- a/altoslib/AltosLog.java +++ b/altoslib/AltosLog.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosLongitude.java b/altoslib/AltosLongitude.java index ff4f0c2b..65e1402c 100644 --- a/altoslib/AltosLongitude.java +++ b/altoslib/AltosLongitude.java @@ -15,7 +15,7 @@   * 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"; } diff --git a/altoslib/AltosMag.java b/altoslib/AltosMag.java index f7595639..e03108d6 100644 --- a/altoslib/AltosMag.java +++ b/altoslib/AltosMag.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosMap.java b/altoslib/AltosMap.java new file mode 100644 index 00000000..2c93adb8 --- /dev/null +++ b/altoslib/AltosMap.java @@ -0,0 +1,485 @@ +/* + * 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(); +	} +} diff --git a/altoslib/AltosMapCache.java b/altoslib/AltosMapCache.java new file mode 100644 index 00000000..929fbb01 --- /dev/null +++ b/altoslib/AltosMapCache.java @@ -0,0 +1,207 @@ +/* + * 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); +	} +} diff --git a/altosuilib/AltosUIMapCacheListener.java b/altoslib/AltosMapCacheListener.java index 1eec7b0a..26618c0f 100644 --- a/altosuilib/AltosUIMapCacheListener.java +++ b/altoslib/AltosMapCacheListener.java @@ -15,8 +15,8 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_7; -public interface AltosUIMapCacheListener { +public interface AltosMapCacheListener {  	public void map_cache_changed(int map_cache);  } diff --git a/altoslib/AltosMapInterface.java b/altoslib/AltosMapInterface.java new file mode 100644 index 00000000..7e8dd236 --- /dev/null +++ b/altoslib/AltosMapInterface.java @@ -0,0 +1,47 @@ +/* + * 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); +} diff --git a/altoslib/AltosMapLine.java b/altoslib/AltosMapLine.java new file mode 100644 index 00000000..23a6f889 --- /dev/null +++ b/altoslib/AltosMapLine.java @@ -0,0 +1,81 @@ +/* + * 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); +	} +} diff --git a/altoslib/AltosMapLoader.java b/altoslib/AltosMapLoader.java new file mode 100644 index 00000000..2cd5dbd3 --- /dev/null +++ b/altoslib/AltosMapLoader.java @@ -0,0 +1,205 @@ +/* + * 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; +	} +} diff --git a/altoslib/AltosMapLoaderListener.java b/altoslib/AltosMapLoaderListener.java new file mode 100644 index 00000000..11f59b28 --- /dev/null +++ b/altoslib/AltosMapLoaderListener.java @@ -0,0 +1,28 @@ +/* + * 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); +} diff --git a/altoslib/AltosMapMark.java b/altoslib/AltosMapMark.java new file mode 100644 index 00000000..74e6790f --- /dev/null +++ b/altoslib/AltosMapMark.java @@ -0,0 +1,38 @@ +/* + * 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; +	} +} diff --git a/altoslib/AltosMapPath.java b/altoslib/AltosMapPath.java new file mode 100644 index 00000000..a238ba1a --- /dev/null +++ b/altoslib/AltosMapPath.java @@ -0,0 +1,50 @@ +/* + * 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>(); +	} +} diff --git a/altoslib/AltosMapPathPoint.java b/altoslib/AltosMapPathPoint.java new file mode 100644 index 00000000..0d54744a --- /dev/null +++ b/altoslib/AltosMapPathPoint.java @@ -0,0 +1,50 @@ +/* + * 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; +	} +} + diff --git a/altosuilib/AltosUIMapRectangle.java b/altoslib/AltosMapRectangle.java index dc0e4cc1..941bad9e 100644 --- a/altosuilib/AltosUIMapRectangle.java +++ b/altoslib/AltosMapRectangle.java @@ -15,12 +15,12 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_7; -public class AltosUIMapRectangle { -	AltosUILatLon	ul, lr; +public class AltosMapRectangle { +	AltosLatLon	ul, lr; -	public AltosUIMapRectangle(AltosUILatLon a, AltosUILatLon b) { +	public AltosMapRectangle(AltosLatLon a, AltosLatLon b) {  		double	ul_lat, ul_lon;  		double	lr_lat, lr_lon; @@ -39,7 +39,7 @@ public class AltosUIMapRectangle {  			lr_lon = a.lon;  		} -		ul = new AltosUILatLon(ul_lat, ul_lon); -		lr = new AltosUILatLon(lr_lat, lr_lon); +		ul = new AltosLatLon(ul_lat, ul_lon); +		lr = new AltosLatLon(lr_lat, lr_lon);  	}  } diff --git a/altosuilib/AltosUIMapStore.java b/altoslib/AltosMapStore.java index 70bb6fed..a10a1665 100644 --- a/altosuilib/AltosUIMapStore.java +++ b/altoslib/AltosMapStore.java @@ -15,22 +15,16 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_7;  import java.io.*;  import java.net.*;  import java.util.*; -public class AltosUIMapStore { +public class AltosMapStore {  	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; +	public File				file; +	LinkedList<AltosMapStoreListener>	listeners = new LinkedList<AltosMapStoreListener>();  	int					status; @@ -38,18 +32,18 @@ public class AltosUIMapStore {  		return status;  	} -	public synchronized void add_listener(AltosUIMapStoreListener listener) { +	public synchronized void add_listener(AltosMapStoreListener listener) {  		if (!listeners.contains(listener))  			listeners.add(listener);  	} -	public synchronized void remove_listener(AltosUIMapStoreListener listener) { +	public synchronized void remove_listener(AltosMapStoreListener listener) {  		listeners.remove(listener);  	}  	private synchronized void notify_listeners(int status) {  		this.status = status; -		for (AltosUIMapStoreListener listener : listeners) +		for (AltosMapStoreListener listener : listeners)  			listener.notify_store(this, status);  	} @@ -63,7 +57,7 @@ public class AltosUIMapStore {  		try {  			u = new URL(url);  		} catch (java.net.MalformedURLException e) { -			return bad_request; +			return AltosMapTile.bad_request;  		}  		byte[] data; @@ -81,7 +75,7 @@ public class AltosUIMapStore {  					synchronized (forbidden_lock) {  						forbidden_time = System.nanoTime();  						forbidden_set = true; -						return forbidden; +						return AltosMapTile.forbidden;  					}  				}  			} @@ -98,10 +92,10 @@ public class AltosUIMapStore {  			in.close();  			if (offset != contentLength) -				return failed; +				return AltosMapTile.failed;  		} catch (IOException e) { -			return failed; +			return AltosMapTile.failed;  		}  		try { @@ -110,13 +104,13 @@ public class AltosUIMapStore {  			out.flush();  			out.close();  		} catch (FileNotFoundException e) { -			return bad_request; +			return AltosMapTile.bad_request;  		} catch (IOException e) {  			if (file.exists())  				file.delete(); -			return bad_request; +			return AltosMapTile.bad_request;  		} -		return success; +		return AltosMapTile.success;  	}  	static Object	fetch_lock = new Object(); @@ -124,80 +118,126 @@ public class AltosUIMapStore {  	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() { -			if (file.exists()) { -				notify_listeners(success); -				return; -			} - -			synchronized(forbidden_lock) { -				if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) { -					notify_listeners(forbidden); +			try { +				if (file.exists()) { +					notify_listeners(AltosMapTile.success);  					return;  				} -			} -			int new_status; +				synchronized(forbidden_lock) { +					if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) { +						notify_listeners(AltosMapTile.forbidden); +						return; +					} +				} -			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(); +				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();  				} -			} else { -				new_status = fetch_url(); +				notify_listeners(new_status); +			} finally { +				finish_loader();  			} -			notify_listeners(new_status);  		}  	} +	private Thread make_loader_thread() { +		return new Thread(new loader()); +	} +  	private void load() { -		loader	l = new loader(); -		Thread	lt = new Thread(l); -		lt.start(); +		add_loader();  	} -	private AltosUIMapStore (String url, File file) { +	private AltosMapStore (String url, File file) {  		this.url = url;  		this.file = file;  		if (file.exists()) -			status = success; +			status = AltosMapTile.success;  		else { -			status = loading; +			status = AltosMapTile.loading;  			load();  		}  	} -	public boolean equals(AltosUIMapStore other) { +	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,AltosUIMapStore> stores = new HashMap<String,AltosUIMapStore>(); +	static HashMap<String,AltosMapStore> stores = new HashMap<String,AltosMapStore>(); -	public static AltosUIMapStore get(String url, File file) { -		AltosUIMapStore	store; +	public static AltosMapStore get(String url, File file) { +		AltosMapStore	store;  		synchronized(stores) {  			if (stores.containsKey(url)) {  				store = stores.get(url);  			} else { -				store = new AltosUIMapStore(url, file); +				store = new AltosMapStore(url, file);  				stores.put(url, store);  			}  		}  		return store;  	} -  } diff --git a/altosuilib/AltosUIMapStoreListener.java b/altoslib/AltosMapStoreListener.java index ccda8983..b744b88c 100644 --- a/altosuilib/AltosUIMapStoreListener.java +++ b/altoslib/AltosMapStoreListener.java @@ -15,8 +15,8 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_7; -public interface AltosUIMapStoreListener { -	abstract void notify_store(AltosUIMapStore store, int status); +public interface AltosMapStoreListener { +	abstract void notify_store(AltosMapStore store, int status);  } diff --git a/altoslib/AltosMapTile.java b/altoslib/AltosMapTile.java new file mode 100644 index 00000000..ee9206ee --- /dev/null +++ b/altoslib/AltosMapTile.java @@ -0,0 +1,128 @@ +/* + * 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); +	} +} diff --git a/altosuilib/AltosUIMapTileListener.java b/altoslib/AltosMapTileListener.java index dace5b76..ed47e833 100644 --- a/altosuilib/AltosUIMapTileListener.java +++ b/altoslib/AltosMapTileListener.java @@ -15,10 +15,10 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_7; -public interface AltosUIMapTileListener { -	abstract public void notify_tile(AltosUIMapTile tile, int status); +public interface AltosMapTileListener { +	abstract public void notify_tile(AltosMapTile tile, int status); -	abstract public AltosUIMapCache cache(); +	abstract public AltosMapCache cache();  } diff --git a/altoslib/AltosMapTransform.java b/altoslib/AltosMapTransform.java new file mode 100644 index 00000000..7615c83b --- /dev/null +++ b/altoslib/AltosMapTransform.java @@ -0,0 +1,128 @@ +/* + * 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; +	} +} diff --git a/altosuilib/AltosUIMapZoomListener.java b/altoslib/AltosMapZoomListener.java index 9f74baca..d50774b4 100644 --- a/altosuilib/AltosUIMapZoomListener.java +++ b/altoslib/AltosMapZoomListener.java @@ -15,8 +15,8 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_7; -public interface AltosUIMapZoomListener { +public interface AltosMapZoomListener {  	abstract public void zoom_changed(int zoom);  } diff --git a/altoslib/AltosMma655x.java b/altoslib/AltosMma655x.java index c0b94b8c..a24e76bd 100644 --- a/altoslib/AltosMma655x.java +++ b/altoslib/AltosMma655x.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.util.concurrent.*; diff --git a/altoslib/AltosMs5607.java b/altoslib/AltosMs5607.java index 2bd4ba8f..8e6f2658 100644 --- a/altoslib/AltosMs5607.java +++ b/altoslib/AltosMs5607.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosNoSymbol.java b/altoslib/AltosNoSymbol.java index 77410a25..2e1da598 100644 --- a/altoslib/AltosNoSymbol.java +++ b/altoslib/AltosNoSymbol.java @@ -15,7 +15,7 @@   * 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) { diff --git a/altoslib/AltosOrient.java b/altoslib/AltosOrient.java index 8cdde750..d9350554 100644 --- a/altoslib/AltosOrient.java +++ b/altoslib/AltosOrient.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosParse.java b/altoslib/AltosParse.java index 2fb69c15..de79c2fb 100644 --- a/altoslib/AltosParse.java +++ b/altoslib/AltosParse.java @@ -15,8 +15,9 @@   * 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 { @@ -40,11 +41,23 @@ 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);  		}  	} diff --git a/altoslib/AltosPointDouble.java b/altoslib/AltosPointDouble.java new file mode 100644 index 00000000..45e7785e --- /dev/null +++ b/altoslib/AltosPointDouble.java @@ -0,0 +1,53 @@ +/* + * 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; +	} +} diff --git a/altoslib/AltosPointInt.java b/altoslib/AltosPointInt.java new file mode 100644 index 00000000..a7dd00f7 --- /dev/null +++ b/altoslib/AltosPointInt.java @@ -0,0 +1,53 @@ +/* + * 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); +	} +} diff --git a/altoslib/AltosPreferences.java b/altoslib/AltosPreferences.java index 5aa45d3f..cdff93f6 100644 --- a/altoslib/AltosPreferences.java +++ b/altoslib/AltosPreferences.java @@ -15,10 +15,11 @@   * 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; @@ -42,7 +43,9 @@ public class AltosPreferences {  	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"; @@ -118,6 +121,13 @@ public class AltosPreferences {  	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; @@ -208,6 +218,9 @@ public class AltosPreferences {  		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() { @@ -350,12 +363,43 @@ public class AltosPreferences {  			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; @@ -548,4 +592,33 @@ public class AltosPreferences {  			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; +		} +	}  } diff --git a/altoslib/AltosPreferencesBackend.java b/altoslib/AltosPreferencesBackend.java index c83eaa47..b68eea19 100644 --- a/altoslib/AltosPreferencesBackend.java +++ b/altoslib/AltosPreferencesBackend.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.io.File; diff --git a/altoslib/AltosProgrammer.java b/altoslib/AltosProgrammer.java index 7a92c2d0..6c53e1db 100644 --- a/altoslib/AltosProgrammer.java +++ b/altoslib/AltosProgrammer.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.io.*; diff --git a/altoslib/AltosPyro.java b/altoslib/AltosPyro.java index 502e34de..c5b3e0eb 100644 --- a/altoslib/AltosPyro.java +++ b/altoslib/AltosPyro.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosQuaternion.java b/altoslib/AltosQuaternion.java index 4ad1f3d6..af7667d0 100644 --- a/altoslib/AltosQuaternion.java +++ b/altoslib/AltosQuaternion.java @@ -15,7 +15,7 @@   * 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 */ diff --git a/altoslib/AltosRectangle.java b/altoslib/AltosRectangle.java new file mode 100644 index 00000000..cfa2ea60 --- /dev/null +++ b/altoslib/AltosRectangle.java @@ -0,0 +1,29 @@ +/* + * 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; +	} +} diff --git a/altoslib/AltosReplayReader.java b/altoslib/AltosReplayReader.java index 98f0e7d5..90072754 100644 --- a/altoslib/AltosReplayReader.java +++ b/altoslib/AltosReplayReader.java @@ -15,7 +15,7 @@   * 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.*; @@ -40,7 +40,7 @@ public class AltosReplayReader extends AltosFlightReader {  	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());  	} diff --git a/altoslib/AltosRomconfig.java b/altoslib/AltosRomconfig.java index c93a01c3..d0912f4b 100644 --- a/altoslib/AltosRomconfig.java +++ b/altoslib/AltosRomconfig.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.io.*; diff --git a/altoslib/AltosRotation.java b/altoslib/AltosRotation.java index 49225f77..4e4127cf 100644 --- a/altoslib/AltosRotation.java +++ b/altoslib/AltosRotation.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosSavedState.java b/altoslib/AltosSavedState.java index 552e4533..f557801a 100644 --- a/altoslib/AltosSavedState.java +++ b/altoslib/AltosSavedState.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.io.*; diff --git a/altoslib/AltosSelfFlash.java b/altoslib/AltosSelfFlash.java index 83be4be1..1be45998 100644 --- a/altoslib/AltosSelfFlash.java +++ b/altoslib/AltosSelfFlash.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.io.*; diff --git a/altoslib/AltosSensorEMini.java b/altoslib/AltosSensorEMini.java index cb8356a1..cdcd9f20 100644 --- a/altoslib/AltosSensorEMini.java +++ b/altoslib/AltosSensorEMini.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosSensorMM.java b/altoslib/AltosSensorMM.java index 9d5649aa..95edea22 100644 --- a/altoslib/AltosSensorMM.java +++ b/altoslib/AltosSensorMM.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosSensorMega.java b/altoslib/AltosSensorMega.java index a3c2a033..939bae16 100644 --- a/altoslib/AltosSensorMega.java +++ b/altoslib/AltosSensorMega.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosSensorMetrum.java b/altoslib/AltosSensorMetrum.java index 39592b50..6feb26c0 100644 --- a/altoslib/AltosSensorMetrum.java +++ b/altoslib/AltosSensorMetrum.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosSensorTGPS.java b/altoslib/AltosSensorTGPS.java index 32607fba..e9265bce 100644 --- a/altoslib/AltosSensorTGPS.java +++ b/altoslib/AltosSensorTGPS.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosSensorTM.java b/altoslib/AltosSensorTM.java index c82ba93c..1c439817 100644 --- a/altoslib/AltosSensorTM.java +++ b/altoslib/AltosSensorTM.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosSensorTMini.java b/altoslib/AltosSensorTMini.java index 0fc70e71..ee51a8f6 100644 --- a/altoslib/AltosSensorTMini.java +++ b/altoslib/AltosSensorTMini.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosSpeed.java b/altoslib/AltosSpeed.java index b714412f..093d196d 100644 --- a/altoslib/AltosSpeed.java +++ b/altoslib/AltosSpeed.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosState.java b/altoslib/AltosState.java index d363027c..347c1198 100644 --- a/altoslib/AltosState.java +++ b/altoslib/AltosState.java @@ -19,7 +19,7 @@   * Track flight state from telemetry or eeprom data stream   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.io.*; @@ -31,8 +31,9 @@ public class AltosState implements Cloneable, Serializable {  	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 */ @@ -64,8 +65,10 @@ public class AltosState implements Cloneable, Serializable {  		}  		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);  		} @@ -1040,6 +1043,10 @@ public class AltosState implements Cloneable, Serializable {  		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; diff --git a/altoslib/AltosStateIterable.java b/altoslib/AltosStateIterable.java index 3b58fc32..6202f970 100644 --- a/altoslib/AltosStateIterable.java +++ b/altoslib/AltosStateIterable.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosStateUpdate.java b/altoslib/AltosStateUpdate.java index 93b9f1c0..55007eb1 100644 --- a/altoslib/AltosStateUpdate.java +++ b/altoslib/AltosStateUpdate.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosTelemetry.java b/altoslib/AltosTelemetry.java index 449384d5..3825ab8f 100644 --- a/altoslib/AltosTelemetry.java +++ b/altoslib/AltosTelemetry.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.text.*; diff --git a/altoslib/AltosTelemetryConfiguration.java b/altoslib/AltosTelemetryConfiguration.java index 8c922b03..1cce0f24 100644 --- a/altoslib/AltosTelemetryConfiguration.java +++ b/altoslib/AltosTelemetryConfiguration.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosTelemetryFile.java b/altoslib/AltosTelemetryFile.java index 738e4dd7..d28795c4 100644 --- a/altoslib/AltosTelemetryFile.java +++ b/altoslib/AltosTelemetryFile.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosTelemetryIterable.java b/altoslib/AltosTelemetryIterable.java index 131389d5..ced29121 100644 --- a/altoslib/AltosTelemetryIterable.java +++ b/altoslib/AltosTelemetryIterable.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosTelemetryLegacy.java b/altoslib/AltosTelemetryLegacy.java index 923d139e..5aad8e62 100644 --- a/altoslib/AltosTelemetryLegacy.java +++ b/altoslib/AltosTelemetryLegacy.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  import java.text.*; diff --git a/altoslib/AltosTelemetryLocation.java b/altoslib/AltosTelemetryLocation.java index 85da27d5..2cd75621 100644 --- a/altoslib/AltosTelemetryLocation.java +++ b/altoslib/AltosTelemetryLocation.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosTelemetryMap.java b/altoslib/AltosTelemetryMap.java index e8c02e9b..36b5b1c8 100644 --- a/altoslib/AltosTelemetryMap.java +++ b/altoslib/AltosTelemetryMap.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosTelemetryMegaData.java b/altoslib/AltosTelemetryMegaData.java index 2b80df4a..30752365 100644 --- a/altoslib/AltosTelemetryMegaData.java +++ b/altoslib/AltosTelemetryMegaData.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosTelemetryMegaSensor.java b/altoslib/AltosTelemetryMegaSensor.java index a01c0826..bb3fdb37 100644 --- a/altoslib/AltosTelemetryMegaSensor.java +++ b/altoslib/AltosTelemetryMegaSensor.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosTelemetryMetrumData.java b/altoslib/AltosTelemetryMetrumData.java index e53f1283..6d593467 100644 --- a/altoslib/AltosTelemetryMetrumData.java +++ b/altoslib/AltosTelemetryMetrumData.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosTelemetryMetrumSensor.java b/altoslib/AltosTelemetryMetrumSensor.java index 415b00a6..6d4cbea0 100644 --- a/altoslib/AltosTelemetryMetrumSensor.java +++ b/altoslib/AltosTelemetryMetrumSensor.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosTelemetryMini.java b/altoslib/AltosTelemetryMini.java index 02537c9c..fa722fa5 100644 --- a/altoslib/AltosTelemetryMini.java +++ b/altoslib/AltosTelemetryMini.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosTelemetryRaw.java b/altoslib/AltosTelemetryRaw.java index 4254fc83..a6e5ca49 100644 --- a/altoslib/AltosTelemetryRaw.java +++ b/altoslib/AltosTelemetryRaw.java @@ -15,7 +15,7 @@   * 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) { diff --git a/altoslib/AltosTelemetryReader.java b/altoslib/AltosTelemetryReader.java index 908fb5c7..cd5a08c4 100644 --- a/altoslib/AltosTelemetryReader.java +++ b/altoslib/AltosTelemetryReader.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altoslib/AltosTelemetrySatellite.java b/altoslib/AltosTelemetrySatellite.java index 474789ba..e3d52b46 100644 --- a/altoslib/AltosTelemetrySatellite.java +++ b/altoslib/AltosTelemetrySatellite.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosTelemetrySensor.java b/altoslib/AltosTelemetrySensor.java index b0c84fd3..8dd2c225 100644 --- a/altoslib/AltosTelemetrySensor.java +++ b/altoslib/AltosTelemetrySensor.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosTelemetryStandard.java b/altoslib/AltosTelemetryStandard.java index fb8a162e..d4aaff5b 100644 --- a/altoslib/AltosTelemetryStandard.java +++ b/altoslib/AltosTelemetryStandard.java @@ -15,7 +15,7 @@   * 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; diff --git a/altoslib/AltosTemperature.java b/altoslib/AltosTemperature.java index 494f4e3e..d729b576 100644 --- a/altoslib/AltosTemperature.java +++ b/altoslib/AltosTemperature.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosUnits.java b/altoslib/AltosUnits.java index f6e34e77..7ca2f03c 100644 --- a/altoslib/AltosUnits.java +++ b/altoslib/AltosUnits.java @@ -15,7 +15,9 @@   * 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 { @@ -29,13 +31,22 @@ 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) { @@ -105,4 +116,4 @@ public abstract class AltosUnits {  	public String say_units(double v) {  		return say_units(v, AltosConvert.imperial_units);  	} -}
\ No newline at end of file +} diff --git a/altoslib/AltosUnitsListener.java b/altoslib/AltosUnitsListener.java index 1b66eb45..c5539fc9 100644 --- a/altoslib/AltosUnitsListener.java +++ b/altoslib/AltosUnitsListener.java @@ -15,7 +15,7 @@   * 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); diff --git a/altosuilib/AltosUIVersion.java.in b/altoslib/AltosVersion.java.in index beb62cbf..ebba5a00 100644 --- a/altosuilib/AltosUIVersion.java.in +++ b/altoslib/AltosVersion.java.in @@ -15,14 +15,14 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_7; -public class AltosUIVersion { +public class AltosVersion {  	public final static String version = "@VERSION@";  	public final static String google_maps_api_key = @GOOGLEKEY@; -	static boolean has_google_maps_api_key() { +	public static boolean has_google_maps_api_key() {  		return google_maps_api_key != null && google_maps_api_key.length() > 1;  	}  } diff --git a/altoslib/AltosVoltage.java b/altoslib/AltosVoltage.java index 986d74b4..0469fb14 100644 --- a/altoslib/AltosVoltage.java +++ b/altoslib/AltosVoltage.java @@ -15,7 +15,7 @@   * 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 { diff --git a/altoslib/AltosWriter.java b/altoslib/AltosWriter.java index b125bd87..70fadd2e 100644 --- a/altoslib/AltosWriter.java +++ b/altoslib/AltosWriter.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_7;  public interface AltosWriter { diff --git a/altoslib/Makefile.am b/altoslib/Makefile.am index c640c69c..a6b178fa 100644 --- a/altoslib/Makefile.am +++ b/altoslib/Makefile.am @@ -128,7 +128,35 @@ altoslib_JAVA = \  	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 diff --git a/altosui/Altos.java b/altosui/Altos.java index e82931b2..a4077a38 100644 --- a/altosui/Altos.java +++ b/altosui/Altos.java @@ -20,8 +20,8 @@ package altosui;  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 { diff --git a/altosui/AltosAscent.java b/altosui/AltosAscent.java index 10080efd..ef7c1cdb 100644 --- a/altosui/AltosAscent.java +++ b/altosui/AltosAscent.java @@ -21,8 +21,8 @@ import java.util.*;  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; diff --git a/altosui/AltosCompanionInfo.java b/altosui/AltosCompanionInfo.java index 68dab227..20004ad4 100644 --- a/altosui/AltosCompanionInfo.java +++ b/altosui/AltosCompanionInfo.java @@ -19,8 +19,8 @@ package altosui;  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; diff --git a/altosui/AltosConfig.java b/altosui/AltosConfig.java index 3c5415d2..570354a3 100644 --- a/altosui/AltosConfig.java +++ b/altosui/AltosConfig.java @@ -22,8 +22,8 @@ import javax.swing.*;  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 { diff --git a/altosui/AltosConfigPyroUI.java b/altosui/AltosConfigPyroUI.java index 61208dfe..694226be 100644 --- a/altosui/AltosConfigPyroUI.java +++ b/altosui/AltosConfigPyroUI.java @@ -17,12 +17,13 @@  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 @@ -87,9 +88,9 @@ public class AltosConfigPyroUI  			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);  				}  			} @@ -129,9 +130,9 @@ public class AltosConfigPyroUI  				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());  				}  			} @@ -298,8 +299,8 @@ public class AltosConfigPyroUI  		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);  		}  	} diff --git a/altosui/AltosConfigTD.java b/altosui/AltosConfigTD.java index cd20a174..9b9cdcaa 100644 --- a/altosui/AltosConfigTD.java +++ b/altosui/AltosConfigTD.java @@ -21,8 +21,8 @@ import java.awt.event.*;  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 { diff --git a/altosui/AltosConfigTDUI.java b/altosui/AltosConfigTDUI.java index 52225108..e17af47d 100644 --- a/altosui/AltosConfigTDUI.java +++ b/altosui/AltosConfigTDUI.java @@ -21,8 +21,8 @@ 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 AltosConfigTDUI  	extends AltosUIDialog diff --git a/altosui/AltosConfigUI.java b/altosui/AltosConfigUI.java index 54f06065..3872ff83 100644 --- a/altosui/AltosConfigUI.java +++ b/altosui/AltosConfigUI.java @@ -21,8 +21,9 @@ 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 java.text.*; +import org.altusmetrum.altoslib_7.*; +import org.altusmetrum.altosuilib_7.*;  public class AltosConfigUI  	extends AltosUIDialog @@ -43,6 +44,7 @@ public class AltosConfigUI  	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; @@ -66,6 +68,7 @@ public class AltosConfigUI  	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; @@ -218,11 +221,20 @@ public class AltosConfigUI  	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() { @@ -577,6 +589,33 @@ public class AltosConfigUI  		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; @@ -938,8 +977,13 @@ public class AltosConfigUI  	} -	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() { @@ -968,14 +1012,21 @@ public class AltosConfigUI  		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) @@ -1234,7 +1285,12 @@ public class AltosConfigUI  	}  	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) { @@ -1313,4 +1369,16 @@ public class AltosConfigUI  		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(); +	}  } diff --git a/altosui/AltosConfigureUI.java b/altosui/AltosConfigureUI.java index 7bc50570..543711b2 100644 --- a/altosui/AltosConfigureUI.java +++ b/altosui/AltosConfigureUI.java @@ -22,7 +22,7 @@ import java.awt.event.*;  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 diff --git a/altosui/AltosDescent.java b/altosui/AltosDescent.java index 0db1a4c2..d37f6f66 100644 --- a/altosui/AltosDescent.java +++ b/altosui/AltosDescent.java @@ -21,8 +21,8 @@ import java.util.*;  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 { diff --git a/altosui/AltosFlightStatus.java b/altosui/AltosFlightStatus.java index c0d3312b..42127b1c 100644 --- a/altosui/AltosFlightStatus.java +++ b/altosui/AltosFlightStatus.java @@ -19,8 +19,8 @@ package altosui;  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; diff --git a/altosui/AltosFlightStatusTableModel.java b/altosui/AltosFlightStatusTableModel.java index f2031698..5238e14b 100644 --- a/altosui/AltosFlightStatusTableModel.java +++ b/altosui/AltosFlightStatusTableModel.java @@ -27,7 +27,7 @@ import java.util.*;  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 = { diff --git a/altosui/AltosFlightStatusUpdate.java b/altosui/AltosFlightStatusUpdate.java index 3f6494b8..44c3261a 100644 --- a/altosui/AltosFlightStatusUpdate.java +++ b/altosui/AltosFlightStatusUpdate.java @@ -18,7 +18,7 @@  package altosui;  import java.awt.event.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_7.*;  public class AltosFlightStatusUpdate implements ActionListener { diff --git a/altosui/AltosFlightUI.java b/altosui/AltosFlightUI.java index 424c57da..009ba4ad 100644 --- a/altosui/AltosFlightUI.java +++ b/altosui/AltosFlightUI.java @@ -22,8 +22,8 @@ import java.awt.event.*;  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; @@ -40,7 +40,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {  	AltosDescent	descent;  	AltosLanded	landed;  	AltosCompanionInfo	companion; -	AltosUIMap      sitemap; +	AltosUIMapNew      sitemap;  	boolean		has_map;  	boolean		has_companion;  	boolean		has_state; @@ -189,12 +189,12 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {  		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"); @@ -210,14 +210,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {  						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)); @@ -233,14 +226,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {  					}  				});  			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)) { @@ -252,14 +238,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {  							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; @@ -271,26 +250,17 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {  					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 @@ -319,17 +289,12 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {  		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); diff --git a/altosui/AltosGraphUI.java b/altosui/AltosGraphUI.java index f5e339a5..70543610 100644 --- a/altosui/AltosGraphUI.java +++ b/altosui/AltosGraphUI.java @@ -23,8 +23,8 @@ import java.util.ArrayList;  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; @@ -35,7 +35,7 @@ public class AltosGraphUI extends AltosUIFrame implements AltosFontListener, Alt  	JTabbedPane		pane;  	AltosGraph		graph;  	AltosUIEnable		enable; -	AltosUIMap		map; +	AltosUIMapNew		map;  	AltosState		state;  	AltosGraphDataSet	graphDataSet;  	AltosFlightStats	stats; @@ -47,7 +47,7 @@ public class AltosGraphUI extends AltosUIFrame implements AltosFontListener, Alt  		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;  			} diff --git a/altosui/AltosIdleMonitorUI.java b/altosui/AltosIdleMonitorUI.java index 204d81ed..7f6abbde 100644 --- a/altosui/AltosIdleMonitorUI.java +++ b/altosui/AltosIdleMonitorUI.java @@ -24,8 +24,8 @@ import javax.swing.event.*;  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; @@ -35,9 +35,11 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl  	AltosFlightStatus	flightStatus;  	AltosIgnitor		ignitor;  	AltosIdleMonitor	thread; +	AltosUIMapNew      	sitemap;  	int			serial;  	boolean			remote;  	boolean			has_ignitor; +	boolean			has_map;  	void stop_display() {  		if (thread != null) { @@ -83,11 +85,26 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl  				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);  //		} @@ -119,6 +136,7 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl  	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);  		} @@ -132,30 +150,6 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl  		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, @@ -209,11 +203,14 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl  		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()); @@ -222,6 +219,8 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl  		/* 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() { @@ -238,15 +237,17 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl  			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 @@ -261,8 +262,10 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl  		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); diff --git a/altosui/AltosIgniteUI.java b/altosui/AltosIgniteUI.java index 1a2dc4f1..4fff5d72 100644 --- a/altosui/AltosIgniteUI.java +++ b/altosui/AltosIgniteUI.java @@ -24,8 +24,8 @@ import java.io.*;  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 diff --git a/altosui/AltosIgnitor.java b/altosui/AltosIgnitor.java index 82582a28..deb96a5e 100644 --- a/altosui/AltosIgnitor.java +++ b/altosui/AltosIgnitor.java @@ -20,8 +20,8 @@ package altosui;  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 { diff --git a/altosui/AltosLanded.java b/altosui/AltosLanded.java index 2237df0c..6a4bdbd7 100644 --- a/altosui/AltosLanded.java +++ b/altosui/AltosLanded.java @@ -21,8 +21,8 @@ import java.awt.*;  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 { diff --git a/altosui/AltosLaunch.java b/altosui/AltosLaunch.java index 37077c42..67bbddc2 100644 --- a/altosui/AltosLaunch.java +++ b/altosui/AltosLaunch.java @@ -20,7 +20,7 @@ package altosui;  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; diff --git a/altosui/AltosLaunchUI.java b/altosui/AltosLaunchUI.java index 4c205d44..52587e58 100644 --- a/altosui/AltosLaunchUI.java +++ b/altosui/AltosLaunchUI.java @@ -23,7 +23,7 @@ import javax.swing.*;  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) { diff --git a/altosui/AltosPad.java b/altosui/AltosPad.java index 6214fa5a..4f3ea137 100644 --- a/altosui/AltosPad.java +++ b/altosui/AltosPad.java @@ -18,8 +18,8 @@  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 { @@ -103,9 +103,12 @@ 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; @@ -243,12 +246,12 @@ public class AltosPad extends AltosUIFlightTab {  	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++)); diff --git a/altosui/AltosUI.java b/altosui/AltosUI.java index 0af09651..32490b65 100644 --- a/altosui/AltosUI.java +++ b/altosui/AltosUI.java @@ -22,8 +22,8 @@ import java.awt.event.*;  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(); @@ -295,7 +295,7 @@ public class AltosUI extends AltosUIFrame {  	}  	void LoadMaps() { -		new AltosUIMapPreload(AltosUI.this); +		new AltosUIMapPreloadNew(AltosUI.this);  	}  	void LaunchController() { @@ -587,16 +587,7 @@ public class AltosUI extends AltosUIFrame {  			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; diff --git a/altosui/altos-windows.nsi.in b/altosui/altos-windows.nsi.in index 6d4dabfa..2f22a4a3 100644 --- a/altosui/altos-windows.nsi.in +++ b/altosui/altos-windows.nsi.in @@ -103,16 +103,17 @@ Section "${REG_NAME} Application"  	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" @@ -153,7 +154,6 @@ Section "File Associations"  	SetOutPath $INSTDIR -	File "../icon/${WIN_APP_EXE}"  	File "../icon/${WIN_TELEM_EXE}"  	File "../icon/${WIN_EEPROM_EXE}" @@ -165,15 +165,13 @@ Section "File Associations"  	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" @@ -188,7 +186,7 @@ Section "File Associations"  	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" diff --git a/altosuilib/.gitignore b/altosuilib/.gitignore index 4ad8a77a..943408ec 100644 --- a/altosuilib/.gitignore +++ b/altosuilib/.gitignore @@ -1,4 +1,3 @@ -AltosUIVersion.java  bin  classaltosuilib.stamp  altosuilib*.jar diff --git a/altosuilib/AltosBTDevice.java b/altosuilib/AltosBTDevice.java index 0dd1cab2..d6eed64d 100644 --- a/altosuilib/AltosBTDevice.java +++ b/altosuilib/AltosBTDevice.java @@ -15,10 +15,10 @@   * 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 { @@ -104,17 +104,20 @@ 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); diff --git a/altosuilib/AltosBTDeviceIterator.java b/altosuilib/AltosBTDeviceIterator.java index eebad36b..1e46899a 100644 --- a/altosuilib/AltosBTDeviceIterator.java +++ b/altosuilib/AltosBTDeviceIterator.java @@ -15,11 +15,11 @@   * 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; diff --git a/altosuilib/AltosBTKnown.java b/altosuilib/AltosBTKnown.java index 73ee473f..27897836 100644 --- a/altosuilib/AltosBTKnown.java +++ b/altosuilib/AltosBTKnown.java @@ -15,10 +15,10 @@   * 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>(); diff --git a/altosuilib/AltosBTManage.java b/altosuilib/AltosBTManage.java index c4ac363f..94468a03 100644 --- a/altosuilib/AltosBTManage.java +++ b/altosuilib/AltosBTManage.java @@ -15,7 +15,7 @@   * 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.*; @@ -23,7 +23,7 @@ import javax.swing.*;  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; diff --git a/altosuilib/AltosCSVUI.java b/altosuilib/AltosCSVUI.java index 94d523fe..353b5127 100644 --- a/altosuilib/AltosCSVUI.java +++ b/altosuilib/AltosCSVUI.java @@ -15,13 +15,13 @@   * 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 diff --git a/altosuilib/AltosConfigFreqUI.java b/altosuilib/AltosConfigFreqUI.java index 6253e3e4..de7b4bed 100644 --- a/altosuilib/AltosConfigFreqUI.java +++ b/altosuilib/AltosConfigFreqUI.java @@ -15,13 +15,14 @@   * 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; @@ -51,10 +52,10 @@ class AltosEditFreqUI extends AltosUIDialog implements ActionListener {  		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;  	} diff --git a/altosuilib/AltosDataChooser.java b/altosuilib/AltosDataChooser.java index 3fe76687..ea8a5648 100644 --- a/altosuilib/AltosDataChooser.java +++ b/altosuilib/AltosDataChooser.java @@ -15,12 +15,12 @@   * 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; diff --git a/altosuilib/AltosDevice.java b/altosuilib/AltosDevice.java index 401387a4..b7cfe97e 100644 --- a/altosuilib/AltosDevice.java +++ b/altosuilib/AltosDevice.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_7;  import libaltosJNI.*; diff --git a/altosuilib/AltosDeviceDialog.java b/altosuilib/AltosDeviceDialog.java index 5fb1331e..53e3ed84 100644 --- a/altosuilib/AltosDeviceDialog.java +++ b/altosuilib/AltosDeviceDialog.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altosuilib/AltosDeviceUIDialog.java b/altosuilib/AltosDeviceUIDialog.java index 9618895e..d82892a5 100644 --- a/altosuilib/AltosDeviceUIDialog.java +++ b/altosuilib/AltosDeviceUIDialog.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altosuilib/AltosDisplayThread.java b/altosuilib/AltosDisplayThread.java index ccf8b3ef..d3783860 100644 --- a/altosuilib/AltosDisplayThread.java +++ b/altosuilib/AltosDisplayThread.java @@ -15,13 +15,13 @@   * 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 { diff --git a/altosuilib/AltosEepromDelete.java b/altosuilib/AltosEepromDelete.java index 9fb21cf4..57ebb4c1 100644 --- a/altosuilib/AltosEepromDelete.java +++ b/altosuilib/AltosEepromDelete.java @@ -15,13 +15,13 @@   * 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; diff --git a/altosuilib/AltosEepromManage.java b/altosuilib/AltosEepromManage.java index 47a62ef7..522fac45 100644 --- a/altosuilib/AltosEepromManage.java +++ b/altosuilib/AltosEepromManage.java @@ -15,13 +15,13 @@   * 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 { diff --git a/altosuilib/AltosEepromMonitor.java b/altosuilib/AltosEepromMonitor.java index 83c5c1d0..52a4b640 100644 --- a/altosuilib/AltosEepromMonitor.java +++ b/altosuilib/AltosEepromMonitor.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altosuilib/AltosEepromMonitorUI.java b/altosuilib/AltosEepromMonitorUI.java index ac39fb1d..dd01ce25 100644 --- a/altosuilib/AltosEepromMonitorUI.java +++ b/altosuilib/AltosEepromMonitorUI.java @@ -15,12 +15,12 @@   * 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; diff --git a/altosuilib/AltosEepromSelect.java b/altosuilib/AltosEepromSelect.java index b291c35a..2c9d10cf 100644 --- a/altosuilib/AltosEepromSelect.java +++ b/altosuilib/AltosEepromSelect.java @@ -15,13 +15,13 @@   * 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; diff --git a/altosuilib/AltosFlashUI.java b/altosuilib/AltosFlashUI.java index ace8fbe5..6b4a5238 100644 --- a/altosuilib/AltosFlashUI.java +++ b/altosuilib/AltosFlashUI.java @@ -15,7 +15,7 @@   * 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.*; @@ -23,7 +23,7 @@ import javax.swing.*;  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 diff --git a/altosuilib/AltosFlightInfoTableModel.java b/altosuilib/AltosFlightInfoTableModel.java index f9d7d180..76fe682a 100644 --- a/altosuilib/AltosFlightInfoTableModel.java +++ b/altosuilib/AltosFlightInfoTableModel.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_7;  import javax.swing.table.*; diff --git a/altosuilib/AltosFlightStatsTable.java b/altosuilib/AltosFlightStatsTable.java index 8a686646..4240bf9f 100644 --- a/altosuilib/AltosFlightStatsTable.java +++ b/altosuilib/AltosFlightStatsTable.java @@ -15,12 +15,12 @@   * 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; diff --git a/altosuilib/AltosGraph.java b/altosuilib/AltosGraph.java index d7739228..34a5e1b3 100644 --- a/altosuilib/AltosGraph.java +++ b/altosuilib/AltosGraph.java @@ -15,14 +15,14 @@   * 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.*; diff --git a/altosuilib/AltosGraphDataPoint.java b/altosuilib/AltosGraphDataPoint.java index 47989d0e..b849dd7c 100644 --- a/altosuilib/AltosGraphDataPoint.java +++ b/altosuilib/AltosGraphDataPoint.java @@ -15,9 +15,9 @@   * 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 { diff --git a/altosuilib/AltosGraphDataSet.java b/altosuilib/AltosGraphDataSet.java index b9c9d2a8..f0cda986 100644 --- a/altosuilib/AltosGraphDataSet.java +++ b/altosuilib/AltosGraphDataSet.java @@ -15,12 +15,12 @@   * 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; diff --git a/altosuilib/AltosInfoTable.java b/altosuilib/AltosInfoTable.java index 89a656c9..68008b2f 100644 --- a/altosuilib/AltosInfoTable.java +++ b/altosuilib/AltosInfoTable.java @@ -15,13 +15,13 @@   * 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; @@ -137,6 +137,8 @@ public class AltosInfoTable extends JTable implements AltosFlightDisplay, Hierar  		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) diff --git a/altosuilib/AltosLed.java b/altosuilib/AltosLed.java index fa33c4b6..e328b717 100644 --- a/altosuilib/AltosLed.java +++ b/altosuilib/AltosLed.java @@ -15,7 +15,7 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_7;  import javax.swing.*; diff --git a/altosuilib/AltosLights.java b/altosuilib/AltosLights.java index 05d06ac4..b2c1cbcc 100644 --- a/altosuilib/AltosLights.java +++ b/altosuilib/AltosLights.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altosuilib/AltosPositionListener.java b/altosuilib/AltosPositionListener.java index 1274a64a..81e2cbaf 100644 --- a/altosuilib/AltosPositionListener.java +++ b/altosuilib/AltosPositionListener.java @@ -15,7 +15,7 @@   * 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); diff --git a/altosuilib/AltosRomconfigUI.java b/altosuilib/AltosRomconfigUI.java index 99e4d004..e7977cf4 100644 --- a/altosuilib/AltosRomconfigUI.java +++ b/altosuilib/AltosRomconfigUI.java @@ -15,12 +15,12 @@   * 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 diff --git a/altosuilib/AltosScanUI.java b/altosuilib/AltosScanUI.java index 5a7e21b1..fccfbaee 100644 --- a/altosuilib/AltosScanUI.java +++ b/altosuilib/AltosScanUI.java @@ -15,7 +15,7 @@   * 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.*; @@ -25,7 +25,7 @@ import java.io.*;  import java.util.*;  import java.text.*;  import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_7.*;  class AltosScanResult {  	String		callsign; @@ -62,9 +62,18 @@ class AltosScanResult {  		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);  	} diff --git a/altosuilib/AltosSerial.java b/altosuilib/AltosSerial.java index 95815a7b..280887a6 100644 --- a/altosuilib/AltosSerial.java +++ b/altosuilib/AltosSerial.java @@ -19,13 +19,13 @@   * 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.*;  /* diff --git a/altosuilib/AltosSerialInUseException.java b/altosuilib/AltosSerialInUseException.java index 0487e146..1ca8e792 100644 --- a/altosuilib/AltosSerialInUseException.java +++ b/altosuilib/AltosSerialInUseException.java @@ -15,7 +15,7 @@   * 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; diff --git a/altosuilib/AltosUIAxis.java b/altosuilib/AltosUIAxis.java index 89f5493b..4173232f 100644 --- a/altosuilib/AltosUIAxis.java +++ b/altosuilib/AltosUIAxis.java @@ -15,14 +15,14 @@   * 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.*; diff --git a/altosuilib/AltosUIConfigure.java b/altosuilib/AltosUIConfigure.java index b022aeec..75957b33 100644 --- a/altosuilib/AltosUIConfigure.java +++ b/altosuilib/AltosUIConfigure.java @@ -15,13 +15,14 @@   * 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> { @@ -269,8 +270,8 @@ public class AltosUIConfigure  		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++; diff --git a/altosuilib/AltosUIDataMissing.java b/altosuilib/AltosUIDataMissing.java index a0e41fef..b2a76165 100644 --- a/altosuilib/AltosUIDataMissing.java +++ b/altosuilib/AltosUIDataMissing.java @@ -15,7 +15,7 @@   * 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; diff --git a/altosuilib/AltosUIDataPoint.java b/altosuilib/AltosUIDataPoint.java index 82ce862f..fcbfbe95 100644 --- a/altosuilib/AltosUIDataPoint.java +++ b/altosuilib/AltosUIDataPoint.java @@ -15,7 +15,7 @@   * 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; diff --git a/altosuilib/AltosUIDataSet.java b/altosuilib/AltosUIDataSet.java index 6293911d..f25a6bcb 100644 --- a/altosuilib/AltosUIDataSet.java +++ b/altosuilib/AltosUIDataSet.java @@ -15,7 +15,7 @@   * 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(); diff --git a/altosuilib/AltosUIDialog.java b/altosuilib/AltosUIDialog.java index 77e549c4..a0c180c1 100644 --- a/altosuilib/AltosUIDialog.java +++ b/altosuilib/AltosUIDialog.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altosuilib/AltosUIEnable.java b/altosuilib/AltosUIEnable.java index 481e5b87..0f9ed8ff 100644 --- a/altosuilib/AltosUIEnable.java +++ b/altosuilib/AltosUIEnable.java @@ -15,7 +15,7 @@   * 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.*; @@ -23,7 +23,7 @@ import javax.swing.*;  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.*; diff --git a/altosuilib/AltosUIFlightTab.java b/altosuilib/AltosUIFlightTab.java index ea4f0cb0..270466a6 100644 --- a/altosuilib/AltosUIFlightTab.java +++ b/altosuilib/AltosUIFlightTab.java @@ -15,13 +15,13 @@   * 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; diff --git a/altosuilib/AltosUIFrame.java b/altosuilib/AltosUIFrame.java index 39b1eb73..3f74bca6 100644 --- a/altosuilib/AltosUIFrame.java +++ b/altosuilib/AltosUIFrame.java @@ -15,7 +15,7 @@   * 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.*; @@ -207,6 +207,62 @@ public class AltosUIFrame extends JFrame implements AltosUIListener, AltosPositi  			}  		}  	} + +	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); diff --git a/altosuilib/AltosUIFreqList.java b/altosuilib/AltosUIFreqList.java index 430069f5..744a05dd 100644 --- a/altosuilib/AltosUIFreqList.java +++ b/altosuilib/AltosUIFreqList.java @@ -15,10 +15,10 @@   * 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> { diff --git a/altosuilib/AltosUIGraph.java b/altosuilib/AltosUIGraph.java index d20aa54b..3b34a988 100644 --- a/altosuilib/AltosUIGraph.java +++ b/altosuilib/AltosUIGraph.java @@ -15,14 +15,14 @@   * 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.*; diff --git a/altosuilib/AltosUIGrapher.java b/altosuilib/AltosUIGrapher.java index fcd3546f..80b3acd7 100644 --- a/altosuilib/AltosUIGrapher.java +++ b/altosuilib/AltosUIGrapher.java @@ -15,14 +15,14 @@   * 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.*; diff --git a/altosuilib/AltosUIImage.java b/altosuilib/AltosUIImage.java new file mode 100644 index 00000000..3a2e2c01 --- /dev/null +++ b/altosuilib/AltosUIImage.java @@ -0,0 +1,38 @@ +/* + * 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; +	} +} diff --git a/altosuilib/AltosUIIndicator.java b/altosuilib/AltosUIIndicator.java index f2e77218..989af389 100644 --- a/altosuilib/AltosUIIndicator.java +++ b/altosuilib/AltosUIIndicator.java @@ -15,11 +15,11 @@   * 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; diff --git a/altosuilib/AltosUILib.java b/altosuilib/AltosUILib.java index 2fa6cbd6..abd9e7e1 100644 --- a/altosuilib/AltosUILib.java +++ b/altosuilib/AltosUILib.java @@ -15,12 +15,12 @@   * 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 { diff --git a/altosuilib/AltosUIListener.java b/altosuilib/AltosUIListener.java index 9eec0b24..80c18c35 100644 --- a/altosuilib/AltosUIListener.java +++ b/altosuilib/AltosUIListener.java @@ -15,7 +15,7 @@   * 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); diff --git a/altosuilib/AltosUIMap.java b/altosuilib/AltosUIMap.java deleted file mode 100644 index 5c6d5bdf..00000000 --- a/altosuilib/AltosUIMap.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * 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); -	} -} diff --git a/altosuilib/AltosUIMapCache.java b/altosuilib/AltosUIMapCache.java deleted file mode 100644 index 73401e3c..00000000 --- a/altosuilib/AltosUIMapCache.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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); -	} -} diff --git a/altosuilib/AltosUIMapImage.java b/altosuilib/AltosUIMapImage.java deleted file mode 100644 index 97220a40..00000000 --- a/altosuilib/AltosUIMapImage.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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; -		} -	} -} diff --git a/altosuilib/AltosUIMapLine.java b/altosuilib/AltosUIMapLine.java deleted file mode 100644 index 2634f843..00000000 --- a/altosuilib/AltosUIMapLine.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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); -	} -} diff --git a/altosuilib/AltosUIMapMark.java b/altosuilib/AltosUIMapMark.java deleted file mode 100644 index b4b98efa..00000000 --- a/altosuilib/AltosUIMapMark.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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; -	} -} diff --git a/altosuilib/AltosUIMapNew.java b/altosuilib/AltosUIMapNew.java new file mode 100644 index 00000000..8ac18296 --- /dev/null +++ b/altosuilib/AltosUIMapNew.java @@ -0,0 +1,538 @@ +/* + * 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); +	} +} diff --git a/altosuilib/AltosUIMapPath.java b/altosuilib/AltosUIMapPath.java deleted file mode 100644 index e77af580..00000000 --- a/altosuilib/AltosUIMapPath.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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>(); -	} -} diff --git a/altosuilib/AltosUIMapPreload.java b/altosuilib/AltosUIMapPreloadNew.java index e82b6c60..ba559534 100644 --- a/altosuilib/AltosUIMapPreload.java +++ b/altosuilib/AltosUIMapPreloadNew.java @@ -15,7 +15,7 @@   * 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.*; @@ -26,7 +26,7 @@ import java.text.*;  import java.lang.Math;  import java.net.URL;  import java.net.URLConnection; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_7.*;  class AltosUIMapPos extends Box {  	AltosUIFrame	owner; @@ -53,33 +53,33 @@ class AltosUIMapPos extends Box {  		hemi.setSelectedIndex(h);  	} -	public double get_value() throws NumberFormatException { +	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 = Double.parseDouble(d_t); -		} catch (NumberFormatException ne) { +			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 ne; +			throw pe;  		}  		try {  			if (m_t.equals(""))  				m = 0;  			else -				m = Double.parseDouble(m_t); -		} catch (NumberFormatException ne) { +				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 ne; +			throw pe;  		}  		v = d + m/60.0;  		if (h == 1) @@ -118,250 +118,87 @@ class AltosUIMapPos extends Box {  	}  } -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 { +public class AltosUIMapPreloadNew extends AltosUIFrame implements ActionListener, ItemListener, AltosLaunchSiteListener, AltosMapLoaderListener  {  	AltosUIFrame	owner; -	AltosUIMap	map; -	AltosUIMapCache	cache = new AltosUIMapCache(); +	AltosUIMapNew	map;  	AltosUIMapPos	lat;  	AltosUIMapPos	lon;  	JProgressBar	pbar; -	int		pbar_max; -	int		pbar_cur; -	AltosUISites	sites; +	AltosMapLoader	loader; +  	JLabel		site_list_label; -	JComboBox<AltosUISite>	site_list; +	JComboBox<AltosLaunchSite>	site_list;  	JToggleButton	load_button;  	boolean		loading;  	JButton		close_button; -	JCheckBox[]	maptypes = new JCheckBox[AltosUIMap.maptype_terrain - AltosUIMap.maptype_hybrid + 1]; +	JCheckBox[]	maptypes = new JCheckBox[AltosMap.maptype_terrain - AltosMap.maptype_hybrid + 1];  	JComboBox<Integer>	min_zoom;  	JComboBox<Integer>	max_zoom; -	JComboBox<Integer>	radius; +	JComboBox<Double>	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" }; +	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; -	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); -		} -	} +	static final String[]	lat_hemi_names = { "N", "S" }; +	static final String[]	lon_hemi_names = { "E", "W" };  	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; +	/* 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); +				} +			});  	} -	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(); +	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); +				} +			});  	} -	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(); +	public void loader_done(int max) { +		SwingUtilities.invokeLater(new Runnable() { +				public void run() { +					pbar.setValue(0); +					pbar.setString(""); +					load_button.setSelected(false); +					loading = false; +				} +			});  	} -	/* 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 void debug(String format, Object ... arguments) { +		System.out.printf(format, arguments);  	} -	public AltosUIMapCache cache() { return cache; } -	public void set_sites() { -		int	i = 1; -		for (AltosUISite site : sites.sites) { -			site_list.insertItemAt(site, i); -			i++; -		} +	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) { @@ -369,8 +206,8 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I  		if (state == ItemEvent.SELECTED) {  			Object	o = e.getItem(); -			if (o instanceof AltosUISite) { -				AltosUISite	site = (AltosUISite) o; +			if (o instanceof AltosLaunchSite) { +				AltosLaunchSite	site = (AltosLaunchSite) o;  				lat.set_value(site.latitude);  				lon.set_value(site.longitude);  			} @@ -388,21 +225,39 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I  				try {  					latitude = lat.get_value();  					longitude = lon.get_value(); -					min_z = (Integer) min_zoom.getSelectedItem(); -					max_z = (Integer) max_zoom.getSelectedItem(); +					int min_z = (Integer) min_zoom.getSelectedItem(); +					int max_z = (Integer) max_zoom.getSelectedItem();  					if (max_z < min_z)  						max_z = min_z; -					r = (Integer) radius.getSelectedItem(); +					Double r = (Double) radius.getSelectedItem(); + +					if (AltosPreferences.imperial_units()) +						r = AltosConvert.distance.inverse(r); +					else +						r = r * 1000;  					loading = true; -				} catch (NumberFormatException ne) { + +					loader.load(latitude, longitude, min_z, max_z, r, all_types()); +				} catch (ParseException pe) {  					load_button.setSelected(false);  				} -				start_load();  			}  		}  	} -	public AltosUIMapPreload(AltosUIFrame in_owner) { +	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(); @@ -413,7 +268,9 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I  		pane.setLayout(new GridBagLayout()); -		map = new AltosUIMap(); +		map = new AltosUIMapNew(); + +		loader = new AltosMapLoader(map.map, this);  		c.fill = GridBagConstraints.BOTH;  		c.anchor = GridBagConstraints.CENTER; @@ -461,10 +318,10 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I  		pane.add(site_list_label, c); -		site_list = new JComboBox<AltosUISite>(new AltosUISite[] { new AltosUISite("Site List", 0, 0) }); +		site_list = new JComboBox<AltosLaunchSite>(new AltosLaunchSite[] { new AltosLaunchSite("Site List", 0, 0) });  		site_list.addItemListener(this); -		sites = new AltosUISites(this); +		new AltosLaunchSites(this);  		c.fill = GridBagConstraints.HORIZONTAL;  		c.anchor = GridBagConstraints.CENTER; @@ -555,9 +412,9 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I  		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); +		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; @@ -588,13 +445,21 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I  		c.gridy = 3;  		pane.add(max_zoom, c); -		JLabel radius_label = new JLabel("Tile Radius"); +		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); -		radius = new JComboBox<Integer>(radii); -		radius.setSelectedItem(radii[4]); +		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; diff --git a/altosuilib/AltosUIMapTile.java b/altosuilib/AltosUIMapTile.java deleted file mode 100644 index afd1bbc6..00000000 --- a/altosuilib/AltosUIMapTile.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * 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()); -	} -} diff --git a/altosuilib/AltosUIMapTransform.java b/altosuilib/AltosUIMapTransform.java deleted file mode 100644 index 25497403..00000000 --- a/altosuilib/AltosUIMapTransform.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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; -	} -} diff --git a/altosuilib/AltosUIMapView.java b/altosuilib/AltosUIMapView.java deleted file mode 100644 index c8632b99..00000000 --- a/altosuilib/AltosUIMapView.java +++ /dev/null @@ -1,472 +0,0 @@ -/* - * 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(); -	} -} diff --git a/altosuilib/AltosUIMarker.java b/altosuilib/AltosUIMarker.java index 843ee939..287468bd 100644 --- a/altosuilib/AltosUIMarker.java +++ b/altosuilib/AltosUIMarker.java @@ -15,14 +15,14 @@   * 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.*; diff --git a/altosuilib/AltosUIPreferences.java b/altosuilib/AltosUIPreferences.java index 9760494c..e9378442 100644 --- a/altosuilib/AltosUIPreferences.java +++ b/altosuilib/AltosUIPreferences.java @@ -15,13 +15,13 @@   * 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 { @@ -55,10 +55,6 @@ 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()); @@ -75,9 +71,6 @@ public class AltosUIPreferences extends AltosPreferences {  		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(); } @@ -225,32 +218,4 @@ public class AltosUIPreferences extends AltosPreferences {  			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; -		} -	}  } diff --git a/altosuilib/AltosUIPreferencesBackend.java b/altosuilib/AltosUIPreferencesBackend.java index 91fe42ec..5fbfa7f9 100644 --- a/altosuilib/AltosUIPreferencesBackend.java +++ b/altosuilib/AltosUIPreferencesBackend.java @@ -15,11 +15,11 @@   * 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 { diff --git a/altosuilib/AltosUIRateList.java b/altosuilib/AltosUIRateList.java index 0c783a89..12d791b1 100644 --- a/altosuilib/AltosUIRateList.java +++ b/altosuilib/AltosUIRateList.java @@ -15,10 +15,10 @@   * 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> { diff --git a/altosuilib/AltosUISeries.java b/altosuilib/AltosUISeries.java index 4cd5ccd1..c91c1204 100644 --- a/altosuilib/AltosUISeries.java +++ b/altosuilib/AltosUISeries.java @@ -15,14 +15,14 @@   * 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.*; diff --git a/altosuilib/AltosUITelemetryList.java b/altosuilib/AltosUITelemetryList.java index 77eef567..b4f80b0b 100644 --- a/altosuilib/AltosUITelemetryList.java +++ b/altosuilib/AltosUITelemetryList.java @@ -15,11 +15,11 @@   * 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> { diff --git a/altosuilib/AltosUIUnitsIndicator.java b/altosuilib/AltosUIUnitsIndicator.java index f86e274f..8e0a9e07 100644 --- a/altosuilib/AltosUIUnitsIndicator.java +++ b/altosuilib/AltosUIUnitsIndicator.java @@ -15,11 +15,11 @@   * 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 { @@ -36,6 +36,10 @@ 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);  	} @@ -77,7 +81,7 @@ public abstract class AltosUIUnitsIndicator extends AltosUIIndicator {  				v[i] = value(state, listener_state, i);  			else  				v[i] = AltosLib.MISSING; -			if (hide(state, i)) +			if (hide(state, listener_state, i))  				hide = true;  		} diff --git a/altosuilib/AltosUIVoltageIndicator.java b/altosuilib/AltosUIVoltageIndicator.java index 44ad2ea2..840e5ad0 100644 --- a/altosuilib/AltosUIVoltageIndicator.java +++ b/altosuilib/AltosUIVoltageIndicator.java @@ -15,11 +15,11 @@   * 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 { diff --git a/altosuilib/AltosUSBDevice.java b/altosuilib/AltosUSBDevice.java index e940493f..48daf131 100644 --- a/altosuilib/AltosUSBDevice.java +++ b/altosuilib/AltosUSBDevice.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altosuilib/AltosVoice.java b/altosuilib/AltosVoice.java index 867f6619..98cc3c83 100644 --- a/altosuilib/AltosVoice.java +++ b/altosuilib/AltosVoice.java @@ -15,7 +15,7 @@   * 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; diff --git a/altosuilib/GrabNDrag.java b/altosuilib/GrabNDrag.java index 5d9ce2d9..cf0eacee 100644 --- a/altosuilib/GrabNDrag.java +++ b/altosuilib/GrabNDrag.java @@ -15,7 +15,7 @@   * 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.*; diff --git a/altosuilib/Makefile.am b/altosuilib/Makefile.am index d51da91d..a00e4a12 100644 --- a/altosuilib/Makefile.am +++ b/altosuilib/Makefile.am @@ -12,8 +12,6 @@ altosuilib_JAVA = \  	GrabNDrag.java \  	AltosDevice.java \  	AltosDeviceDialog.java \ -	AltosFlightDisplay.java \ -	AltosFontListener.java \  	AltosPositionListener.java \  	AltosUIConfigure.java \  	AltosUIAxis.java \ @@ -31,7 +29,6 @@ altosuilib_JAVA = \  	AltosUIPreferencesBackend.java \  	AltosUIPreferences.java \  	AltosUISeries.java \ -	AltosUIVersion.java \  	AltosUSBDevice.java \  	AltosVoice.java \  	AltosDisplayThread.java \ @@ -60,23 +57,8 @@ altosuilib_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 \ @@ -84,6 +66,7 @@ altosuilib_JAVA = \  	AltosUIFreqList.java \  	AltosUITelemetryList.java \  	AltosUIRateList.java \ +	AltosUIImage.java \  	OSXAdapter.java  JAR=altosuilib_$(ALTOSUILIB_VERSION).jar diff --git a/altosuilib/OSXAdapter.java b/altosuilib/OSXAdapter.java index 28b00ce1..7397e2a0 100755 --- a/altosuilib/OSXAdapter.java +++ b/altosuilib/OSXAdapter.java @@ -55,7 +55,7 @@ Copyright © 2003-2007 Apple, Inc., All Rights Reserved  */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_7;  import java.lang.reflect.*;  import java.util.HashMap; diff --git a/ao-bringup/cal-freq b/ao-bringup/cal-freq index 5d876e21..d3d9dc95 100755 --- a/ao-bringup/cal-freq +++ b/ao-bringup/cal-freq @@ -10,42 +10,16 @@ case $# in  	;;  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 diff --git a/ao-bringup/test-easymega b/ao-bringup/test-easymega index 76f3effb..2f0a7822 100755 --- a/ao-bringup/test-easymega +++ b/ao-bringup/test-easymega @@ -12,7 +12,7 @@ echo "\t$PRODUCT v$VERSION powered from USB"  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") diff --git a/ao-bringup/test-telemetrum b/ao-bringup/test-telemetrum index 57a4d90d..13407e86 100755 --- a/ao-bringup/test-telemetrum +++ b/ao-bringup/test-telemetrum @@ -61,6 +61,7 @@ ao-list | while read product serial dev; do  	    echo""  	    echo "$PRODUCT-v$VERSION" serial "$serial" is ready to ship +	    echo "\007"  	    ret=0  	    ;;      esac diff --git a/ao-bringup/turnon_easymega b/ao-bringup/turnon_easymega index b313e162..1e75e72f 100755 --- a/ao-bringup/turnon_easymega +++ b/ao-bringup/turnon_easymega @@ -1,14 +1,14 @@  #!/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 @@ -38,7 +38,7 @@ $USBLOAD --serial=$SERIAL $REPO/easymega-v$VERSION*.elf || 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*) @@ -52,7 +52,7 @@ esac  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 diff --git a/ao-bringup/turnon_telegps b/ao-bringup/turnon_telegps index 123f0b54..ba97d503 100755 --- a/ao-bringup/turnon_telegps +++ b/ao-bringup/turnon_telegps @@ -72,10 +72,8 @@ case "$dev" in          ;;  esac -echo 'E 0' > $dev +SERIAL=$SERIAL ./cal-freq $dev  ./test-telegps -SERIAL=$SERIAL ./cal-freq $dev -  exit $? diff --git a/ao-tools/Makefile.am b/ao-tools/Makefile.am index 6a170cbd..66d2560e 100644 --- a/ao-tools/Makefile.am +++ b/ao-tools/Makefile.am @@ -2,7 +2,8 @@ SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-list \  	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 diff --git a/ao-tools/ao-cal-freq/.gitignore b/ao-tools/ao-cal-freq/.gitignore new file mode 100644 index 00000000..4fe14865 --- /dev/null +++ b/ao-tools/ao-cal-freq/.gitignore @@ -0,0 +1 @@ +ao-cal-freq diff --git a/ao-tools/ao-cal-freq/Makefile.am b/ao-tools/ao-cal-freq/Makefile.am new file mode 100644 index 00000000..e11c2b0a --- /dev/null +++ b/ao-tools/ao-cal-freq/Makefile.am @@ -0,0 +1,11 @@ +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 diff --git a/ao-tools/ao-cal-freq/ao-cal-freq.1 b/ao-tools/ao-cal-freq/ao-cal-freq.1 new file mode 100644 index 00000000..bfc3101c --- /dev/null +++ b/ao-tools/ao-cal-freq/ao-cal-freq.1 @@ -0,0 +1,58 @@ +.\" +.\" 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 diff --git a/ao-tools/ao-cal-freq/ao-cal-freq.c b/ao-tools/ao-cal-freq/ao-cal-freq.c new file mode 100644 index 00000000..464faf0f --- /dev/null +++ b/ao-tools/ao-cal-freq/ao-cal-freq.c @@ -0,0 +1,280 @@ +/* + * 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); +} diff --git a/ao-tools/ao-dump-up/ao-dump-up.c b/ao-tools/ao-dump-up/ao-dump-up.c index 6268dc8b..b1f85af6 100644 --- a/ao-tools/ao-dump-up/ao-dump-up.c +++ b/ao-tools/ao-dump-up/ao-dump-up.c @@ -29,12 +29,13 @@  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);  } @@ -134,7 +135,7 @@ static int swap16(int i)  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;  	}  } @@ -165,9 +166,13 @@ main (int argc, char **argv)  	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; @@ -179,8 +184,21 @@ main (int argc, char **argv)  			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) diff --git a/ao-tools/ao-flash/Makefile.am b/ao-tools/ao-flash/Makefile.am index 6b7ea6bb..4231dc21 100644 --- a/ao-tools/ao-flash/Makefile.am +++ b/ao-tools/ao-flash/Makefile.am @@ -1,3 +1,3 @@ -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 diff --git a/ao-tools/ao-flash/ao-flash-stm b/ao-tools/ao-flash/ao-flash-stm index 9eebf5d2..9eebf5d2 100644..100755 --- a/ao-tools/ao-flash/ao-flash-stm +++ b/ao-tools/ao-flash/ao-flash-stm diff --git a/ao-tools/ao-flash/ao-flash-stm32f0x b/ao-tools/ao-flash/ao-flash-stm32f0x new file mode 100755 index 00000000..45643a4f --- /dev/null +++ b/ao-tools/ao-flash/ao-flash-stm32f0x @@ -0,0 +1,16 @@ +#!/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 diff --git a/ao-tools/ao-flash/ao-flash-stm32f0x.1 b/ao-tools/ao-flash/ao-flash-stm32f0x.1 new file mode 100644 index 00000000..07ff5b59 --- /dev/null +++ b/ao-tools/ao-flash/ao-flash-stm32f0x.1 @@ -0,0 +1,36 @@ +.\" +.\" 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 diff --git a/ao-tools/ao-list/ao-list.c b/ao-tools/ao-list/ao-list.c index c4b43d8f..4c065e79 100644 --- a/ao-tools/ao-list/ao-list.c +++ b/ao-tools/ao-list/ao-list.c @@ -28,12 +28,12 @@ main (int argc, char **argv)  	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);  	} diff --git a/ao-tools/ao-mega/ao-mega.c b/ao-tools/ao-mega/ao-mega.c index 523229e6..cfb58bb4 100644 --- a/ao-tools/ao-mega/ao-mega.c +++ b/ao-tools/ao-mega/ao-mega.c @@ -85,7 +85,7 @@ main (int argc, char **argv)  			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) { @@ -126,8 +126,7 @@ main (int argc, char **argv)  							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); diff --git a/ao-tools/lib/cc-usb.c b/ao-tools/lib/cc-usb.c index 1a4dc7a1..1e023c7e 100644 --- a/ao-tools/lib/cc-usb.c +++ b/ao-tools/lib/cc-usb.c @@ -207,8 +207,10 @@ _cc_usb_sync(struct cc_usb *cc, int wait_for_input, int write_timeout)  					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, diff --git a/ao-tools/lib/cc-usbdev.c b/ao-tools/lib/cc-usbdev.c index 95bfa244..6c3ba591 100644 --- a/ao-tools/lib/cc-usbdev.c +++ b/ao-tools/lib/cc-usbdev.c @@ -219,7 +219,7 @@ is_am(int idVendor, int idProduct) {  }  struct cc_usbdevs * -cc_usbdevs_scan(void) +cc_usbdevs_scan(int non_tty)  {  	int			e;  	struct dirent		**ents; @@ -241,7 +241,7 @@ cc_usbdevs_scan(void)  		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 *)); @@ -274,7 +274,7 @@ match_dev(char *product, int serial)  	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++) { diff --git a/ao-tools/lib/cc.h b/ao-tools/lib/cc.h index bff4b2c9..ff95e4fc 100644 --- a/ao-tools/lib/cc.h +++ b/ao-tools/lib/cc.h @@ -50,7 +50,7 @@ void  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); diff --git a/configure.ac b/configure.ac index b09daa82..efa68e2a 100644 --- a/configure.ac +++ b/configure.ac @@ -18,19 +18,21 @@ dnl  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]) @@ -514,9 +516,9 @@ AC_OUTPUT([  Makefile  src/Makedefs  altoslib/Makefile +altoslib/AltosVersion.java  icon/Makefile  altosuilib/Makefile -altosuilib/AltosUIVersion.java  altosui/Makefile  altosui/Info.plist  altosui/altos-windows.nsi @@ -529,6 +531,7 @@ telegps/Info.plist  telegps/telegps-windows.nsi  altosdroid/Makefile  altosdroid/local.properties +altosdroid/AndroidManifest.xml  ao-tools/Makefile  ao-tools/lib/Makefile  ao-tools/ao-rawload/Makefile @@ -551,6 +554,7 @@ ao-tools/ao-test-igniter/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 diff --git a/doc/Makefile b/doc/Makefile index 2887a229..9c6189b4 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -18,7 +18,8 @@ RELNOTES=\  	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 \ @@ -51,6 +52,7 @@ PICTURES=\  	micropeak-statistics.png \  	MicroPeakUSB-2.0-inuse.jpg \  	MicroPeakUSB-2.0.jpg \ +	monitor-idle.png \  	scan-channels.png \  	site-map.png \  	table.png \ diff --git a/doc/altusmetrum.xsl b/doc/altusmetrum.xsl index 1631983a..a40481f1 100644 --- a/doc/altusmetrum.xsl +++ b/doc/altusmetrum.xsl @@ -41,6 +41,13 @@      </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>  	<revremark> @@ -2620,9 +2627,9 @@ NAR #88757, TRA #12200  	  <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> @@ -3853,8 +3860,7 @@ NAR #88757, TRA #12200        <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 @@ -3909,15 +3915,18 @@ NAR #88757, TRA #12200  	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 @@ -3929,6 +3938,13 @@ NAR #88757, TRA #12200      </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 @@ -3939,22 +3955,28 @@ NAR #88757, TRA #12200  	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> @@ -3968,7 +3990,7 @@ NAR #88757, TRA #12200        </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' @@ -3983,14 +4005,90 @@ NAR #88757, TRA #12200        </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> @@ -4004,91 +4102,353 @@ NAR #88757, TRA #12200        <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> @@ -4097,9 +4457,9 @@ NAR #88757, TRA #12200        <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> @@ -5916,6 +6276,13 @@ NAR #88757, TRA #12200    <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  	  xmlns:xi="http://www.w3.org/2001/XInclude" diff --git a/doc/load-maps.png b/doc/load-maps.png Binary files differindex ae98c9a5..150b8b3c 100644 --- a/doc/load-maps.png +++ b/doc/load-maps.png diff --git a/doc/monitor-idle.png b/doc/monitor-idle.png Binary files differnew file mode 100644 index 00000000..964063f1 --- /dev/null +++ b/doc/monitor-idle.png diff --git a/doc/release-notes-1.6.1.xsl b/doc/release-notes-1.6.1.xsl new file mode 100644 index 00000000..058d43fe --- /dev/null +++ b/doc/release-notes-1.6.1.xsl @@ -0,0 +1,189 @@ +<?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> diff --git a/doc/telemetry.xsl b/doc/telemetry.xsl index e4101507..2e0b3ea1 100644 --- a/doc/telemetry.xsl +++ b/doc/telemetry.xsl @@ -110,7 +110,7 @@        </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'/> @@ -124,7 +124,7 @@  	  <tbody>  	    <row>  	      <entry>0x01</entry> -	      <entry>TeleMetrum Sensor Data</entry> +	      <entry>TeleMetrum v1.x Sensor Data</entry>  	    </row>  	    <row>  	      <entry>0x02</entry> @@ -138,7 +138,7 @@  	</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. @@ -214,6 +214,316 @@        </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'>  	<tgroup cols='2' align='center' colsep='1' rowsep='1'> @@ -315,7 +625,7 @@        </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> @@ -604,62 +914,169 @@  	</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> @@ -668,10 +1085,12 @@      <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> diff --git a/icon/Makefile.am b/icon/Makefile.am index c08e9236..af238ac4 100644 --- a/icon/Makefile.am +++ b/icon/Makefile.am @@ -150,14 +150,21 @@ SUFFIXES=.svg .build .icns .ico .rc .o .exe  	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 diff --git a/icon/make-rc b/icon/make-rc new file mode 100755 index 00000000..de647278 --- /dev/null +++ b/icon/make-rc @@ -0,0 +1,53 @@ +#!/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 diff --git a/icon/windows-stub.c b/icon/windows-stub.c index 8df3e0aa..e075a02d 100644 --- a/icon/windows-stub.c +++ b/icon/windows-stub.c @@ -1,2 +1,201 @@ -__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); +} diff --git a/micropeak/Makefile.am b/micropeak/Makefile.am index 15865606..a8834a66 100644 --- a/micropeak/Makefile.am +++ b/micropeak/Makefile.am @@ -124,20 +124,29 @@ FAT_FILES=$(FATJAR) $(ALTOSLIB_CLASS) $(ALTOSUILIB_CLASS) $(FREETTS_CLASS) $(JFR  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) @@ -274,7 +283,7 @@ $(MACOSX_DIST): $(MACOSX_FILES)  	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 diff --git a/micropeak/MicroData.java b/micropeak/MicroData.java index 857dbe58..1001a370 100644 --- a/micropeak/MicroData.java +++ b/micropeak/MicroData.java @@ -20,8 +20,8 @@ package org.altusmetrum.micropeak;  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; diff --git a/micropeak/MicroDataPoint.java b/micropeak/MicroDataPoint.java index 45d5ba27..1d42032f 100644 --- a/micropeak/MicroDataPoint.java +++ b/micropeak/MicroDataPoint.java @@ -17,7 +17,7 @@  package org.altusmetrum.micropeak; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altosuilib_7.*;  public class MicroDataPoint implements AltosUIDataPoint {  	public double		time; diff --git a/micropeak/MicroDeviceDialog.java b/micropeak/MicroDeviceDialog.java index 55328482..91c3306b 100644 --- a/micropeak/MicroDeviceDialog.java +++ b/micropeak/MicroDeviceDialog.java @@ -21,7 +21,7 @@ import javax.swing.*;  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 { diff --git a/micropeak/MicroDownload.java b/micropeak/MicroDownload.java index a03d7b3a..028f4431 100644 --- a/micropeak/MicroDownload.java +++ b/micropeak/MicroDownload.java @@ -23,8 +23,8 @@ import javax.swing.*;  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; diff --git a/micropeak/MicroExport.java b/micropeak/MicroExport.java index 93c8cae8..7e75574d 100644 --- a/micropeak/MicroExport.java +++ b/micropeak/MicroExport.java @@ -23,8 +23,8 @@ import java.util.ArrayList;  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 { diff --git a/micropeak/MicroFile.java b/micropeak/MicroFile.java index 78f7dac2..1d2447fc 100644 --- a/micropeak/MicroFile.java +++ b/micropeak/MicroFile.java @@ -19,8 +19,8 @@ package org.altusmetrum.micropeak;  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 { diff --git a/micropeak/MicroFileChooser.java b/micropeak/MicroFileChooser.java index 5ac22e30..a48df6d1 100644 --- a/micropeak/MicroFileChooser.java +++ b/micropeak/MicroFileChooser.java @@ -20,8 +20,8 @@ package org.altusmetrum.micropeak;  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; diff --git a/micropeak/MicroFrame.java b/micropeak/MicroFrame.java index 47b03a12..4cb156f2 100644 --- a/micropeak/MicroFrame.java +++ b/micropeak/MicroFrame.java @@ -21,7 +21,7 @@ import java.awt.*;  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 = { diff --git a/micropeak/MicroGraph.java b/micropeak/MicroGraph.java index a6d511c3..d2c6a9e3 100644 --- a/micropeak/MicroGraph.java +++ b/micropeak/MicroGraph.java @@ -22,8 +22,8 @@ import java.util.ArrayList;  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.*; diff --git a/micropeak/MicroPeak.java b/micropeak/MicroPeak.java index 9f7095f3..be50ac18 100644 --- a/micropeak/MicroPeak.java +++ b/micropeak/MicroPeak.java @@ -23,8 +23,8 @@ import javax.swing.*;  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 { diff --git a/micropeak/MicroRaw.java b/micropeak/MicroRaw.java index 587fe927..ed5410e0 100644 --- a/micropeak/MicroRaw.java +++ b/micropeak/MicroRaw.java @@ -20,8 +20,8 @@ package org.altusmetrum.micropeak;  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 { diff --git a/micropeak/MicroSave.java b/micropeak/MicroSave.java index 65026bda..15916ba0 100644 --- a/micropeak/MicroSave.java +++ b/micropeak/MicroSave.java @@ -24,8 +24,8 @@ import javax.swing.filechooser.FileNameExtensionFilter;  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 { diff --git a/micropeak/MicroSerial.java b/micropeak/MicroSerial.java index 4282aba1..c0ec161c 100644 --- a/micropeak/MicroSerial.java +++ b/micropeak/MicroSerial.java @@ -20,7 +20,7 @@ package org.altusmetrum.micropeak;  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; diff --git a/micropeak/MicroSerialLog.java b/micropeak/MicroSerialLog.java index 3ee44649..a8c8b2ad 100644 --- a/micropeak/MicroSerialLog.java +++ b/micropeak/MicroSerialLog.java @@ -20,7 +20,7 @@ package org.altusmetrum.micropeak;  import java.util.*;  import java.io.*;  import libaltosJNI.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altosuilib_7.*;  public interface MicroSerialLog { diff --git a/micropeak/MicroStats.java b/micropeak/MicroStats.java index 47ef2a79..8cb64f4d 100644 --- a/micropeak/MicroStats.java +++ b/micropeak/MicroStats.java @@ -18,8 +18,8 @@  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; diff --git a/micropeak/MicroStatsTable.java b/micropeak/MicroStatsTable.java index 139c4416..ef6023dc 100644 --- a/micropeak/MicroStatsTable.java +++ b/micropeak/MicroStatsTable.java @@ -19,8 +19,8 @@ package org.altusmetrum.micropeak;  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; diff --git a/micropeak/MicroUSB.java b/micropeak/MicroUSB.java index fa20488d..906458c4 100644 --- a/micropeak/MicroUSB.java +++ b/micropeak/MicroUSB.java @@ -19,8 +19,8 @@ package org.altusmetrum.micropeak;  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 { diff --git a/micropeak/micropeak-windows.nsi.in b/micropeak/micropeak-windows.nsi.in index a3779630..c3b63f7b 100644 --- a/micropeak/micropeak-windows.nsi.in +++ b/micropeak/micropeak-windows.nsi.in @@ -65,9 +65,9 @@ UninstPage instfiles  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 @@ -104,16 +104,17 @@ Section "${REG_NAME} Application"  	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" @@ -129,11 +130,8 @@ Section "File Associations"  	SetOutPath $INSTDIR -	File "../icon/${WIN_APP_EXE}"  	File "../icon/${WIN_MPD_EXE}" -	SearchPath $1 "javaw.exe" -  	; application elements  	DeleteRegKey HKCR "${PROG_ID}" @@ -143,7 +141,7 @@ Section "File Associations"  	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 diff --git a/src/Makefile b/src/Makefile index 05e99c7f..dc74bf8c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -34,13 +34,15 @@ ARMM3DIRS=\  	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 diff --git a/src/chaoskey-v0.1/.gitignore b/src/chaoskey-v0.1/.gitignore new file mode 100644 index 00000000..b0adba26 --- /dev/null +++ b/src/chaoskey-v0.1/.gitignore @@ -0,0 +1,2 @@ +ao_product.h +chaoskey-* diff --git a/src/chaoskey-v0.1/Makefile b/src/chaoskey-v0.1/Makefile new file mode 100644 index 00000000..ac4a6788 --- /dev/null +++ b/src/chaoskey-v0.1/Makefile @@ -0,0 +1,70 @@ +# +# 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: diff --git a/src/chaoskey-v0.1/ao_chaoskey.c b/src/chaoskey-v0.1/ao_chaoskey.c new file mode 100644 index 00000000..48c8bf04 --- /dev/null +++ b/src/chaoskey-v0.1/ao_chaoskey.c @@ -0,0 +1,41 @@ +/* + * 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(); +} diff --git a/src/chaoskey-v0.1/ao_pins.h b/src/chaoskey-v0.1/ao_pins.h new file mode 100644 index 00000000..72963dba --- /dev/null +++ b/src/chaoskey-v0.1/ao_pins.h @@ -0,0 +1,67 @@ +/* + * 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_ */ diff --git a/src/chaoskey-v0.1/flash-loader/.gitignore b/src/chaoskey-v0.1/flash-loader/.gitignore new file mode 100644 index 00000000..a60a4945 --- /dev/null +++ b/src/chaoskey-v0.1/flash-loader/.gitignore @@ -0,0 +1,2 @@ +ao_product.h +chaoskey* diff --git a/src/chaoskey-v0.1/flash-loader/Makefile b/src/chaoskey-v0.1/flash-loader/Makefile new file mode 100644 index 00000000..4f61a240 --- /dev/null +++ b/src/chaoskey-v0.1/flash-loader/Makefile @@ -0,0 +1,8 @@ +# +# AltOS flash loader build +# +# + +TOPDIR=../.. +HARDWARE=chaoskey-v0.1 +include $(TOPDIR)/stmf0/Makefile-flash.defs diff --git a/src/chaoskey-v0.1/flash-loader/ao_pins.h b/src/chaoskey-v0.1/flash-loader/ao_pins.h new file mode 100644 index 00000000..295e0258 --- /dev/null +++ b/src/chaoskey-v0.1/flash-loader/ao_pins.h @@ -0,0 +1,36 @@ +/* + * 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_ */ diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 19beb78f..a8016673 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -707,8 +707,7 @@ static int tncPositionPacket(void)      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; @@ -719,28 +718,99 @@ static int tncPositionPacket(void)      }      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); diff --git a/src/drivers/ao_btm.c b/src/drivers/ao_btm.c index 93d9dd9d..8e7052cb 100644 --- a/src/drivers/ao_btm.c +++ b/src/drivers/ao_btm.c @@ -263,6 +263,15 @@ uint8_t  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();  } @@ -350,6 +359,10 @@ __xdata struct ao_task ao_btm_task;  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  	 */ @@ -380,6 +393,8 @@ ao_btm(void)  	/* 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); @@ -388,10 +403,6 @@ ao_btm(void)  	/* 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); diff --git a/src/drivers/ao_cc1200.c b/src/drivers/ao_cc1200.c index df4bd335..6547be39 100644 --- a/src/drivers/ao_cc1200.c +++ b/src/drivers/ao_cc1200.c @@ -41,7 +41,11 @@ int8_t	ao_radio_rssi;			/* Last received RSSI value */  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) @@ -301,20 +305,28 @@ ao_radio_idle(void)   *	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)) diff --git a/src/telelco-v0.2/ao_lco.c b/src/drivers/ao_lco.c index 12a247bf..b8698a80 100644 --- a/src/telelco-v0.2/ao_lco.c +++ b/src/drivers/ao_lco.c @@ -50,7 +50,7 @@ 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); +	ao_seven_segment_set(AO_LCO_PAD_DIGIT, pad);  }  static void @@ -60,6 +60,30 @@ ao_lco_set_box(uint8_t box)  	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) @@ -79,9 +103,12 @@ 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) +	/* 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; +	return (ao_pad_query.channels >> (pad - 1)) & 1;  }  static uint8_t @@ -89,7 +116,7 @@ ao_lco_pad_first(void)  {  	uint8_t	pad; -	for (pad = 0; pad < AO_PAD_MAX_CHANNELS; pad++) +	for (pad = 1; pad <= AO_PAD_MAX_CHANNELS; pad++)  		if (ao_lco_pad_present(pad))  			return pad;  	return 0; @@ -117,14 +144,14 @@ ao_lco_input(void)  						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 < 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_pad(ao_lco_pad); +						ao_lco_set_display();  					}  				}  				break; @@ -143,8 +170,9 @@ ao_lco_input(void)  					} 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_box(ao_lco_box); +						ao_lco_set_display();  					}  				}  				break; @@ -209,9 +237,12 @@ ao_lco_update(void)  		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); +			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; @@ -223,6 +254,7 @@ ao_lco_update(void)  	       query.igniter_status[2],  	       query.igniter_status[3]);  #endif +	PRINTD("ao_lco_update valid %d\n", ao_lco_valid);  	ao_wakeup(&ao_pad_query);  } @@ -253,8 +285,10 @@ ao_lco_search(void)  	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); @@ -263,7 +297,9 @@ ao_lco_search(void)  			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;  			} @@ -275,9 +311,8 @@ ao_lco_search(void)  		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); +	ao_lco_pad = 1; +	ao_lco_set_display();  }  static void @@ -287,12 +322,12 @@ ao_lco_igniter_status(void)  	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;  		} -		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); @@ -353,15 +388,18 @@ ao_lco_monitor(void)  			       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); +			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(); -			ao_lco_arm(ao_lco_box, 1 << ao_lco_pad, ao_lco_tick_offset); -			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();  		} diff --git a/src/telelco-v0.2/ao_lco.h b/src/drivers/ao_lco.h index 253f9702..253f9702 100644 --- a/src/telelco-v0.2/ao_lco.h +++ b/src/drivers/ao_lco.h diff --git a/src/drivers/ao_pad.c b/src/drivers/ao_pad.c index dc2c83fe..ffe46c68 100644 --- a/src/drivers/ao_pad.c +++ b/src/drivers/ao_pad.c @@ -29,6 +29,10 @@ static __pdata uint8_t	ao_pad_box;  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 @@ -36,8 +40,8 @@ static __pdata uint8_t	ao_pad_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 @@ -123,6 +127,8 @@ ao_pad_monitor(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; @@ -138,7 +144,7 @@ ao_pad_monitor(void)  		}  		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; @@ -255,7 +261,7 @@ ao_pad(void)  		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", diff --git a/src/drivers/ao_pad.h b/src/drivers/ao_pad.h index 23062899..d77d105a 100644 --- a/src/drivers/ao_pad.h +++ b/src/drivers/ao_pad.h @@ -39,6 +39,7 @@ struct ao_pad_query {  	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 */  }; diff --git a/src/drivers/ao_trng.c b/src/drivers/ao_trng.c new file mode 100644 index 00000000..e69cd30b --- /dev/null +++ b/src/drivers/ao_trng.c @@ -0,0 +1,79 @@ +/* + * 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); +} diff --git a/src/drivers/ao_trng.h b/src/drivers/ao_trng.h new file mode 100644 index 00000000..78577428 --- /dev/null +++ b/src/drivers/ao_trng.h @@ -0,0 +1,24 @@ +/* + * 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_ */ diff --git a/src/drivers/ao_trng_send.c b/src/drivers/ao_trng_send.c new file mode 100644 index 00000000..bac6035c --- /dev/null +++ b/src/drivers/ao_trng_send.c @@ -0,0 +1,65 @@ +/* + * 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"); +} diff --git a/src/drivers/ao_trng_send.h b/src/drivers/ao_trng_send.h new file mode 100644 index 00000000..83312d59 --- /dev/null +++ b/src/drivers/ao_trng_send.h @@ -0,0 +1,24 @@ +/* + * 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_ */ diff --git a/src/kernel/ao_config.c b/src/kernel/ao_config.c index 8dab7c42..b0d3e541 100644 --- a/src/kernel/ao_config.c +++ b/src/kernel/ao_config.c @@ -220,6 +220,10 @@ _ao_config_get(void)  		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;  	} @@ -876,6 +880,23 @@ ao_config_aprs_ssid_set(void)  	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 { @@ -969,6 +990,8 @@ __code struct ao_config_var ao_config_vars[] = {  #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 }, diff --git a/src/kernel/ao_config.h b/src/kernel/ao_config.h index 164584a5..cfe8555c 100644 --- a/src/kernel/ao_config.h +++ b/src/kernel/ao_config.h @@ -57,7 +57,7 @@  #endif  #define AO_CONFIG_MAJOR	1 -#define AO_CONFIG_MINOR	21 +#define AO_CONFIG_MINOR	22  #define AO_AES_LEN 16 @@ -115,8 +115,15 @@ struct ao_config {  #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 diff --git a/src/kernel/ao_product.c b/src/kernel/ao_product.c index b9327bac..c711a4d2 100644 --- a/src/kernel/ao_product.c +++ b/src/kernel/ao_product.c @@ -33,6 +33,10 @@ const char ao_product[] = AO_iProduct_STRING;  #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 [] = @@ -45,7 +49,7 @@ 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 */ @@ -69,7 +73,7 @@ AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] =  	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 */ diff --git a/src/kernel/ao_radio_cmac.c b/src/kernel/ao_radio_cmac.c index bff848f6..b6835346 100644 --- a/src/kernel/ao_radio_cmac.c +++ b/src/kernel/ao_radio_cmac.c @@ -91,7 +91,6 @@ radio_cmac_recv(uint8_t len, uint16_t timeout) __reentrant  		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; @@ -114,13 +113,15 @@ radio_cmac_recv(uint8_t len, uint16_t timeout) __reentrant  	/* 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;  } @@ -161,4 +162,3 @@ ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentr  	ao_mutex_put(&ao_radio_cmac_mutex);  	return i;  } - diff --git a/src/kernel/ao_telemetry.h b/src/kernel/ao_telemetry.h index 711e0d36..672d2317 100644 --- a/src/kernel/ao_telemetry.h +++ b/src/kernel/ao_telemetry.h @@ -258,13 +258,14 @@ struct ao_telemetry_metrum_data {  	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 */  }; @@ -332,6 +333,8 @@ union ao_telemetry_all {  	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; diff --git a/src/micropeak/micropeak-load.tmpl b/src/micropeak/micropeak-load.tmpl index 08236a15..c061559d 100644 --- a/src/micropeak/micropeak-load.tmpl +++ b/src/micropeak/micropeak-load.tmpl @@ -8,13 +8,14 @@ LOADSLOW="%LOADSLOW%"  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" diff --git a/src/microsplash/.gitignore b/src/microsplash/.gitignore index 5f6fe3b2..c2062c34 100644 --- a/src/microsplash/.gitignore +++ b/src/microsplash/.gitignore @@ -1,2 +1,3 @@  ao_product.h -microsplash-* +microsplash-v* +microsplash-load diff --git a/src/microsplash/Makefile b/src/microsplash/Makefile index 10cb825b..9bb636f1 100644 --- a/src/microsplash/Makefile +++ b/src/microsplash/Makefile @@ -8,8 +8,15 @@ vpath make-altitude-pa ../util  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 @@ -48,15 +55,13 @@ INC=\  	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) @@ -68,7 +73,7 @@ endif  # Otherwise, print the full command line.  quiet ?= $($1) -all: $(PROG) $(PROG).hex +all: $(PROG) $(HEX) $(SCRIPT)  CHECK=sh ../util/check-avr-mem @@ -76,16 +81,16 @@ $(PROG): Makefile $(OBJ)  	$(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) > $@ @@ -98,22 +103,30 @@ ao_product.o: ao_product.c ao_product.h  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: diff --git a/src/microsplash/microsplash-load.tmpl b/src/microsplash/microsplash-load.tmpl new file mode 100644 index 00000000..c061559d --- /dev/null +++ b/src/microsplash/microsplash-load.tmpl @@ -0,0 +1,21 @@ +#!/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" diff --git a/src/stmf0/ao_adc_fast.c b/src/stmf0/ao_adc_fast.c index be9b5986..26e6691c 100644 --- a/src/stmf0/ao_adc_fast.c +++ b/src/stmf0/ao_adc_fast.c @@ -18,43 +18,53 @@  #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) | @@ -140,7 +150,6 @@ ao_adc_init(void)  #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; @@ -160,14 +169,16 @@ ao_adc_init(void)  	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) | @@ -186,5 +197,4 @@ ao_adc_init(void)  	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);  } diff --git a/src/stmf0/ao_adc_fast.h b/src/stmf0/ao_adc_fast.h index eec45505..c6903e9f 100644 --- a/src/stmf0/ao_adc_fast.h +++ b/src/stmf0/ao_adc_fast.h @@ -26,62 +26,59 @@ ao_adc_init(void);  /* 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();  } diff --git a/src/stmf0/ao_crc_stm.c b/src/stmf0/ao_crc_stm.c index 78efa93a..863f5ef5 100644 --- a/src/stmf0/ao_crc_stm.c +++ b/src/stmf0/ao_crc_stm.c @@ -60,7 +60,7 @@  #endif  #ifndef AO_CRC_INIT -#define AO_CRC_INIT	0xffffffff; +#define AO_CRC_INIT	0xffffffff  #endif  void diff --git a/src/stmf0/ao_exti.h b/src/stmf0/ao_exti.h new file mode 100644 index 00000000..ebea224d --- /dev/null +++ b/src/stmf0/ao_exti.h @@ -0,0 +1,48 @@ +/* + * 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_ */ diff --git a/src/stmf0/ao_usb_stm.c b/src/stmf0/ao_usb_stm.c index 3ea7da5e..b8146c21 100644 --- a/src/stmf0/ao_usb_stm.c +++ b/src/stmf0/ao_usb_stm.c @@ -83,9 +83,13 @@ static uint16_t	ao_usb_sram_addr;  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 @@ -146,12 +150,10 @@ static inline uint16_t *ao_usb_packet_buffer_addr(uint16_t sram_addr)  	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; @@ -323,27 +325,42 @@ ao_usb_init_ep(uint8_t ep, uint32_t addr, uint32_t type, uint32_t stat_rx, uint3  }  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 @@ -370,6 +387,8 @@ ao_usb_set_ep0(void)  	}  	ao_usb_set_address(0); + +	ao_usb_running = 0;  }  static void @@ -378,9 +397,8 @@ ao_usb_set_configuration(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, @@ -389,11 +407,9 @@ ao_usb_set_configuration(void)  		       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, @@ -402,11 +418,8 @@ ao_usb_set_configuration(void)  		       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, @@ -415,6 +428,9 @@ ao_usb_set_configuration(void)  		       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; @@ -916,8 +932,6 @@ ao_usb_alloc(void)  {  	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; @@ -936,12 +950,28 @@ ao_usb_write(uint16_t *buffer, uint16_t len)  {  	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); @@ -1083,6 +1113,9 @@ ao_usb_init(void)  	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 diff --git a/src/telebt-v3.0/ao_pins.h b/src/telebt-v3.0/ao_pins.h index 6e90afcc..a6a01662 100644 --- a/src/telebt-v3.0/ao_pins.h +++ b/src/telebt-v3.0/ao_pins.h @@ -130,7 +130,7 @@ struct ao_adc {  };  #define AO_ADC_DUMP(p) \ -	printf("tick: %5u %5d batt: %5d\n", \ +	printf("tick: %5u batt %5d\n", \  	       (p)->tick, \  	       (p)->adc.v_batt); diff --git a/src/telelco-v0.3/.gitignore b/src/telelco-v0.3/.gitignore new file mode 100644 index 00000000..a32ec26e --- /dev/null +++ b/src/telelco-v0.3/.gitignore @@ -0,0 +1,2 @@ +ao_product.h +telelco*.elf diff --git a/src/telelco-v0.3/Makefile b/src/telelco-v0.3/Makefile new file mode 100644 index 00000000..83d3fc43 --- /dev/null +++ b/src/telelco-v0.3/Makefile @@ -0,0 +1,108 @@ +# +# 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: diff --git a/src/telelco-v0.3/ao_pins.h b/src/telelco-v0.3/ao_pins.h new file mode 100644 index 00000000..92095a7c --- /dev/null +++ b/src/telelco-v0.3/ao_pins.h @@ -0,0 +1,269 @@ +/* + * 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_ */ diff --git a/src/telelco-v0.3/ao_telelco.c b/src/telelco-v0.3/ao_telelco.c new file mode 100644 index 00000000..d9f7c693 --- /dev/null +++ b/src/telelco-v0.3/ao_telelco.c @@ -0,0 +1,70 @@ +/* + * 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; +} diff --git a/src/telelco-v0.3/flash-loader/Makefile b/src/telelco-v0.3/flash-loader/Makefile new file mode 100644 index 00000000..679e61ba --- /dev/null +++ b/src/telelco-v0.3/flash-loader/Makefile @@ -0,0 +1,8 @@ +# +# AltOS flash loader build +# +# + +TOPDIR=../.. +HARDWARE=telelco-v0.2 +include $(TOPDIR)/stm/Makefile-flash.defs diff --git a/src/telelco-v0.3/flash-loader/ao_pins.h b/src/telelco-v0.3/flash-loader/ao_pins.h new file mode 100644 index 00000000..6c8ff7e2 --- /dev/null +++ b/src/telelco-v0.3/flash-loader/ao_pins.h @@ -0,0 +1,34 @@ +/* + * 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_ */ diff --git a/src/test/ao_flight_test.c b/src/test/ao_flight_test.c index fbbc4bd9..f71c3052 100644 --- a/src/test/ao_flight_test.c +++ b/src/test/ao_flight_test.c @@ -554,9 +554,9 @@ ao_insert(void)  			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], @@ -717,7 +717,7 @@ ao_sleep(void *wchan)  					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; @@ -895,7 +895,6 @@ ao_sleep(void *wchan)  				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]); diff --git a/src/usbtrng-v2.0/Makefile b/src/usbtrng-v2.0/Makefile index abbdbbcc..49798f1c 100644 --- a/src/usbtrng-v2.0/Makefile +++ b/src/usbtrng-v2.0/Makefile @@ -12,6 +12,7 @@ INC = \  	ao_pins.h \  	ao_product.h \  	ao_task.h \ +	ao_adc_fast.h \  	stm32f0.h  # @@ -31,6 +32,7 @@ ALTOS_SRC = \  	ao_boot_chain.c \  	ao_cmd.c \  	ao_usb_stm.c \ +	ao_trng.c \  	ao_task.c \  	ao_product.c diff --git a/src/usbtrng-v2.0/ao_pins.h b/src/usbtrng-v2.0/ao_pins.h index 23759444..1997d205 100644 --- a/src/usbtrng-v2.0/ao_pins.h +++ b/src/usbtrng-v2.0/ao_pins.h @@ -60,4 +60,8 @@  #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_ */ diff --git a/src/usbtrng-v2.0/ao_usbtrng.c b/src/usbtrng-v2.0/ao_usbtrng.c index e1f43cdd..42713b6e 100644 --- a/src/usbtrng-v2.0/ao_usbtrng.c +++ b/src/usbtrng-v2.0/ao_usbtrng.c @@ -18,58 +18,7 @@  #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)  { @@ -86,7 +35,8 @@ void main(void)  	ao_usb_init(); -	ao_cmd_register(usbtrng_cmds); +	ao_trng_init(); +  	ao_led_off(AO_LED_RED);  	ao_start_scheduler(); diff --git a/src/util/ao-make-product.5c b/src/util/ao-make-product.5c index 5f2eb8e8..3ab8d16e 100644 --- a/src/util/ao-make-product.5c +++ b/src/util/ao-make-product.5c @@ -1,54 +1,58 @@ -#!/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 = { @@ -66,6 +70,12 @@ argdesc argd = {  			.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 = "id_product", @@ -83,6 +93,12 @@ argdesc argd = {  			.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",  }; @@ -92,11 +108,14 @@ main()  {  	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");  } diff --git a/telegps/TeleGPS.java b/telegps/TeleGPS.java index fe335176..4f83e8e5 100644 --- a/telegps/TeleGPS.java +++ b/telegps/TeleGPS.java @@ -23,8 +23,9 @@ import javax.swing.*;  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 @@ -70,7 +71,7 @@ public class TeleGPS  	JTabbedPane		pane; -	AltosUIMap   		map; +	AltosUIMapNew  		map;  	TeleGPSInfo		gps_info;  	TeleGPSState		gps_state;  	AltosInfoTable		info_table; @@ -172,7 +173,7 @@ public class TeleGPS  	}  	void load_maps() { -		new AltosUIMapPreload(this); +		new AltosUIMapPreloadNew(this);  	}  	void disconnect() { @@ -372,7 +373,6 @@ public class TeleGPS  				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) { @@ -480,8 +480,6 @@ public class TeleGPS  		bag = getContentPane();  		bag.setLayout(new GridBagLayout()); -		GridBagConstraints c = new GridBagConstraints(); -  		setTitle("TeleGPS");  		menu_bar = new JMenuBar(); @@ -491,25 +489,16 @@ public class TeleGPS  		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>(); @@ -517,13 +506,9 @@ public class TeleGPS  		/* 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); @@ -533,15 +518,9 @@ public class TeleGPS  		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); @@ -650,7 +629,6 @@ public class TeleGPS  	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"); @@ -675,16 +653,7 @@ public class TeleGPS  		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; diff --git a/telegps/TeleGPSConfig.java b/telegps/TeleGPSConfig.java index 55763e22..e44c528c 100644 --- a/telegps/TeleGPSConfig.java +++ b/telegps/TeleGPSConfig.java @@ -22,8 +22,8 @@ import javax.swing.*;  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 { diff --git a/telegps/TeleGPSConfigUI.java b/telegps/TeleGPSConfigUI.java index 5e700b72..8c3d0c2e 100644 --- a/telegps/TeleGPSConfigUI.java +++ b/telegps/TeleGPSConfigUI.java @@ -17,12 +17,13 @@  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 @@ -40,6 +41,7 @@ public class TeleGPSConfigUI  	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; @@ -57,6 +59,7 @@ public class TeleGPSConfigUI  	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; @@ -165,11 +168,20 @@ public class TeleGPSConfigUI  	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() { @@ -413,6 +425,33 @@ public class TeleGPSConfigUI  		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; @@ -648,7 +687,11 @@ public class TeleGPSConfigUI  			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(); @@ -848,7 +891,12 @@ public class TeleGPSConfigUI  	}  	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) { @@ -894,4 +942,16 @@ public class TeleGPSConfigUI  		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(); +	}  } diff --git a/telegps/TeleGPSDisplayThread.java b/telegps/TeleGPSDisplayThread.java index 09610f59..d0d97876 100644 --- a/telegps/TeleGPSDisplayThread.java +++ b/telegps/TeleGPSDisplayThread.java @@ -21,8 +21,8 @@ import java.awt.*;  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 { diff --git a/telegps/TeleGPSGraphUI.java b/telegps/TeleGPSGraphUI.java index d3adc748..f9ca9408 100644 --- a/telegps/TeleGPSGraphUI.java +++ b/telegps/TeleGPSGraphUI.java @@ -26,8 +26,8 @@ import javax.swing.*;  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; @@ -38,7 +38,7 @@ public class TeleGPSGraphUI extends AltosUIFrame  	JTabbedPane		pane;  	AltosGraph		graph;  	AltosUIEnable		enable; -	AltosUIMap		map; +	AltosUIMapNew		map;  	AltosState		state;  	AltosFlightStats	stats;  	AltosGraphDataSet	graphDataSet; @@ -69,7 +69,7 @@ public class TeleGPSGraphUI extends AltosUIFrame  		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); diff --git a/telegps/TeleGPSInfo.java b/telegps/TeleGPSInfo.java index f20a8830..a5efb059 100644 --- a/telegps/TeleGPSInfo.java +++ b/telegps/TeleGPSInfo.java @@ -21,8 +21,8 @@ import java.util.*;  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 { diff --git a/telegps/TeleGPSPreferences.java b/telegps/TeleGPSPreferences.java index 6d7b8985..b62ea5a8 100644 --- a/telegps/TeleGPSPreferences.java +++ b/telegps/TeleGPSPreferences.java @@ -22,7 +22,7 @@ import java.awt.event.*;  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 diff --git a/telegps/TeleGPSState.java b/telegps/TeleGPSState.java index 18f500b9..7c410e98 100644 --- a/telegps/TeleGPSState.java +++ b/telegps/TeleGPSState.java @@ -21,8 +21,8 @@ import java.util.*;  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 { @@ -161,6 +161,26 @@ 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()); @@ -203,5 +223,6 @@ public class TeleGPSState extends AltosUIFlightTab {  		add(new FirmwareVersion(this, y++));  		add(new FlightLogMax(this, y++));  		add(new BatteryVoltage(this, y++)); +		add(new ReceiverBattery(this, y++));  	}  } diff --git a/telegps/TeleGPSStatus.java b/telegps/TeleGPSStatus.java index 050a8449..cc9966b8 100644 --- a/telegps/TeleGPSStatus.java +++ b/telegps/TeleGPSStatus.java @@ -19,8 +19,8 @@ package org.altusmetrum.telegps;  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; diff --git a/telegps/TeleGPSStatusUpdate.java b/telegps/TeleGPSStatusUpdate.java index 9c085a5b..1d545927 100644 --- a/telegps/TeleGPSStatusUpdate.java +++ b/telegps/TeleGPSStatusUpdate.java @@ -18,7 +18,7 @@  package org.altusmetrum.telegps;  import java.awt.event.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_7.*;  public class TeleGPSStatusUpdate implements ActionListener { diff --git a/telegps/telegps-windows.nsi.in b/telegps/telegps-windows.nsi.in index 44656715..b0b5d6a6 100644 --- a/telegps/telegps-windows.nsi.in +++ b/telegps/telegps-windows.nsi.in @@ -103,16 +103,17 @@ Section "${REG_NAME} Application"  	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" @@ -141,7 +142,6 @@ Section "File Associations"  	SetOutPath $INSTDIR -	File "../icon/${WIN_APP_EXE}"  	File "../icon/${WIN_TELEM_EXE}"  	File "../icon/${WIN_EEPROM_EXE}" @@ -153,15 +153,13 @@ Section "File Associations"  	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" @@ -176,7 +174,7 @@ Section "File Associations"  	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"  | 
