Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
authorRobert Garbee <robert@gag.com>
Sat, 13 Jun 2015 23:40:59 +0000 (17:40 -0600)
committerRobert Garbee <robert@gag.com>
Sat, 13 Jun 2015 23:40:59 +0000 (17:40 -0600)
28 files changed:
altosdroid/Notebook
altosdroid/res/menu/option_menu.xml
altosdroid/res/values/Colors.xml [new file with mode: 0644]
altosdroid/res/values/CustomTheme.xml
altosdroid/res/values/strings.xml
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java
altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java
altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java
altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java
altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java
altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java
altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java
altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java
altoslib/AltosPreferences.java
ao-bringup/cal-freq
ao-bringup/turnon_telegps
ao-tools/Makefile.am
ao-tools/ao-cal-freq/.gitignore [new file with mode: 0644]
ao-tools/ao-cal-freq/Makefile.am [new file with mode: 0644]
ao-tools/ao-cal-freq/ao-cal-freq.1 [new file with mode: 0644]
ao-tools/ao-cal-freq/ao-cal-freq.c [new file with mode: 0644]
configure.ac

index 6a246df7fe3dca22e5cc60632ee0ca8c8ed0697d..38a4bc013d0008d9192f00c2cc8ba06943ea8e6c 100644 (file)
@@ -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
index 2109ae04be8d1e1f7f2a013090ea8b8fcf802f82..9cb57dfc952dfa1d29c818d7ed15cb1a0176c685 100644 (file)
@@ -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" />
     <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 (file)
index 0000000..055c6df
--- /dev/null
@@ -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>
index 4daed1f897c6a8b381f3cc089d8fd3a580d35b45..6c701ea25c7cb70f24e50aa0408120bd5bd1a3a8 100644 (file)
@@ -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>
index 1af0dbb70ce9f0625d4fc7d1176ad70bc7020307..79a77ba94c085d0201df637b8de1d35d796740d9 100644 (file)
@@ -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 -->
index 5be9ba84429f38190e9d5038648fa85eab453600..942ebbd5f06d1b4b50441b0677fb948ad23ec54c 100644 (file)
@@ -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;
        }
-
 }
index 75676e284909d4e72038ccb0e827d75cb0f4d1fc..dfc3715323dbaad67db4b2fe93853823a0539119 100644 (file)
@@ -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);
        }
 
index f1f1b6de853e6996034d23d305b8b51a909fb2a4..017315af6c24ef6ae4e739a5286f9fe17f850dbd 100644 (file)
@@ -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,17 +61,27 @@ 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();
@@ -77,12 +89,16 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen
                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;
        }
index 4af117a29bbde343c91a0a0b8c6416aa0836280d..f36ef267c9c5c2660c5431e72638a388aa85125f 100644 (file)
@@ -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);
index 797dc7c3ceee8b991420a7b65730b25d1d826e4e..afce937f415f3d8d88122a219c935a0c9adcc3f7 100644 (file)
@@ -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;
@@ -45,13 +43,6 @@ public class TabAscent 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_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());
index f3f5a0f15d841258c7439fe38a9c2eb73be3a167..3429ee721ee734b36a120f508aef1480ef6c722f 100644 (file)
@@ -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());
index d37891f793aff866e2c0130652044ac8c92929aa..dd3f938edd501c56454913c9e0001f37c5f0a32f 100644 (file)
@@ -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);
index cea3cac60b28dd5021b155d31b5bb9e3bba34cb6..ff36875db9470b1f77dbd4bd21e458a7d2137bf3 100644 (file)
@@ -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)));
index 56e296d9eddd9652999ebd851dd436fdcfe8da56..4b728c23ffca828488197511db27d9a744a7cb00 100644 (file)
@@ -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 */
@@ -347,12 +343,6 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {
                here_off_y = here_bitmap.getHeight() / 2;
        }
 
-       @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));
                        }
                }
 
index f7eb43db8e53c9d72af2d18017459b2e9dfa5829..7a256963c27c460929cee19d2126d7aff6c47cd2 100644 (file)
@@ -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;
@@ -51,13 +49,6 @@ public class TabPad extends AltosDroidTab {
        private TextView mPadLongitudeView;
        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);
@@ -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);
index 7c3c2268c0ae8bf70d59ce7d654d7bfa09130eae..28bab40122cb796a7932fc2e706aee869d78bbe1 100644 (file)
@@ -34,7 +34,7 @@ public class TelemetryLogger {
                        logger = null;
                }
        }
-       
+
        void handleExternalStorageState() {
                String state = Environment.getExternalStorageState();
                if (Environment.MEDIA_MOUNTED.equals(state)) {
index 7b29fe44a2f91a9742652f89dcc6c9b96f3d431a..4d4f0ed1971d9f9f0ebc400729715cb48a9dfbb8 100644 (file)
@@ -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);
index eae360db3913e7678aa1e1cd19e3658737bf49d5..80694ea74357385264f19c5f5cd0968a25d69b8c 100644 (file)
 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
index fb7e18935954a5e6ca4f71803733e516ee06fce1..d023128f30bed382373bb85128a8a4c07984bea5 100644 (file)
@@ -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);
index 2fad96fd81e1000343582492ac3c0f69691f725b..cdff93f6ed80bf2286d5b570bf300e25c058c4a1 100644 (file)
@@ -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;
 
index 5d876e21e9e2ead8179ff20d23a0d98a74ecacd1..d3d9dc956f859729289f0e48b93d75c91071f303 100755 (executable)
@@ -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
index 123f0b548655ed086aab21eafcf6821edde6aef2..ba97d503c09d476c1e52068320c286977dbf1f0c 100755 (executable)
@@ -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 $?
index 6a170cbdc7e1c4e6861a85facac6a66c8cf70cf5..66d2560e0faa36b8ce09e8455fe235bf01362067 100644 (file)
@@ -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 (file)
index 0000000..4fe1486
--- /dev/null
@@ -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 (file)
index 0000000..e11c2b0
--- /dev/null
@@ -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 (file)
index 0000000..bfc3101
--- /dev/null
@@ -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 (file)
index 0000000..464faf0
--- /dev/null
@@ -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);
+}
index d7a14aa8a47ff0ba4595d91e591a5ce0f1d9e729..e0d5f4227f54f7b0aa9db0d3e323db3c07e2a3a1 100644 (file)
@@ -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