diff options
28 files changed, 815 insertions, 263 deletions
| diff --git a/altosdroid/Notebook b/altosdroid/Notebook index 6a246df7..38a4bc01 100644 --- a/altosdroid/Notebook +++ b/altosdroid/Notebook @@ -15,17 +15,39 @@ Desired AltosDroid feature list   *) Monitor-idle mode - *) Frequency scanning + *) TeleBT battery voltage - *) Select satellite imaging mode + *) Select tracker by clicking map - *) TeleBT battery voltage + *) Auto select tracker after long delay - *) Deal with long bluetooth list. Currently, a list longer than -    the screen makes it impossible to use entries off the bottom. + *) Evaluate performance issues - *) Pickle/unpickle state instead of reloading entire history from -    file. Current restart time is lengthy. + *) Merge offline/online maps into single tab with mode + + *) Make voice responses depend on selected tab + + *) Monitor TeleMega igniters + + *) Convert to four tab design: + +	1) Pad + +		Report out GPS status, report igniter/battery status changes + +	2) Flight + +		Report height, speed, az/el/range + +	3) Recovery + +		Report range bearing/heading (bearing if stationary, +		heading if in motion + +	4) Map + +		Pick out report based on current flight status +		(presume flight if in motion, recovery otherwise)  Completed features @@ -47,3 +69,28 @@ 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 diff --git a/altosdroid/res/menu/option_menu.xml b/altosdroid/res/menu/option_menu.xml index 2109ae04..9cb57dfc 100644 --- a/altosdroid/res/menu/option_menu.xml +++ b/altosdroid/res/menu/option_menu.xml @@ -22,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" /> @@ -40,4 +37,13 @@      <item android:id="@+id/map_type"  	  android:icon="@android:drawable/ic_menu_mapmode"  	  android:title="@string/map_type" /> +    <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 index 4daed1f8..6c701ea2 100644 --- a/altosdroid/res/values/CustomTheme.xml +++ b/altosdroid/res/values/CustomTheme.xml @@ -1,7 +1,6 @@  <?xml version="1.0" encoding="utf-8"?>  <resources> -  <style name="CustomTheme" parent="android:Theme"> -    <item name="android:windowNoTitle">false</item> +  <style name="CustomTheme" parent="android:Theme.Holo">    </style>  </resources> diff --git a/altosdroid/res/values/strings.xml b/altosdroid/res/values/strings.xml index 1af0dbb7..79a77ba9 100644 --- a/altosdroid/res/values/strings.xml +++ b/altosdroid/res/values/strings.xml @@ -33,6 +33,8 @@  	<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>  	<!-- MapTypeActivity --> diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java index 5be9ba84..942ebbd5 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java @@ -42,19 +42,13 @@ import android.content.res.Resources;  import android.support.v4.app.FragmentActivity;  import android.support.v4.app.FragmentManager;  import android.util.DisplayMetrics; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.Window; -import android.view.View; -import android.view.LayoutInflater; -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_7.*; @@ -82,9 +76,6 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  	private BluetoothAdapter mBluetoothAdapter = null; -	// Layout Views -	private TextView mTitle; -  	// Flight state values  	private TextView mCallsignView;  	private TextView mRSSIView; @@ -93,6 +84,9 @@ 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;  	// field to display the version at the bottom of the screen  	private TextView mVersion; @@ -110,6 +104,8 @@ 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; @@ -134,13 +130,11 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  			switch (msg.what) {  			case MSG_STATE:  				AltosDebug.debug("MSG_STATE"); -				TelemetryState telemetry_state = (TelemetryState) msg.obj; -				if (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:  				AltosDebug.debug("MSG_UPDATE_AGE"); @@ -221,20 +215,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;  		}  	} @@ -256,17 +250,34 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  	boolean	registered_units_listener; -	void update_state(TelemetryState telemetry_state) { +	int	current_serial; + +	void update_state(TelemetryState new_telemetry_state) { + +		if (new_telemetry_state != null) +			telemetry_state = new_telemetry_state; + +		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]); +  		update_title(telemetry_state); -		update_ui(telemetry_state.state, telemetry_state.location); -		if (telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) -			start_timer(); + +		AltosDebug.debug("update state current serial %d\n", current_serial); + +		AltosState	state = null; +		if (telemetry_state.states.containsKey(current_serial)) +			state = telemetry_state.states.get(current_serial); + +		update_ui(telemetry_state, state, telemetry_state.location); + +		start_timer();  	}  	boolean same_string(String a, String b) { @@ -281,12 +292,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; @@ -296,6 +350,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; @@ -370,7 +426,7 @@ 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); @@ -427,9 +483,7 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  		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); @@ -447,11 +501,6 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  		mTabsAdapter.addTab(mTabHost.newTabSpec("map").setIndicator(create_tab_view("Map")), TabMap.class, null);  		mTabsAdapter.addTab(mTabHost.newTabSpec("offmap").setIndicator(create_tab_view("OffMap")), TabMapOffline.class, null); -		// 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); -  		// Display the Version  		mVersion = (TextView) findViewById(R.id.version);  		mVersion.setText("Version: " + BuildInfo.version + @@ -465,6 +514,8 @@ 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);  	}  	private boolean ensureBluetooth() { @@ -737,6 +788,26 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  		}  	} +	void select_tracker(int serial) { +		int i; +		for (i = 0; i < serials.length; i++) +			if (serials[i] == serial) +				break; +		if (i == serials.length) +			return; + +		AltosDebug.debug("Switching to serial %d\n", serial); +		current_serial = serial; +		update_state(null); +	} + +	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; @@ -817,8 +888,43 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  			serverIntent = new Intent(this, MapTypeActivity.class);  			startActivityForResult(serverIntent, REQUEST_MAP_TYPE);  			return true; +		case R.id.select_tracker: +			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("Select a tracker"); +				builder_serial.setItems(trackers, +							new DialogInterface.OnClickListener() { +								public void onClick(DialogInterface dialog, int item) { +									select_tracker(serials[item]); +								} +							}); +				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;  	} -  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java index 75676e28..dfc37153 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java @@ -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 f1f1b6de..017315af 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java @@ -29,11 +29,13 @@ import android.location.Location;  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(); @@ -41,8 +43,8 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen  	}  	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, @@ -59,30 +61,44 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen  		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; -  			ft.show(this); -			show(state, from_receiver, receiver); +			show(last_telem_state, last_state, last_from_receiver, last_receiver);  		} else  			ft.hide(this);  		ft.commitAllowingStateLoss();  	}  	@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(AltosState state, AltosGreatCircle from_receiver, Location receiver, boolean is_current) { +	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; +		AltosDebug.debug("update_ui tab %s is_current %b\n", tab_name(), is_current);  		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/DeviceListActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java index 4af117a2..f36ef267 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java @@ -191,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/TabAscent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java index 797dc7c3..afce937f 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java @@ -30,8 +30,6 @@ import android.widget.TextView;  import android.location.Location;  public class TabAscent extends AltosDroidTab { -	AltosDroid mAltosDroid; -  	private TextView mHeightView;  	private TextView mMaxHeightView;  	private TextView mSpeedView; @@ -46,13 +44,6 @@ public class TabAscent extends AltosDroidTab {  	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); @@ -78,18 +69,11 @@ public class TabAscent extends AltosDroidTab {  		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) { +	public void show(TelemetryState telem_state, 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()); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java index f3f5a0f1..3429ee72 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java @@ -30,8 +30,6 @@ import android.widget.TextView;  import android.location.Location;  public class TabDescent extends AltosDroidTab { -	AltosDroid mAltosDroid; -  	private TextView mSpeedView;  	private TextView mHeightView;  	private TextView mElevationView; @@ -46,14 +44,6 @@ public class TabDescent extends AltosDroidTab {  	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); @@ -81,17 +71,9 @@ public class TabDescent extends AltosDroidTab {  		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) { +	public void show(TelemetryState telem_state, 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()); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java index d37891f7..dd3f938e 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java @@ -29,8 +29,6 @@ import android.widget.TextView;  import android.location.Location;  public class TabLanded extends AltosDroidTab { -	AltosDroid mAltosDroid; -  	private TextView mBearingView;  	private TextView mDistanceView;  	private TextView mTargetLatitudeView; @@ -41,14 +39,6 @@ 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); @@ -66,16 +56,9 @@ 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 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); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java index cea3cac6..ff36875d 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java @@ -33,6 +33,7 @@ import com.google.android.gms.maps.model.PolylineOptions;  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; @@ -43,8 +44,6 @@ import android.widget.TextView;  import android.location.Location;  public class TabMap extends AltosDroidTab { -	AltosDroid mAltosDroid; -  	private SupportMapFragment mMapFragment;  	private GoogleMap mMap;  	private boolean mapLoaded = false; @@ -63,11 +62,40 @@ public class TabMap extends AltosDroidTab {  	private double mapAccuracy = -1; -	@Override -	public void onAttach(Activity activity) { -		super.onAttach(activity); -		mAltosDroid = (AltosDroid) activity; -		mAltosDroid.registerTab(this); +	private Bitmap rocket_bitmap(String text) { + +		/* From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/ +		 */ +		Bitmap orig_bitmap = BitmapFactory.decodeResource(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; + +		AltosDebug.debug("map label x %f y %f\n", x, y); + +		canvas.drawText(text, 0, text.length(), x, y, paint); +		return bitmap; +	} + +	private Marker rocket_marker(int serial, double lat, double lon) { +		Bitmap	bitmap = rocket_bitmap(String.format("%d", serial)); + +		return mMap.addMarker(new MarkerOptions() +				      .icon(BitmapDescriptorFactory.fromBitmap(bitmap)) +				      .position(new LatLng(lat, lon)) +				      .visible(false));  	}  	@Override @@ -102,33 +130,17 @@ public class TabMap extends AltosDroidTab {  		getChildFragmentManager().beginTransaction().add(R.id.map, 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) { -			set_map_type(mAltosDroid.map_type); +			set_map_type(altos_droid.map_type);  			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) -					); +			Bitmap label_bitmap = rocket_bitmap("hello"); + +			mRocketMarker = rocket_marker(1800,0,0);  			mPadMarker = mMap.addMarker(  					new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad)) @@ -156,7 +168,7 @@ public class TabMap extends AltosDroidTab {  	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));  			set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance); @@ -166,6 +178,8 @@ public class TabMap extends AltosDroidTab {  			if (mapLoaded) {  				if (state.gps != null) {  					mRocketMarker.setPosition(new LatLng(state.gps.lat, state.gps.lon)); +					mRocketMarker.setTitle("hello world"); +					mRocketMarker.setSnippet("hello");  					mRocketMarker.setVisible(true);  					mPolyline.setPoints(Arrays.asList(new LatLng(state.pad_lat, state.pad_lon), new LatLng(state.gps.lat, state.gps.lon))); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java index 56e296d9..4b728c23 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java @@ -54,8 +54,6 @@ class Rocket {  public class TabMapOffline extends AltosDroidTab implements AltosMapInterface { -	AltosDroid mAltosDroid; -  	AltosMap map;  	AltosLatLon	here; @@ -325,11 +323,9 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {  	@Override  	public void onAttach(Activity activity) {  		super.onAttach(activity); -		mAltosDroid = (AltosDroid) activity; -		mAltosDroid.registerTab(this);  		map = new AltosMap(this); -		map.set_maptype(mAltosDroid.map_type); +		map.set_maptype(altos_droid.map_type);  		pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad);  		/* arrow at the bottom of the launchpad image */ @@ -348,12 +344,6 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {  	}  	@Override -	public void onDetach() { -		super.onDetach(); -		mAltosDroid = null; -	} - -	@Override  	public void onCreate(Bundle savedInstanceState) {  		super.onCreate(savedInstanceState);  	} @@ -382,7 +372,6 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {  	public void onDestroyView() {  		super.onDestroyView(); -		mAltosDroid.unregisterTab(this);  	}  	private void center(double lat, double lon, double accuracy) { @@ -395,7 +384,7 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {  	public String tab_name() { return "offmap"; } -	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); @@ -411,20 +400,30 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {  			}  			if (state.pad_lat != AltosLib.MISSING && pad == null)  				pad = new AltosLatLon(state.pad_lat, state.pad_lon); +		} -			int serial = state.serial; -			if (serial == AltosLib.MISSING) -				serial = 0; +		if (telem_state != null) { +			Integer[] old_serial = rockets.keySet().toArray(new Integer[0]); +			Integer[] new_serial = telem_state.states.keySet().toArray(new Integer[0]); -			Rocket	rocket = null; +			/* remove deleted keys */ +			for (int serial : old_serial) { +				if (!telem_state.states.containsKey(serial)) +					rockets.remove(serial); +			} + +			/* set remaining keys */ -			if (state.gps != null && state.gps.locked) { -				if (!rockets.containsKey(serial)) { +			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(String.format("%d", serial), this);  					rockets.put(serial, rocket); -				} else -					rocket = rockets.get(serial); -				rocket.set_position(new AltosLatLon(state.gps.lat, state.gps.lon)); +				} +				rocket.set_position(new AltosLatLon(t_state.gps.lat, t_state.gps.lon));  			}  		} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java index f7eb43db..7a256963 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java @@ -30,8 +30,6 @@ import android.widget.TextView;  import android.location.Location;  public class TabPad extends AltosDroidTab { -	AltosDroid mAltosDroid; -  	private TextView mBatteryVoltageView;  	private TextView mBatteryVoltageLabel;  	private GoNoGoLights mBatteryLights; @@ -52,13 +50,6 @@ public class TabPad extends AltosDroidTab {  	private TextView mPadAltitudeView;  	@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); @@ -100,16 +91,9 @@ public class TabPad extends AltosDroidTab {          return v;  	} -	@Override -	public void onDestroy() { -		super.onDestroy(); -		mAltosDroid.unregisterTab(this); -		mAltosDroid = null; -	} -  	public String tab_name() { return "pad"; } -	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); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java index 7c3c2268..28bab401 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java @@ -34,7 +34,7 @@ public class TelemetryLogger {  			logger = null;  		}  	} -	 +  	void handleExternalStorageState() {  		String state = Environment.getExternalStorageState();  		if (Environment.MEDIA_MOUNTED.equals(state)) { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java index 7b29fe44..4d4f0ed1 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java @@ -21,6 +21,7 @@ package org.altusmetrum.AltosDroid;  import java.text.*;  import java.io.*; +import java.util.*;  import java.util.concurrent.*;  import android.os.Handler; @@ -34,25 +35,18 @@ public class TelemetryReader extends Thread {  	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(); @@ -60,14 +54,12 @@ public class TelemetryReader extends Thread {  	}  	public void run() { -		AltosState  state = null; -  		try {  			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) {  					AltosDebug.error("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage());  				} catch (AltosCRCException ce) { @@ -84,12 +76,11 @@ public class TelemetryReader extends Thread {  		}  	} -	public TelemetryReader (AltosLink in_link, Handler in_handler, AltosState in_state) { +	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); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java index eae360db..80694ea7 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; @@ -62,6 +60,7 @@ public class TelemetryService extends Service implements LocationListener {  	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. @@ -120,6 +119,10 @@ public class TelemetryService extends Service implements LocationListener {  				s.address = null;  				s.disconnect(true);  				break; +			case MSG_DELETE_SERIAL: +				AltosDebug.debug("Delete Serial command received"); +				s.delete_serial((Integer) msg.obj); +				break;  			case MSG_SETFREQUENCY:  				AltosDebug.debug("MSG_SETFREQUENCY");  				s.telemetry_state.frequency = (Double) msg.obj; @@ -197,13 +200,8 @@ 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) { -					AltosDebug.debug("Save state"); -					AltosPreferences.set_state(0, s.telemetry_state.state, null); -				}  				AltosDebug.debug("MSG_TELEMETRY"); -				s.send_to_clients(); +				s.telemetry((AltosTelemetry) msg.obj);  				break;  			case MSG_CRC_ERROR:  				// forward crc error messages @@ -217,13 +215,31 @@ 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) { +			AltosDebug.debug("Save state %d", telem.serial); +			AltosPreferences.set_state(telem.serial, state, null); +		} +		send_to_clients(); +	} +  	/* Construct the message to deliver to clients  	 */  	private Message message() {  		if (telemetry_state == null)  			AltosDebug.debug("telemetry_state null!"); -		if (telemetry_state.state == null) -			AltosDebug.debug("telemetry_state.state null!"); +		if (telemetry_state.states == null) +			AltosDebug.debug("telemetry_state.states null!");  		return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state);  	} @@ -332,6 +348,12 @@ public class TelemetryService extends Service implements LocationListener {  		}  	} +	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); @@ -372,7 +394,7 @@ public class TelemetryService extends Service implements LocationListener {  		telemetry_state.connect = TelemetryState.CONNECT_CONNECTED;  		telemetry_state.address = address; -		telemetry_reader = new TelemetryReader(altos_link, handler, telemetry_state.state); +		telemetry_reader = new TelemetryReader(altos_link, handler);  		telemetry_reader.start();  		AltosDebug.debug("connected TelemetryReader started"); @@ -406,11 +428,32 @@ 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(); -		if (saved_state != null) { -			AltosDebug.debug("recovered old state flight %d\n", saved_state.state.flight); -			telemetry_state.state = saved_state.state; +		telemetry_state.latest_serial = AltosPreferences.latest_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 diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java index fb7e1893..d023128f 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java @@ -17,6 +17,7 @@  package org.altusmetrum.AltosDroid; +import java.util.*;  import org.altusmetrum.altoslib_7.*;  import android.location.Location; @@ -29,16 +30,19 @@ public class TelemetryState {  	int		connect;  	DeviceAddress	address;  	AltosConfigData	config; -	AltosState	state;  	Location	location;  	int		crc_errors;  	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;  		frequency = AltosPreferences.frequency(0); diff --git a/altoslib/AltosPreferences.java b/altoslib/AltosPreferences.java index 2fad96fd..cdff93f6 100644 --- a/altoslib/AltosPreferences.java +++ b/altoslib/AltosPreferences.java @@ -19,6 +19,7 @@ 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"; @@ -360,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; 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/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/configure.ac b/configure.ac index d7a14aa8..e0d5f422 100644 --- a/configure.ac +++ b/configure.ac @@ -551,6 +551,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 | 
