Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
authorBdale Garbee <bdale@gag.com>
Sat, 6 Sep 2014 19:41:36 +0000 (13:41 -0600)
committerBdale Garbee <bdale@gag.com>
Sat, 6 Sep 2014 19:41:36 +0000 (13:41 -0600)
Conflicts:
ao-bringup/turnon_telemega

45 files changed:
altosdroid/Notebook
altosdroid/res/layout/device_list.xml
altosdroid/res/menu/option_menu.xml
altosdroid/res/values/strings.xml
altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java
altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.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/TabPad.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java [new file with mode: 0644]
altoslib/AltosEepromGPS.java
altoslib/AltosFlightReader.java
altoslib/AltosLog.java
altoslib/AltosPreferences.java
altoslib/AltosReplayReader.java
altoslib/AltosStateIterable.java
altoslib/AltosTelemetryMegaData.java
altoslib/AltosTelemetryReader.java
altosui/AltosConfigPyroUI.java
altosui/AltosConfigTD.java
altosui/AltosPad.java
ao-bringup/test-easymega [new file with mode: 0755]
ao-bringup/turnon_easymega
ao-bringup/turnon_telemega
ao-bringup/turnon_telemetrum
ao-tools/Makefile.am
ao-tools/ao-cal-accel/.gitignore [new file with mode: 0644]
ao-tools/ao-cal-accel/Makefile.am [new file with mode: 0644]
ao-tools/ao-cal-accel/ao-cal-accel.1 [new file with mode: 0644]
ao-tools/ao-cal-accel/ao-cal-accel.c [new file with mode: 0644]
configure.ac
src/drivers/ao_pad.c
src/kernel/ao_log.c
src/kernel/ao_log.h
src/kernel/ao_pyro.c
src/kernel/ao_tracker.c

index ebb3578d0f4d4202db3390151338053e4f9bd868..5a4df032ef35a36b6a0511a3c7721cd352f61aa8 100644 (file)
@@ -8,6 +8,8 @@ Desired AltosDroid feature list
 
  *) Highlight current frequency in the frequency list.
 
+       Placed current frequency in title bar
+
  *) Random frequency selection. Provide some mechanism to input
     arbitrary radio frequencies. Could be like AltosUI which allows
     you to edit the list of frequencies and assign names to them,
@@ -23,10 +25,16 @@ Desired AltosDroid feature list
 
  *) Remember most-recently-used TBT and frequency, perhaps
     auto-connect at startup.
+
+       Done
     
  *) Re-loading flight data from .telem file to get back to
     'find my rocket' mode after shutting down the application.
 
+       Done
+
  *) Imperial Units mode
 
+       Done
+
  *) TeleBT battery voltage
index 395695f82e16d48334783bc5a7d41ed2d9a2fd75..93d655172d608655323207004068f85d59b49626 100644 (file)
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     >
-    <TextView android:id="@+id/title_paired_devices"
+    <Button android:id="@+id/button_scan"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:text="@string/title_paired_devices"
+        android:text="@string/button_scan"
+    />
+    <TextView android:id="@+id/title_new_devices"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/title_other_devices"
         android:visibility="gone"
         android:background="#666"
         android:textColor="#fff"
         android:paddingLeft="5dp"
     />
-    <ListView android:id="@+id/paired_devices"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:stackFromBottom="true"
-        android:layout_weight="1"
-    />
-    <TextView android:id="@+id/title_new_devices"
+    <TextView android:id="@+id/title_paired_devices"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:text="@string/title_other_devices"
+        android:text="@string/title_paired_devices"
         android:visibility="gone"
         android:background="#666"
         android:textColor="#fff"
         android:paddingLeft="5dp"
     />
-    <ListView android:id="@+id/new_devices"
+    <ListView android:id="@+id/paired_devices"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:stackFromBottom="true"
-        android:layout_weight="2"
+        android:layout_weight="1"
     />
-    <Button android:id="@+id/button_scan"
+    <ListView android:id="@+id/new_devices"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:text="@string/button_scan"
+        android:stackFromBottom="true"
+        android:layout_weight="2"
     />
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
index ee9d475f88029931288eab3a5b370df2390fba71..3bd5a54e2bb4b6d6dd0abcb81f5fb39d94e7954e 100644 (file)
     <item android:id="@+id/connect_scan"
           android:icon="@android:drawable/ic_menu_search"
           android:title="@string/connect_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/select_rate"
           android:icon="@android:drawable/ic_menu_preferences"
           android:title="@string/select_rate" />
+    <item android:id="@+id/change_units"
+         android:icon="@android:drawable/ic_menu_view"
+         android:title="@string/change_units" />
 </menu>
index ce335b76be47e9751ec108d3c1af60018e09716f..0cc9934937ad07634cbf97a75c19b5d51020f75d 100644 (file)
        <!-- AltosDroid -->
        <string name="bt_not_enabled">Bluetooth was not enabled.</string>
        <string name="title_connecting">connecting…</string>
-       <string name="title_connected_to">connected</string>
+       <string name="title_connected_to">connected</string>
        <string name="title_not_connected">not connected</string>
 
        <!-- Options Menu -->
        <string name="connect_device">Connect a device</string>
+       <string name="quit">Quit</string>
        <string name="select_freq">Select radio frequency</string>
        <string name="select_rate">Select data rate</string>
+       <string name="change_units">Change units</string>
 
        <!-- DeviceListActivity -->
        <string name="scanning">scanning for devices…</string>
index 484efaf833a6ee4ad2e41ddc13e771d60473e82f..51ef5e94652d2c2dfe47a3d0b774497d873a1f7c 100644 (file)
@@ -52,6 +52,7 @@ public class AltosBluetooth extends AltosLink {
 
        // Constructor
        public AltosBluetooth(BluetoothDevice in_device, Handler in_handler) {
+//             set_debug(D);
                adapter = BluetoothAdapter.getDefaultAdapter();
                device = in_device;
                handler = in_handler;
@@ -136,9 +137,27 @@ public class AltosBluetooth extends AltosLink {
                }
        }
 
+       public double frequency() {
+               return frequency;
+       }
+
+       public int telemetry_rate() {
+               return telemetry_rate;
+       }
+
+       public void save_frequency() {
+               AltosPreferences.set_frequency(0, frequency);
+       }
+
+       public void save_telemetry_rate() {
+               AltosPreferences.set_telemetry_rate(0, telemetry_rate);
+       }
+
        private synchronized void wait_connected() throws InterruptedException, IOException {
                if (input == null) {
+                       if (D) Log.d(TAG, "wait_connected...");
                        wait();
+                       if (D) Log.d(TAG, "wait_connected done");
                        if (input == null) throw new IOException();
                }
        }
index c9c38d9819a6e7ac798d3a12a800fdcf93dfe300..f6cceac947dbab97cc6b6d838a7fd22e2f21f960 100644 (file)
@@ -44,32 +44,34 @@ import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.Window;
+import android.view.View;
 import android.widget.TabHost;
 import android.widget.TextView;
+import android.widget.RelativeLayout;
 import android.widget.Toast;
 import android.app.AlertDialog;
 import android.location.Location;
 
 import org.altusmetrum.altoslib_5.*;
 
-public class AltosDroid extends FragmentActivity {
+public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
        // Debugging
        static final String TAG = "AltosDroid";
        static final boolean D = true;
 
        // Message types received by our Handler
-       public static final int MSG_STATE_CHANGE    = 1;
-       public static final int MSG_TELEMETRY       = 2;
-       public static final int MSG_UPDATE_AGE      = 3;
-       public static final int MSG_LOCATION        = 4;
-       public static final int MSG_CRC_ERROR       = 5;
+
+       public static final int MSG_STATE           = 1;
+       public static final int MSG_UPDATE_AGE      = 2;
 
        // Intent request codes
-       private static final int REQUEST_CONNECT_DEVICE = 1;
-       private static final int REQUEST_ENABLE_BT      = 2;
+       public static final int REQUEST_CONNECT_DEVICE = 1;
+       public static final int REQUEST_ENABLE_BT      = 2;
 
        public static FragmentManager   fm;
 
+       private BluetoothAdapter mBluetoothAdapter = null;
+
        // Layout Views
        private TextView mTitle;
 
@@ -78,12 +80,16 @@ public class AltosDroid extends FragmentActivity {
        private TextView mRSSIView;
        private TextView mSerialView;
        private TextView mFlightView;
+       private RelativeLayout mStateLayout;
        private TextView mStateView;
        private TextView mAgeView;
 
        // field to display the version at the bottom of the screen
        private TextView mVersion;
 
+       private double frequency;
+       private int telemetry_rate;
+
        // Tabs
        TabHost         mTabHost;
        AltosViewPager  mViewPager;
@@ -92,23 +98,14 @@ public class AltosDroid extends FragmentActivity {
        int             tabHeight;
 
        // Timer and Saved flight state for Age calculation
-       private Timer timer = new Timer();
+       private Timer timer;
        AltosState saved_state;
-       Location saved_location;
 
        // Service
        private boolean mIsBound   = false;
        private Messenger mService = null;
        final Messenger mMessenger = new Messenger(new IncomingHandler(this));
 
-       // Preferences
-       private AltosDroidPreferences prefs = null;
-
-       // TeleBT Config data
-       private AltosConfigData mConfigData = null;
-       // Local Bluetooth adapter
-       private BluetoothAdapter mBluetoothAdapter = null;
-
        // Text to Speech
        private AltosVoice mAltosVoice = null;
 
@@ -120,39 +117,21 @@ public class AltosDroid extends FragmentActivity {
                @Override
                public void handleMessage(Message msg) {
                        AltosDroid ad = mAltosDroid.get();
+
                        switch (msg.what) {
-                       case MSG_STATE_CHANGE:
-                               if(D) Log.d(TAG, "MSG_STATE_CHANGE: " + msg.arg1);
-                               switch (msg.arg1) {
-                               case TelemetryService.STATE_CONNECTED:
-                                       ad.mConfigData = (AltosConfigData) msg.obj;
-                                       String str = String.format(" %s S/N: %d", ad.mConfigData.product, ad.mConfigData.serial);
-                                       ad.mTitle.setText(R.string.title_connected_to);
-                                       ad.mTitle.append(str);
-                                       Toast.makeText(ad.getApplicationContext(), "Connected to " + str, Toast.LENGTH_SHORT).show();
-                                       break;
-                               case TelemetryService.STATE_CONNECTING:
-                                       ad.mTitle.setText(R.string.title_connecting);
-                                       break;
-                               case TelemetryService.STATE_READY:
-                               case TelemetryService.STATE_NONE:
-                                       ad.mConfigData = null;
-                                       ad.mTitle.setText(R.string.title_not_connected);
-                                       break;
+                       case MSG_STATE:
+                               if(D) Log.d(TAG, "MSG_STATE");
+                               TelemetryState telemetry_state = (TelemetryState) msg.obj;
+                               if (telemetry_state == null) {
+                                       Log.d(TAG, "telemetry_state null!");
+                                       return;
                                }
-                               break;
-                       case MSG_TELEMETRY:
-                               ad.update_ui((AltosState) msg.obj);
-                               break;
-                       case MSG_LOCATION:
-                               ad.set_location((Location) msg.obj);
-                               break;
-                       case MSG_CRC_ERROR:
+
+                               ad.update_state(telemetry_state);
                                break;
                        case MSG_UPDATE_AGE:
-                               if (ad.saved_state != null) {
-                                       ad.mAgeView.setText(String.format("%d", (System.currentTimeMillis() - ad.saved_state.received_time + 500) / 1000));
-                               }
+                               if(D) Log.d(TAG, "MSG_UPDATE_AGE");
+                               ad.update_age();
                                break;
                        }
                }
@@ -208,10 +187,65 @@ public class AltosDroid extends FragmentActivity {
                mTabs.remove(mTab);
        }
 
-       void set_location(Location location) {
-               saved_location = location;
-               Log.d(TAG, "set_location");
-               update_ui(saved_state);
+       public void units_changed(boolean imperial_units) {
+               for (AltosDroidTab mTab : mTabs)
+                       mTab.units_changed(imperial_units);
+       }
+
+       void update_title(TelemetryState telemetry_state) {
+               switch (telemetry_state.connect) {
+               case TelemetryState.CONNECT_CONNECTED:
+                       if (telemetry_state.config != null) {
+                               String str = String.format("S/N %d %6.3f MHz", telemetry_state.config.serial,
+                                                          telemetry_state.frequency);
+                               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);
+                       } else {
+                               mTitle.setText(R.string.title_connected_to);
+                       }
+                       break;
+               case TelemetryState.CONNECT_CONNECTING:
+                       mTitle.setText(R.string.title_connecting);
+                       break;
+               case TelemetryState.CONNECT_READY:
+               case TelemetryState.CONNECT_NONE:
+                       mTitle.setText(R.string.title_not_connected);
+                       break;
+               }
+       }
+
+       void start_timer() {
+               if (timer == null) {
+                       timer = new Timer();
+                       timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 1000L, 1000L);
+               }
+       }
+
+       void stop_timer() {
+               if (timer != null) {
+                       timer.cancel();
+                       timer.purge();
+                       timer = null;
+               }
+       }
+
+       boolean registered_units_listener;
+
+       void update_state(TelemetryState telemetry_state) {
+
+               if (!registered_units_listener) {
+                       registered_units_listener = true;
+                       AltosPreferences.register_units_listener(this);
+               }
+
+               update_title(telemetry_state);
+               update_ui(telemetry_state.state, telemetry_state.location);
+               if (telemetry_state.connect == TelemetryState.CONNECT_CONNECTED)
+                       start_timer();
+               else
+                       stop_timer();
        }
 
        boolean same_string(String a, String b) {
@@ -226,42 +260,73 @@ public class AltosDroid extends FragmentActivity {
                }
        }
 
-       void update_ui(AltosState state) {
+       void update_age() {
+               if (saved_state != null)
+                       mAgeView.setText(String.format("%d", (System.currentTimeMillis() - saved_state.received_time + 500) / 1000));
+       }
+
+       void update_ui(AltosState state, Location location) {
 
                Log.d(TAG, "update_ui");
-               if (state != null && saved_state != null) {
-                       if (saved_state.state != state.state) {
-                               String currentTab = mTabHost.getCurrentTabTag();
-                               Log.d(TAG, "switch state");
-                               switch (state.state) {
-                               case AltosLib.ao_flight_boost:
-                                       if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("ascent");
-                                       break;
-                               case AltosLib.ao_flight_drogue:
-                                       if (currentTab.equals("ascent")) mTabHost.setCurrentTabByTag("descent");
-                                       break;
-                               case AltosLib.ao_flight_landed:
-                                       if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("landed");
-                                       break;
-                               }
-                       }
-               }
+
+               int prev_state = AltosLib.ao_flight_invalid;
 
                AltosGreatCircle from_receiver = null;
 
-               if (state != null && saved_location != null && state.gps != null && state.gps.locked) {
-                       double altitude = 0;
-                       if (saved_location.hasAltitude())
-                               altitude = saved_location.getAltitude();
-                       from_receiver = new AltosGreatCircle(saved_location.getLatitude(),
-                                                            saved_location.getLongitude(),
-                                                            altitude,
-                                                            state.gps.lat,
-                                                            state.gps.lon,
-                                                            state.gps.alt);
-               }
+               if (saved_state != null)
+                       prev_state = saved_state.state;
 
                if (state != null) {
+                       Log.d(TAG, String.format("prev state %d new state  %d\n", prev_state, state.state));
+                       if (state.state == AltosLib.ao_flight_stateless) {
+                               boolean prev_locked = false;
+                               boolean locked = false;
+
+                               if(state.gps != null)
+                                       locked = state.gps.locked;
+                               if (saved_state != null && saved_state.gps != null)
+                                       prev_locked = saved_state.gps.locked;
+                               if (prev_locked != locked) {
+                                       String currentTab = mTabHost.getCurrentTabTag();
+                                       if (locked) {
+                                               if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent");
+                                       } else {
+                                               if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("pad");
+                                       }
+                               }
+                       } else {
+                               if (prev_state != state.state) {
+                                       String currentTab = mTabHost.getCurrentTabTag();
+                                       Log.d(TAG, "switch state");
+                                       switch (state.state) {
+                                       case AltosLib.ao_flight_boost:
+                                               if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("ascent");
+                                               break;
+                                       case AltosLib.ao_flight_drogue:
+                                               if (currentTab.equals("ascent")) mTabHost.setCurrentTabByTag("descent");
+                                               break;
+                                       case AltosLib.ao_flight_landed:
+                                               if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("landed");
+                                               break;
+                                       case AltosLib.ao_flight_stateless:
+                                               if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent");
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if (location != null && state.gps != null && state.gps.locked) {
+                               double altitude = 0;
+                               if (location.hasAltitude())
+                                       altitude = location.getAltitude();
+                               from_receiver = new AltosGreatCircle(location.getLatitude(),
+                                                                    location.getLongitude(),
+                                                                    altitude,
+                                                                    state.gps.lat,
+                                                                    state.gps.lon,
+                                                                    state.gps.alt);
+                       }
+
                        if (saved_state == null || !same_string(saved_state.callsign, state.callsign)) {
                                Log.d(TAG, "update callsign");
                                mCallsignView.setText(state.callsign);
@@ -276,7 +341,12 @@ public class AltosDroid extends FragmentActivity {
                        }
                        if (saved_state == null || state.state != saved_state.state) {
                                Log.d(TAG, "update state");
-                               mStateView.setText(state.state_name());
+                               if (state.state == AltosLib.ao_flight_stateless) {
+                                       mStateLayout.setVisibility(View.GONE);
+                               } else {
+                                       mStateView.setText(state.state_name());
+                                       mStateLayout.setVisibility(View.VISIBLE);
+                               }
                        }
                        if (saved_state == null || state.rssi != saved_state.rssi) {
                                Log.d(TAG, "update rssi");
@@ -285,10 +355,10 @@ public class AltosDroid extends FragmentActivity {
                }
 
                for (AltosDroidTab mTab : mTabs)
-                       mTab.update_ui(state, from_receiver, saved_location, mTab == mTabsAdapter.currentItem());
+                       mTab.update_ui(state, from_receiver, location, mTab == mTabsAdapter.currentItem());
 
                if (state != null)
-                       mAltosVoice.tell(state);
+                       mAltosVoice.tell(state, from_receiver);
 
                saved_state = state;
        }
@@ -330,8 +400,6 @@ public class AltosDroid extends FragmentActivity {
                super.onCreate(savedInstanceState);
                if(D) Log.e(TAG, "+++ ON CREATE +++");
 
-               fm = getSupportFragmentManager();
-
                // Get local Bluetooth adapter
                mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
 
@@ -339,12 +407,9 @@ public class AltosDroid extends FragmentActivity {
                if (mBluetoothAdapter == null) {
                        Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
                        finish();
-                       return;
                }
 
-               // Initialise preferences
-               prefs = new AltosDroidPreferences(this);
-               AltosPreferences.init(prefs);
+               fm = getSupportFragmentManager();
 
                // Set up the window layout
                requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
@@ -387,7 +452,6 @@ public class AltosDroid extends FragmentActivity {
                for (int i = 0; i < 5; i++)
                        mTabHost.getTabWidget().getChildAt(i).getLayoutParams().height = tabHeight;
 
-
                // Set up the custom title
                mTitle = (TextView) findViewById(R.id.title_left_text);
                mTitle.setText(R.string.app_name);
@@ -403,11 +467,10 @@ public class AltosDroid extends FragmentActivity {
                mRSSIView      = (TextView) findViewById(R.id.rssi_value);
                mSerialView    = (TextView) findViewById(R.id.serial_value);
                mFlightView    = (TextView) findViewById(R.id.flight_value);
+               mStateLayout   = (RelativeLayout) findViewById(R.id.state_container);
                mStateView     = (TextView) findViewById(R.id.state_value);
                mAgeView       = (TextView) findViewById(R.id.age_value);
 
-               timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 1000L, 100L);
-
                mAltosVoice = new AltosVoice(this);
        }
 
@@ -416,15 +479,16 @@ public class AltosDroid extends FragmentActivity {
                super.onStart();
                if(D) Log.e(TAG, "++ ON START ++");
 
+               // Start Telemetry Service
+               startService(new Intent(AltosDroid.this, TelemetryService.class));
+
                if (!mBluetoothAdapter.isEnabled()) {
                        Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
-                       startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
+                       startActivityForResult(enableIntent, AltosDroid.REQUEST_ENABLE_BT);
                }
 
-               // Start Telemetry Service
-               startService(new Intent(AltosDroid.this, TelemetryService.class));
-
                doBindService();
+
        }
 
        @Override
@@ -453,6 +517,7 @@ public class AltosDroid extends FragmentActivity {
                if(D) Log.e(TAG, "--- ON DESTROY ---");
 
                if (mAltosVoice != null) mAltosVoice.stop();
+               stop_timer();
        }
 
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
@@ -480,19 +545,21 @@ public class AltosDroid extends FragmentActivity {
                }
        }
 
-       private void connectDevice(Intent data) {
-               // Get the device MAC address
-               String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
-               // Get the BLuetoothDevice object
-               BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
+       private void connectDevice(String address) {
                // Attempt to connect to the device
                try {
-                       if (D) Log.d(TAG, "Connecting to " + device.getName());
-                       mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, device));
+                       if (D) Log.d(TAG, "Connecting to " + address);
+                       mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, address));
                } catch (RemoteException e) {
                }
        }
 
+       private void connectDevice(Intent data) {
+               // Get the device MAC address
+               String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
+               connectDevice(address);
+       }
+
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
                MenuInflater inflater = getMenuInflater();
@@ -550,6 +617,11 @@ public class AltosDroid extends FragmentActivity {
                        serverIntent = new Intent(this, DeviceListActivity.class);
                        startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
                        return true;
+               case R.id.quit:
+                       Log.d(TAG, "R.id.quit");
+                       stopService(new Intent(AltosDroid.this, TelemetryService.class));
+                       finish();
+                       return true;
                case R.id.select_freq:
                        // Set the TBT radio frequency
 
@@ -597,6 +669,10 @@ public class AltosDroid extends FragmentActivity {
                        AlertDialog alert_rate = builder_rate.create();
                        alert_rate.show();
                        return true;
+               case R.id.change_units:
+                       boolean imperial = AltosPreferences.imperial_units();
+                       AltosPreferences.set_imperial_units(!imperial);
+                       return true;
                }
                return false;
        }
index 70d7def828706a972211872d4f82cb80e6d20cc9..9cef131905abad0c1437dc1722aa155a0cbfba38 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ * 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
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
-
 package org.altusmetrum.AltosDroid;
 
-import java.io.File;
-import java.util.Map;
 import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Environment;
-
 import org.altusmetrum.altoslib_5.*;
 
-public class AltosDroidPreferences implements AltosPreferencesBackend {
-       public final static String        NAME    = "org.altusmetrum.AltosDroid";
-       private Context                   context = null;
-       private SharedPreferences         prefs   = null;
-       private SharedPreferences.Editor  editor  = null;
-
-       public AltosDroidPreferences(Context in_context) {
-               this(in_context, NAME);
-       }
-
-       public AltosDroidPreferences(Context in_context, String in_prefs) {
-               context = in_context;
-               prefs   = context.getSharedPreferences(in_prefs, 0);
-               editor  = prefs.edit();
-       }
-
-       public String[] keys() {
-               Map<String, ?> all = prefs.getAll();
-               return (String[])all.keySet().toArray();
-       }
-
-       public AltosPreferencesBackend node(String key) {
-               return new AltosDroidPreferences(context, key);
-       }
-
-       public boolean nodeExists(String key) {
-               return prefs.contains(key);
-       }
-
-       public boolean getBoolean(String key, boolean def) {
-               return prefs.getBoolean(key, def);
-       }
-
-       public double getDouble(String key, double def) {
-               Float f = Float.valueOf(prefs.getFloat(key, (float)def));
-               return f.doubleValue();
-       }
-
-       public int getInt(String key, int def) {
-               return prefs.getInt(key, def);
-       }
-
-       public String getString(String key, String def) {
-               return prefs.getString(key, def);
-       }
+public class AltosDroidPreferences extends AltosPreferences {
 
-       public void putBoolean(String key, boolean value) {
-               editor.putBoolean(key, value);
-       }
+       /* Active device preference name */
+       final static String activeDevicePreference = "ACTIVE-DEVICE";
 
-       public void putDouble(String key, double value) {
-               editor.putFloat(key, (float)value);
-       }
+       static String active_device_address;
 
-       public void putInt(String key, int value) {
-               editor.putInt(key, value);
-       }
+       public static void init(Context context) {
+               if (backend != null)
+                       return;
 
-       public void putString(String key, String value) {
-               editor.putString(key, value);
-       }
+               AltosPreferences.init(new AltosDroidPreferencesBackend(context));
 
-       public void remove(String key) {
-               editor.remove(key);
+               active_device_address = backend.getString(activeDevicePreference, null);
        }
 
-       public void flush() {
-               editor.apply();
+       public static void set_active_device(String address) {
+               synchronized(backend) {
+                       active_device_address = address;
+                       backend.putString(activeDevicePreference, active_device_address);
+                       flush_preferences();
+               }
        }
 
-       public File homeDirectory() {
-               return Environment.getExternalStorageDirectory();
+       public static String active_device() {
+               synchronized(backend) {
+                       return active_device_address;
+               }
        }
 }
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java
new file mode 100644 (file)
index 0000000..be41ae7
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.io.File;
+import java.util.Map;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Environment;
+
+import org.altusmetrum.altoslib_5.*;
+
+public class AltosDroidPreferencesBackend implements AltosPreferencesBackend {
+       public final static String        NAME    = "org.altusmetrum.AltosDroid";
+       private Context                   context = null;
+       private SharedPreferences         prefs   = null;
+       private SharedPreferences.Editor  editor  = null;
+
+       public AltosDroidPreferencesBackend(Context in_context) {
+               this(in_context, NAME);
+       }
+
+       public AltosDroidPreferencesBackend(Context in_context, String in_prefs) {
+               context = in_context;
+               prefs   = context.getSharedPreferences(in_prefs, 0);
+               editor  = prefs.edit();
+       }
+
+       public String[] keys() {
+               Map<String, ?> all = prefs.getAll();
+               return (String[])all.keySet().toArray();
+       }
+
+       public AltosPreferencesBackend node(String key) {
+               return new AltosDroidPreferencesBackend(context, key);
+       }
+
+       public boolean nodeExists(String key) {
+               return prefs.contains(key);
+       }
+
+       public boolean getBoolean(String key, boolean def) {
+               return prefs.getBoolean(key, def);
+       }
+
+       public double getDouble(String key, double def) {
+               Float f = Float.valueOf(prefs.getFloat(key, (float)def));
+               return f.doubleValue();
+       }
+
+       public int getInt(String key, int def) {
+               return prefs.getInt(key, def);
+       }
+
+       public String getString(String key, String def) {
+               return prefs.getString(key, def);
+       }
+
+       public void putBoolean(String key, boolean value) {
+               editor.putBoolean(key, value);
+       }
+
+       public void putDouble(String key, double value) {
+               editor.putFloat(key, (float)value);
+       }
+
+       public void putInt(String key, int value) {
+               editor.putInt(key, value);
+       }
+
+       public void putString(String key, String value) {
+               editor.putString(key, value);
+       }
+
+       public void remove(String key) {
+               editor.remove(key);
+       }
+
+       public void flush() {
+               editor.apply();
+       }
+
+       public File homeDirectory() {
+               return Environment.getExternalStorageDirectory();
+       }
+}
index b960eb1a2a7d5c5e81f93e75d3640e049bace381..8e625da6563b8268242d9166a6019cc23e47fac3 100644 (file)
@@ -27,8 +27,9 @@ import android.support.v4.app.FragmentTransaction;
 import android.support.v4.app.FragmentManager;
 import android.location.Location;
 import android.util.Log;
+import android.widget.TextView;
 
-public abstract class AltosDroidTab extends Fragment {
+public abstract class AltosDroidTab extends Fragment implements AltosUnitsListener {
        AltosState              last_state;
        AltosGreatCircle        last_from_receiver;
        Location                last_receiver;
@@ -37,39 +38,46 @@ public abstract class AltosDroidTab extends Fragment {
 
        public abstract String tab_name();
 
+       public void units_changed(boolean imperial_units) {
+               if (!isHidden() && last_state != null)
+                       show(last_state, last_from_receiver, last_receiver);
+       }
+
+       public void set_value(TextView text_view,
+                             AltosUnits units,
+                             int width,
+                             double value) {
+               if (value == AltosLib.MISSING)
+                       text_view.setText("");
+               else
+                       text_view.setText(units.show(width, value));
+       }
+
        public void set_visible(boolean visible) {
                FragmentTransaction     ft = AltosDroid.fm.beginTransaction();
-               if (visible)
+               if (visible) {
+                       AltosState              state = last_state;
+                       AltosGreatCircle        from_receiver = last_from_receiver;
+                       Location                receiver = last_receiver;
+
+                       show(state, from_receiver, receiver);
                        ft.show(this);
-               else
+               else
                        ft.hide(this);
                ft.commit();
        }
 
        public void update_ui(AltosState state, AltosGreatCircle from_receiver, Location receiver, boolean is_current) {
+               last_state = state;
+               last_from_receiver = from_receiver;
+               last_receiver = receiver;
                if (is_current) {
-                       Log.d(AltosDroid.TAG, String.format("%s: visible, performing update", tab_name()));
+                       if (AltosDroid.D) Log.d(AltosDroid.TAG, String.format("%s: visible, performing update", tab_name()));
 
                        show(state, from_receiver, receiver);
                } else {
-                       Log.d(AltosDroid.TAG, String.format("%s: not visible, skipping update", tab_name()));
-                       last_state = state;
-                       last_from_receiver = from_receiver;
-                       last_receiver = receiver;
+                       if (AltosDroid.D) Log.d(AltosDroid.TAG, String.format("%s: not visible, skipping update", tab_name()));
                        return;
                }
        }
-
-       public void onHiddenChanged(boolean hidden) {
-               if (last_state != null && isVisible()) {
-                       AltosState              state = last_state;
-                       AltosGreatCircle        from_receiver = last_from_receiver;
-                       Location                receiver = last_receiver;
-
-                       last_state = null;
-                       last_from_receiver = null;
-                       last_receiver = null;
-                       show(state, from_receiver, receiver);
-               }
-       }
 }
index b05913b68140ce1224f6c028bd3cba3ff4d304fa..969992d3026c20f4fa1d94793353dd03457d6956 100644 (file)
@@ -58,7 +58,7 @@ public class AltosVoice {
                }
        }
 
-       public void tell(AltosState state) {
+       public void tell(AltosState state, AltosGreatCircle from_receiver) {
                if (!tts_enabled) return;
 
                boolean spoke = false;
@@ -68,12 +68,14 @@ public class AltosVoice {
                        if ((old_state == null || old_state.state <= AltosLib.ao_flight_boost) &&
                            state.state > AltosLib.ao_flight_boost) {
                                if (state.max_speed() != AltosLib.MISSING)
-                                       speak(String.format("max speed: %d meters per second.", (int) (state.max_speed() + 0.5)));
+                                       speak(String.format("Max speed: %s.",
+                                                           AltosConvert.speed.say_units(state.max_speed())));
                                spoke = true;
                        } else if ((old_state == null || old_state.state < AltosLib.ao_flight_drogue) &&
                                   state.state >= AltosLib.ao_flight_drogue) {
                                if (state.max_height() != AltosLib.MISSING)
-                                       speak(String.format("max height: %d meters.", (int) (state.max_height() + 0.5)));
+                                       speak(String.format("Max height: %s.",
+                                                           AltosConvert.height.say_units(state.max_height())));
                                spoke = true;
                        }
                }
@@ -88,13 +90,14 @@ public class AltosVoice {
                }
                old_state = state;
                if (idle_thread != null)
-                       idle_thread.notice(state, spoke);
+                       idle_thread.notice(state, from_receiver, spoke);
        }
 
 
        class IdleThread extends Thread {
                boolean            started;
                private AltosState state;
+               private AltosGreatCircle from_receiver;
                int                reported_landing;
                int                report_interval;
                long               report_time;
@@ -112,29 +115,30 @@ public class AltosVoice {
                                return;
                        }
 
-                       /* If the rocket isn't on the pad, then report height */
-                       if (((AltosLib.ao_flight_drogue <= state.state &&
+                       /* If the rocket isn't on the pad, then report location */
+                       if ((AltosLib.ao_flight_drogue <= state.state &&
                              state.state < AltosLib.ao_flight_landed) ||
-                            state.state == AltosLib.ao_flight_stateless) &&
-                           state.range >= 0)
+                            state.state == AltosLib.ao_flight_stateless)
                        {
-                               if (state.from_pad != null) {
-                                       speak(String.format("Height %d, bearing %s %d, elevation %d, range %d.\n",
-                                                           (int) (state.height() + 0.5),
-                                                           state.from_pad.bearing_words(
+                               AltosGreatCircle        position;
+
+                               if (from_receiver != null)
+                                       position = from_receiver;
+                               else
+                                       position = state.from_pad;
+
+                               if (position != null) {
+                                       speak(String.format("Height %s, bearing %s %d, elevation %d, range %s.\n",
+                                                           AltosConvert.height.say_units(state.height()),
+                                                           position.bearing_words(
                                                                    AltosGreatCircle.BEARING_VOICE),
-                                                           (int) (state.from_pad.bearing + 0.5),
-                                                           (int) (state.elevation + 0.5),
-                                                           (int) (state.range + 0.5)));
-                               } else {
-                                       speak(String.format("Height %d, elevation %d, range %d.\n",
-                                                           (int) (state.height() + 0.5),
-                                                           (int) (state.elevation + 0.5),
-                                                           (int) (state.range + 0.5)));
+                                                           (int) (position.bearing + 0.5),
+                                                           (int) (position.elevation + 0.5),
+                                                           AltosConvert.distance.say_units(position.range)));
                                }
                        } else if (state.state > AltosLib.ao_flight_pad) {
                                if (state.height() != AltosLib.MISSING)
-                                       speak(String.format("%d meters", (int) (state.height() + 0.5)));
+                                       speak(AltosConvert.height.say_units(state.height()));
                        } else {
                                reported_landing = 0;
                        }
@@ -153,9 +157,9 @@ public class AltosVoice {
                                else
                                        speak("rocket may have crashed");
                                if (state.from_pad != null)
-                                       speak(String.format("Bearing %d degrees, range %d meters.",
+                                       speak(String.format("Bearing %d degrees, range %s.",
                                                            (int) (state.from_pad.bearing + 0.5),
-                                                           (int) (state.from_pad.distance + 0.5)));
+                                                           AltosConvert.distance.say_units(state.from_pad.distance)));
                                ++reported_landing;
                        }
                }
@@ -186,9 +190,10 @@ public class AltosVoice {
                        }
                }
 
-               public synchronized void notice(AltosState new_state, boolean spoken) {
+               public synchronized void notice(AltosState new_state, AltosGreatCircle new_from_receiver, boolean spoken) {
                        AltosState old_state = state;
                        state = new_state;
+                       from_receiver = new_from_receiver;
                        if (!started && state.state > AltosLib.ao_flight_pad) {
                                started = true;
                                start();
index 8e8d9c03a32c9105f0d6553b461a107d773a785e..267c90f8e57cc2f0df621f82959e5b9a344a4573 100644 (file)
@@ -20,6 +20,7 @@ package org.altusmetrum.AltosDroid;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.widget.ImageView;
+import android.view.View;
 
 public class GoNoGoLights {
        private Boolean state;
@@ -51,14 +52,27 @@ public class GoNoGoLights {
                missing = m;
                set = true;
                if (missing) {
+                       hide();
                        red.setImageDrawable(dGray);
                        green.setImageDrawable(dGray);
                } else if (state) {
                        red.setImageDrawable(dGray);
                        green.setImageDrawable(dGreen);
+                       show();
                } else {
                        red.setImageDrawable(dRed);
                        green.setImageDrawable(dGray);
+                       show();
                }
        }
+
+       public void show() {
+               red.setVisibility(View.VISIBLE);
+               green.setVisibility(View.VISIBLE);
+       }
+
+       public void hide() {
+               red.setVisibility(View.GONE);
+               green.setVisibility(View.GONE);
+       }
 }
index c146c27746d274dbde683582fff5dc9ee56064fa..fa4e3c8b3da13e7c5c957a422bf7e85ab1da9d62 100644 (file)
@@ -91,12 +91,13 @@ public class TabAscent extends AltosDroidTab {
 
        public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
                if (state != null) {
-                       mHeightView.setText(AltosDroid.number("%6.0f m", state.height()));
-                       mMaxHeightView.setText(AltosDroid.number("%6.0f m", state.max_height()));
-                       mSpeedView.setText(AltosDroid.number("%6.0f m/s", state.speed()));
-                       mMaxSpeedView.setText(AltosDroid.number("%6.0f m/s", state.max_speed()));
-                       mAccelView.setText(AltosDroid.number("%6.0f m/s²", state.acceleration()));
-                       mMaxAccelView.setText(AltosDroid.number("%6.0f m/s²", state.max_acceleration()));
+                       set_value(mHeightView, AltosConvert.height, 6, state.height());
+                       set_value(mHeightView, AltosConvert.height, 6, state.height());
+                       set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height());
+                       set_value(mSpeedView, AltosConvert.speed, 6, state.speed());
+                       set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed());
+                       set_value(mAccelView, AltosConvert.accel, 6, state.acceleration());
+                       set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration());
 
                        if (state.gps != null) {
                                mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
index 6d781efd36a3e43dbde3988d02227c33cc302d39..28068666a4df627de6302450d8182936706205fa 100644 (file)
@@ -93,14 +93,14 @@ public class TabDescent extends AltosDroidTab {
 
        public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
                if (state != null) {
-                       mSpeedView.setText(AltosDroid.number("%6.0f m/s", state.speed()));
-                       mHeightView.setText(AltosDroid.number("%6.0f m", state.height()));
+                       set_value(mSpeedView, AltosConvert.speed, 6, state.speed());
+                       set_value(mHeightView, AltosConvert.height, 6, state.height());
                        if (from_receiver != null) {
                                mElevationView.setText(AltosDroid.number("%3.0f°", from_receiver.elevation));
-                               mRangeView.setText(AltosDroid.number("%6.0f m", from_receiver.range));
+                               set_value(mRangeView, AltosConvert.distance, 6, from_receiver.range);
                                mBearingView.setText(AltosDroid.number("%3.0f°", from_receiver.bearing));
                                mCompassView.setText(from_receiver.bearing_words(AltosGreatCircle.BEARING_LONG));
-                               mDistanceView.setText(AltosDroid.number("%6.0f m", from_receiver.distance));
+                               set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
                        } else { 
                                mElevationView.setText("<unknown>");
                                mRangeView.setText("<unknown>");
index 16427d8b19204f9d3445007e9504441929472475..b257b9365d652c51fc67a3db2676138bb1fe53c6 100644 (file)
@@ -78,7 +78,7 @@ public class TabLanded extends AltosDroidTab {
        public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
                if (from_receiver != null) {
                        mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
-                       mDistanceView.setText(String.format("%6.0f m", from_receiver.distance));
+                       set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
                }
                if (state != null && state.gps != null) {
                        mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
@@ -91,9 +91,9 @@ public class TabLanded extends AltosDroidTab {
                }
 
                if (state != null) {
-                       mMaxHeightView.setText(String.format("%6.0f m", state.max_height()));
-                       mMaxAccelView.setText(String.format("%6.0f m/s²", state.max_acceleration()));
-                       mMaxSpeedView.setText(String.format("%6.0f m/s", state.max_speed()));
+                       set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height());
+                       set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration());
+                       set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed());
                }
        }
 }
index 811e5482bcb442595d36777c9f820355a430ed31..ab338ac26284cc45370a291d1edd99999435dee0 100644 (file)
@@ -158,7 +158,7 @@ public class TabMap extends AltosDroidTab {
        public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
                if (from_receiver != null) {
                        mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
-                       mDistanceView.setText(String.format("%6.0f m", from_receiver.distance));
+                       set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
                }
 
                if (state != null) {
index 03b78b75dbf29a0cfa9e24b766358bee9d388853..32df71d760a2543d55945f967ce2edfcd4a99a5f 100644 (file)
@@ -33,10 +33,13 @@ public class TabPad extends AltosDroidTab {
        AltosDroid mAltosDroid;
 
        private TextView mBatteryVoltageView;
+       private TextView mBatteryVoltageLabel;
        private GoNoGoLights mBatteryLights;
        private TextView mApogeeVoltageView;
+       private TextView mApogeeVoltageLabel;
        private GoNoGoLights mApogeeLights;
        private TextView mMainVoltageView;
+       private TextView mMainVoltageLabel;
        private GoNoGoLights mMainLights;
        private TextView mDataLoggingView;
        private GoNoGoLights mDataLoggingLights;
@@ -59,16 +62,19 @@ public class TabPad extends AltosDroidTab {
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
                View v = inflater.inflate(R.layout.tab_pad, container, false);
                mBatteryVoltageView = (TextView) v.findViewById(R.id.battery_voltage_value);
+               mBatteryVoltageLabel = (TextView) v.findViewById(R.id.battery_voltage_label);
                mBatteryLights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled),
                                                  (ImageView) v.findViewById(R.id.battery_greenled),
                                                  getResources());
 
                mApogeeVoltageView = (TextView) v.findViewById(R.id.apogee_voltage_value);
+               mApogeeVoltageLabel = (TextView) v.findViewById(R.id.apogee_voltage_label);
                mApogeeLights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
                                                 (ImageView) v.findViewById(R.id.apogee_greenled),
                                                 getResources());
 
                mMainVoltageView = (TextView) v.findViewById(R.id.main_voltage_value);
+               mMainVoltageLabel = (TextView) v.findViewById(R.id.main_voltage_label);
                mMainLights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
                                               (ImageView) v.findViewById(R.id.main_greenled),
                                               getResources());
@@ -107,11 +113,23 @@ public class TabPad extends AltosDroidTab {
                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);
-
-                       mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
+                       if (state.apogee_voltage == AltosLib.MISSING) {
+                               mApogeeVoltageView.setVisibility(View.GONE);
+                               mApogeeVoltageLabel.setVisibility(View.GONE);
+                       } else {
+                               mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
+                               mApogeeVoltageView.setVisibility(View.VISIBLE);
+                               mApogeeVoltageLabel.setVisibility(View.VISIBLE);
+                       }
                        mApogeeLights.set(state.apogee_voltage >= AltosLib.ao_igniter_good, state.apogee_voltage == AltosLib.MISSING);
-
-                       mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage));
+                       if (state.main_voltage == AltosLib.MISSING) {
+                               mMainVoltageView.setVisibility(View.GONE);
+                               mMainVoltageLabel.setVisibility(View.GONE);
+                       } else {
+                               mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage));
+                               mMainVoltageView.setVisibility(View.VISIBLE);
+                               mMainVoltageLabel.setVisibility(View.VISIBLE);
+                       }
                        mMainLights.set(state.main_voltage >= AltosLib.ao_igniter_good, state.main_voltage == AltosLib.MISSING);
 
                        if (state.flight != 0) {
@@ -141,12 +159,12 @@ public class TabPad extends AltosDroidTab {
                }
 
                if (receiver != null) {
-                       double altitude = 0;
+                       double altitude = AltosLib.MISSING;
                        if (receiver.hasAltitude())
                                altitude = receiver.getAltitude();
                        mPadLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
                        mPadLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "W", "E"));
-                       mPadAltitudeView.setText(AltosDroid.number("%4.0f m", altitude));
+                       set_value(mPadAltitudeView, AltosConvert.height, 6, altitude);
                }
        }
 
index 3ba5afa9a25aa0b3055b2bca87c0224e75952df2..971c3e80ddab085af28e729d9e34e3197c25641c 100644 (file)
@@ -31,6 +31,7 @@ import org.altusmetrum.altoslib_5.*;
 public class TelemetryReader extends Thread {
 
        private static final String TAG = "TelemetryReader";
+       private static final boolean D = true;
 
        int         crc_errors;
 
@@ -39,6 +40,8 @@ public class TelemetryReader extends Thread {
        AltosLink   link;
        AltosState  state = null;
 
+       AltosFlightReader       stacked;
+
        LinkedBlockingQueue<AltosLine> telemQueue;
 
        public AltosState read() throws ParseException, AltosCRCException, InterruptedException, IOException {
@@ -56,6 +59,10 @@ public class TelemetryReader extends Thread {
 
        public void close() {
                state = null;
+               if (stacked != null) {
+                       stacked.close(false);
+                       stacked = null;
+               }
                link.remove_monitor(telemQueue);
                link = null;
                telemQueue.clear();
@@ -66,7 +73,27 @@ public class TelemetryReader extends Thread {
                AltosState  state = null;
 
                try {
-                       for (;;) {
+                       if (D) Log.d(TAG, "starting reader");
+                       while (stacked != null) {
+                               AltosState      stacked_state = null;
+                               try {
+                                       stacked_state = stacked.read();
+                               } catch (ParseException pe) {
+                                       continue;
+                               } catch (AltosCRCException ce) {
+                                       continue;
+                               }
+                               if (stacked_state != null)
+                                       state = stacked_state;
+                               else
+                                       stacked = null;
+                       }
+                       if (state != null) {
+                               if (D) Log.d(TAG, "Send initial state");
+                               handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget();
+                       }
+                       if (D) Log.d(TAG, "starting loop");
+                       while (telemQueue != null) {
                                try {
                                        state = read();
                                        handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget();
@@ -84,12 +111,34 @@ public class TelemetryReader extends Thread {
                }
        }
 
-       public TelemetryReader (AltosLink in_link, Handler in_handler) {
+       public TelemetryReader (AltosLink in_link, Handler in_handler, AltosFlightReader in_stacked) {
+               if (D) Log.d(TAG, "connected TelemetryReader create started");
                link    = in_link;
                handler = in_handler;
+               stacked = in_stacked;
 
                state = null;
                telemQueue = new LinkedBlockingQueue<AltosLine>();
                link.add_monitor(telemQueue);
+               link.set_telemetry(AltosLib.ao_telemetry_standard);
+
+               if (D) Log.d(TAG, "connected TelemetryReader created");
+       }
+
+       private static AltosFlightReader existing_data(AltosLink link) {
+               if (link == null)
+                       return null;
+
+               File    file = AltosPreferences.logfile(link.serial);
+               if (file != null) {
+                       AltosStateIterable      iterable = AltosStateIterable.iterable(file);
+                       if (iterable != null)
+                               return new AltosReplayReader(iterable.iterator(), file, false);
+               }
+               return null;
+       }
+
+       public TelemetryReader(AltosLink link, Handler handler) {
+               this(link, handler, existing_data(link));
        }
 }
index 4ec353e30e88ff2d298b8bea8cb7005607061699..30d94409a917a339aeeed88b4de31ca560a843b5 100644 (file)
@@ -28,6 +28,7 @@ import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothAdapter;
 import android.content.Intent;
 import android.content.Context;
 import android.os.Bundle;
@@ -63,11 +64,6 @@ public class TelemetryService extends Service implements LocationListener {
        static final int MSG_CRC_ERROR         = 9;
        static final int MSG_SETBAUD           = 10;
 
-       public static final int STATE_NONE       = 0;
-       public static final int STATE_READY      = 1;
-       public static final int STATE_CONNECTING = 2;
-       public static final int STATE_CONNECTED  = 3;
-
        // Unique Identification Number for the Notification.
        // We use it on Notification start, and to cancel it.
        private int NOTIFICATION = R.string.telemetry_service_label;
@@ -81,21 +77,17 @@ public class TelemetryService extends Service implements LocationListener {
        final Messenger mMessenger = new Messenger(mHandler); // Target we publish for clients to send messages to IncomingHandler.
 
        // Name of the connected device
-       private BluetoothDevice device           = null;
+       String address;
        private AltosBluetooth  mAltosBluetooth  = null;
-       private AltosConfigData mConfigData      = null;
        private TelemetryReader mTelemetryReader = null;
        private TelemetryLogger mTelemetryLogger = null;
+       // Local Bluetooth adapter
+       private BluetoothAdapter mBluetoothAdapter = null;
 
-       // internally track state of bluetooth connection
-       private int state = STATE_NONE;
+       private TelemetryState  telemetry_state;
 
        // Last data seen; send to UI when it starts
 
-       private AltosState last_state;
-       private Location last_location;
-       private int last_crc_errors;
-
        // Handler of incoming messages from clients.
        static class IncomingHandler extends Handler {
                private final WeakReference<TelemetryService> service;
@@ -104,17 +96,15 @@ public class TelemetryService extends Service implements LocationListener {
                @Override
                public void handleMessage(Message msg) {
                        TelemetryService s = service.get();
+                       if (s == null)
+                               return;
                        switch (msg.what) {
                        case MSG_REGISTER_CLIENT:
                                s.mClients.add(msg.replyTo);
                                try {
                                        // Now we try to send the freshly connected UI any relavant information about what
-                                       // we're talking to - Basically state and Config Data.
-                                       msg.replyTo.send(Message.obtain(null, AltosDroid.MSG_STATE_CHANGE, s.state, -1, s.mConfigData));
-                                       // We also send any recent telemetry or location data that's cached
-                                       if (s.last_state      != null) msg.replyTo.send(Message.obtain(null, AltosDroid.MSG_TELEMETRY, s.last_state     ));
-                                       if (s.last_location   != null) msg.replyTo.send(Message.obtain(null, AltosDroid.MSG_LOCATION , s.last_location  ));
-                                       if (s.last_crc_errors != 0   ) msg.replyTo.send(Message.obtain(null, AltosDroid.MSG_CRC_ERROR, s.last_crc_errors));
+                                       // we're talking to
+                                       msg.replyTo.send(s.message());
                                } catch (RemoteException e) {
                                        s.mClients.remove(msg.replyTo);
                                }
@@ -126,47 +116,59 @@ public class TelemetryService extends Service implements LocationListener {
                                break;
                        case MSG_CONNECT:
                                if (D) Log.d(TAG, "Connect command received");
-                               s.device = (BluetoothDevice) msg.obj;
-                               s.startAltosBluetooth();
+                               String address = (String) msg.obj;
+                               AltosDroidPreferences.set_active_device(address);
+                               s.startAltosBluetooth(address);
                                break;
                        case MSG_CONNECTED:
                                if (D) Log.d(TAG, "Connected to device");
-                               s.connected();
+                               try {
+                                       s.connected();
+                               } catch (InterruptedException ie) {
+                               }
                                break;
                        case MSG_CONNECT_FAILED:
                                if (D) Log.d(TAG, "Connection failed... retrying");
-                               s.startAltosBluetooth();
+                               if (s.address != null)
+                                       s.startAltosBluetooth(s.address);
                                break;
                        case MSG_DISCONNECTED:
-                               // Only do the following if we haven't been shutdown elsewhere..
-                               if (s.device != null) {
-                                       if (D) Log.d(TAG, "Disconnected from " + s.device.getName());
-                                       s.stopAltosBluetooth();
-                               }
+                               Log.d(TAG, "MSG_DISCONNECTED");
+                               s.stopAltosBluetooth();
                                break;
                        case MSG_TELEMETRY:
                                // forward telemetry messages
-                               s.last_state = (AltosState) msg.obj;
-                               s.sendMessageToClients(Message.obtain(null, AltosDroid.MSG_TELEMETRY, msg.obj));
+                               s.telemetry_state.state = (AltosState) msg.obj;
+                               if (D) Log.d(TAG, "MSG_TELEMETRY");
+                               s.sendMessageToClients();
                                break;
                        case MSG_CRC_ERROR:
                                // forward crc error messages
-                               s.last_crc_errors = (Integer) msg.obj;
-                               s.sendMessageToClients(Message.obtain(null, AltosDroid.MSG_CRC_ERROR, msg.obj));
+                               s.telemetry_state.crc_errors = (Integer) msg.obj;
+                               if (D) Log.d(TAG, "MSG_CRC_ERROR");
+                               s.sendMessageToClients();
                                break;
                        case MSG_SETFREQUENCY:
-                               if (s.state == STATE_CONNECTED) {
+                               if (D) Log.d(TAG, "MSG_SETFREQUENCY");
+                               s.telemetry_state.frequency = (Double) msg.obj;
+                               if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
                                        try {
-                                               s.mAltosBluetooth.set_radio_frequency((Double) msg.obj);
+                                               s.mAltosBluetooth.set_radio_frequency(s.telemetry_state.frequency);
+                                               s.mAltosBluetooth.save_frequency();
                                        } catch (InterruptedException e) {
                                        } catch (TimeoutException e) {
                                        }
                                }
+                               s.sendMessageToClients();
                                break;
                        case MSG_SETBAUD:
-                               if (s.state == STATE_CONNECTED) {
-                                       s.mAltosBluetooth.set_telemetry_rate((Integer) msg.obj);
+                               if (D) Log.d(TAG, "MSG_SETBAUD");
+                               s.telemetry_state.telemetry_rate = (Integer) msg.obj;
+                               if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
+                                       s.mAltosBluetooth.set_telemetry_rate(s.telemetry_state.telemetry_rate);
+                                       s.mAltosBluetooth.save_telemetry_rate();
                                }
+                               s.sendMessageToClients();
                                break;
                        default:
                                super.handleMessage(msg);
@@ -174,9 +176,18 @@ public class TelemetryService extends Service implements LocationListener {
                }
        }
 
-       private void sendMessageToClients(Message m) {
+       private Message message() {
+               if (telemetry_state == null)
+                       Log.d(TAG, "telemetry_state null!");
+               return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state);
+       }
+
+       private void sendMessageToClients() {
+               Message m = message();
+               if (D) Log.d(TAG, String.format("Send message to %d clients", mClients.size()));
                for (int i=mClients.size()-1; i>=0; i--) {
                        try {
+                               if (D) Log.d(TAG, String.format("Send message to client %d", i));
                                mClients.get(i).send(m);
                        } catch (RemoteException e) {
                                mClients.remove(i);
@@ -186,7 +197,7 @@ public class TelemetryService extends Service implements LocationListener {
 
        private void stopAltosBluetooth() {
                if (D) Log.d(TAG, "stopAltosBluetooth(): begin");
-               setState(STATE_READY);
+               telemetry_state.connect = TelemetryState.CONNECT_READY;
                if (mTelemetryReader != null) {
                        if (D) Log.d(TAG, "stopAltosBluetooth(): stopping TelemetryReader");
                        mTelemetryReader.interrupt();
@@ -206,18 +217,21 @@ public class TelemetryService extends Service implements LocationListener {
                        mAltosBluetooth.close();
                        mAltosBluetooth = null;
                }
-               device = null;
-               mConfigData = null;
+               telemetry_state.config = null;
+               if (D) Log.d(TAG, "stopAltosBluetooth(): send message to clients");
+               sendMessageToClients();
        }
 
-       private void startAltosBluetooth() {
-               if (device == null) {
-                       return;
-               }
+       private void startAltosBluetooth(String address) {
+               // Get the BLuetoothDevice object
+               BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
+
+               this.address = address;
                if (mAltosBluetooth == null) {
                        if (D) Log.d(TAG, String.format("startAltosBluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress()));
                        mAltosBluetooth = new AltosBluetooth(device, mHandler);
-                       setState(STATE_CONNECTING);
+                       telemetry_state.connect = TelemetryState.CONNECT_CONNECTING;
+                       sendMessageToClients();
                } else {
                        // This is a bit of a hack - if it appears we're still connected, we treat this as a restart.
                        // So, to give a suitable delay to teardown/bringup, we just schedule a resend of a message
@@ -225,28 +239,19 @@ public class TelemetryService extends Service implements LocationListener {
                        // ... then we tear down the existing connection.
                        // We do it this way around so that we don't lose a reference to the device when this method
                        // is called on reception of MSG_CONNECT_FAILED in the handler above.
-                       mHandler.sendMessageDelayed(Message.obtain(null, MSG_CONNECT, device), 3000);
+                       mHandler.sendMessageDelayed(Message.obtain(null, MSG_CONNECT, address), 3000);
                        stopAltosBluetooth();
                }
        }
 
-       private synchronized void setState(int s) {
-               if (D) Log.d(TAG, "setState(): " + state + " -> " + s);
-               state = s;
-
-               // This shouldn't be required - mConfigData should be null for any non-connected
-               // state, but to be safe and to reduce message size
-               AltosConfigData acd = (state == STATE_CONNECTED) ? mConfigData : null;
-
-               sendMessageToClients(Message.obtain(null, AltosDroid.MSG_STATE_CHANGE, state, -1, acd));
-       }
-
-       private void connected() {
+       private void connected() throws InterruptedException {
+               if (D) Log.d(TAG, "connected top");
                try {
                        if (mAltosBluetooth == null)
                                throw new InterruptedException("no bluetooth");
-                       mConfigData = mAltosBluetooth.config_data();
-               } catch (InterruptedException e) {
+                       telemetry_state.config = mAltosBluetooth.config_data();
+                       mAltosBluetooth.set_radio_frequency(telemetry_state.frequency);
+                       mAltosBluetooth.set_telemetry_rate(telemetry_state.telemetry_rate);
                } catch (TimeoutException e) {
                        // If this timed out, then we really want to retry it, but
                        // probably safer to just retry the connection from scratch.
@@ -254,19 +259,25 @@ public class TelemetryService extends Service implements LocationListener {
                        return;
                }
 
-               setState(STATE_CONNECTED);
+               if (D) Log.d(TAG, "connected bluetooth configured");
+               telemetry_state.connect = TelemetryState.CONNECT_CONNECTED;
 
                mTelemetryReader = new TelemetryReader(mAltosBluetooth, mHandler);
                mTelemetryReader.start();
-               
+
+               if (D) Log.d(TAG, "connected TelemetryReader started");
+
                mTelemetryLogger = new TelemetryLogger(this, mAltosBluetooth);
-       }
 
+               if (D) Log.d(TAG, "Notify UI of connection");
+
+               sendMessageToClients();
+       }
 
        private void onTimerTick() {
                if (D) Log.d(TAG, "Timer wakeup");
                try {
-                       if (mClients.size() <= 0 && state != STATE_CONNECTED) {
+                       if (mClients.size() <= 0 && telemetry_state.connect != TelemetryState.CONNECT_CONNECTED) {
                                stopSelf();
                        }
                } catch (Throwable t) {
@@ -277,19 +288,35 @@ public class TelemetryService extends Service implements LocationListener {
 
        @Override
        public void onCreate() {
+               // Get local Bluetooth adapter
+               mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+               // If the adapter is null, then Bluetooth is not supported
+               if (mBluetoothAdapter == null) {
+                       Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
+               }
+
+               // Initialise preferences
+               AltosDroidPreferences.init(this);
+
+               telemetry_state = new TelemetryState();
+
                // Create a reference to the NotificationManager so that we can update our notifcation text later
                //mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
 
-               setState(STATE_READY);
+               telemetry_state.connect = TelemetryState.CONNECT_READY;
 
                // Start our timer - first event in 10 seconds, then every 10 seconds after that.
                timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 10000L, 10000L);
 
                // Listen for GPS and Network position updates
                LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
-               
+
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
-//             locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
+
+               String address = AltosDroidPreferences.active_device();
+               if (address != null)
+                       startAltosBluetooth(address);
        }
 
        @Override
@@ -345,8 +372,9 @@ public class TelemetryService extends Service implements LocationListener {
 
 
        public void onLocationChanged(Location location) {
-               last_location = location;
-               sendMessageToClients(Message.obtain(null, AltosDroid.MSG_LOCATION, location));
+               telemetry_state.location = location;
+               if (D) Log.d(TAG, "location changed");
+               sendMessageToClients();
        }
 
        public void onStatusChanged(String provider, int status, Bundle extras) {
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java
new file mode 100644 (file)
index 0000000..d334173
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import org.altusmetrum.altoslib_5.*;
+import android.location.Location;
+
+public class TelemetryState {
+       public static final int CONNECT_NONE       = 0;
+       public static final int CONNECT_READY      = 1;
+       public static final int CONNECT_CONNECTING = 2;
+       public static final int CONNECT_CONNECTED  = 3;
+
+       int             connect;
+       AltosConfigData config;
+       AltosState      state;
+       Location        location;
+       int             crc_errors;
+       double          frequency;
+       int             telemetry_rate;
+
+       public TelemetryState() {
+               connect = CONNECT_NONE;
+               config = null;
+               state = null;
+               location = null;
+               crc_errors = 0;
+               frequency = AltosPreferences.frequency(0);
+               telemetry_rate = AltosPreferences.telemetry_rate(0);
+       }
+}
index 482f0b5fa118348122cd814d011da7d12bea3ffa..8c991a6e392a822a5094c406a77f27967f158437 100644 (file)
@@ -91,8 +91,10 @@ public class AltosEepromGPS extends AltosEeprom {
 
                switch (cmd) {
                case AltosLib.AO_LOG_FLIGHT:
-                       state.set_boost_tick(tick);
-                       state.set_flight(flight());
+                       if (state.flight == AltosLib.MISSING) {
+                               state.set_boost_tick(tick);
+                               state.set_flight(flight());
+                       }
                        /* no place to log start lat/lon yet */
                        break;
                case AltosLib.AO_LOG_GPS_TIME:
index c0565e8898d926a6320f43a1191d1d575aa8a019..be103838a2ccd683df7dd48f47c1d9a91a142b9d 100644 (file)
@@ -21,16 +21,16 @@ import java.text.*;
 import java.io.*;
 import java.util.concurrent.*;
 
-public class AltosFlightReader {
+public abstract class AltosFlightReader {
        public String name;
 
        public int serial;
 
-       public void init() { }
+       public void init() {}
 
-       public AltosState read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; }
+       public abstract AltosState read() throws InterruptedException, ParseException, AltosCRCException, IOException;
 
-       public void close(boolean interrupted) { }
+       public abstract void close(boolean interrupted);
 
        public void set_frequency(double frequency) throws InterruptedException, TimeoutException { }
 
index 1cac6b5205a1b6c731b42447a2ef132919114018..28116e3dcdb801a062199a8c7c8f070b1a72b1f8 100644 (file)
@@ -72,6 +72,7 @@ public class AltosLog implements Runnable {
                        }
                        log_file.flush();
                        file = a;
+                       AltosPreferences.set_logfile(link.serial, file);
                }
                return log_file != null;
        }
index 72cfeb4bc5a47e81c63c2ba0582e0de3438b5263..dba57dcb73d055f0509257e809346c721dacde92 100644 (file)
@@ -38,6 +38,9 @@ public class AltosPreferences {
        /* telemetry rate format preference name */
        public final static String telemetryRatePreferenceFormat = "RATE-%d";
 
+       /* log file format preference name */
+       public final static String logfilePreferenceFormat = "LOGFILE-%d";
+
        /* voice preference name */
        public final static String voicePreference = "VOICE";
 
@@ -83,6 +86,9 @@ public class AltosPreferences {
        /* Telemetry rate (map serial to telemetry format) */
        public static Hashtable<Integer, Integer> telemetry_rates;
 
+       /* Log file (map serial to logfile name) */
+       public static Hashtable<Integer, File> logfiles;
+
        /* Voice preference */
        public static boolean voice;
 
@@ -151,6 +157,10 @@ public class AltosPreferences {
        public static int launcher_channel;
 
        public static void init(AltosPreferencesBackend in_backend) {
+
+               if (backend != null)
+                       return;
+
                backend = in_backend;
 
                /* Initialize logdir from preferences */
@@ -172,6 +182,8 @@ public class AltosPreferences {
 
                telemetry_rates = new Hashtable<Integer,Integer>();
 
+               logfiles = new Hashtable<Integer,File>();
+
                voice = backend.getBoolean(voicePreference, true);
 
                callsign = backend.getString(callsignPreference,"N0CALL");
@@ -300,6 +312,27 @@ public class AltosPreferences {
                }
        }
 
+       public static void set_logfile(int serial, File new_logfile) {
+               synchronized(backend) {
+                       logfiles.put(serial, new_logfile);
+                       backend.putString(String.format(logfilePreferenceFormat, serial), new_logfile.getPath());
+                       flush_preferences();
+               }
+       }
+
+       public static File logfile(int serial) {
+               synchronized(backend) {
+                       if (logfiles.containsKey(serial))
+                               return logfiles.get(serial);
+                       String logfile_string = backend.getString(String.format(logfilePreferenceFormat, serial), null);
+                       if (logfile_string == null)
+                               return null;
+                       File logfile = new File(logfile_string);
+                       logfiles.put(serial, logfile);
+                       return logfile;
+               }
+       }
+
        public static void set_scanning_telemetry(int new_scanning_telemetry) {
                synchronized (backend) {
                        scanning_telemetry = new_scanning_telemetry;
index 15093af18410a1180e4c8c3bf8a66dee5718ba4d..2864e02a272bb0ba60f27407b0fdf03d27803c0d 100644 (file)
@@ -27,6 +27,7 @@ import java.util.*;
 public class AltosReplayReader extends AltosFlightReader {
        Iterator<AltosState>    iterator;
        File    file;
+       boolean real_time;
 
        public AltosState read() {
                if (iterator.hasNext())
@@ -39,16 +40,22 @@ public class AltosReplayReader extends AltosFlightReader {
 
        public void update(AltosState state) throws InterruptedException {
                /* Make it run in realtime after the rocket leaves the pad */
-               if (state.state > AltosLib.ao_flight_pad && state.time_change > 0)
+               if (real_time && state.state > AltosLib.ao_flight_pad && state.time_change > 0)
                        Thread.sleep((int) (Math.min(state.time_change,10) * 1000));
                state.set_received_time(System.currentTimeMillis());
        }
 
        public File backing_file() { return file; }
 
-       public AltosReplayReader(Iterator<AltosState> in_iterator, File in_file) {
+       public AltosReplayReader(Iterator<AltosState> in_iterator, File in_file,
+                                boolean in_real_time) {
                iterator = in_iterator;
                file = in_file;
+               real_time = in_real_time;
                name = file.getName();
        }
+
+       public AltosReplayReader(Iterator<AltosState> in_iterator, File in_file) {
+               this(in_iterator, in_file, false);
+       }
 }
index f7cd424d554a82422b8f403775f178ad717e3b84..4154b71ceec90a8d77473e0432977b0c47ae6f32 100644 (file)
@@ -26,4 +26,18 @@ public abstract class AltosStateIterable implements Iterable<AltosState> {
        }
 
        public abstract void write(PrintStream out);
+
+       public static AltosStateIterable iterable(File file) {
+               FileInputStream in;
+               try {
+                       in = new FileInputStream(file);
+               } catch (Exception e) {
+                       System.out.printf("Failed to open file '%s'\n", file);
+                       return null;
+               }
+               if (file.getName().endsWith("telem"))
+                       return new AltosTelemetryFile(in);
+               else
+                       return new AltosEepromFile(in);
+       }
 }
index 8b1869bb65f3141fdc83a74496b97fdaa3fb0995..d949c02f3bc233fcc937e17150841f5184daf495 100644 (file)
@@ -36,7 +36,7 @@ public class AltosTelemetryMegaData extends AltosTelemetryStandard {
        public AltosTelemetryMegaData(int[] bytes) {
                super(bytes);
 
-               state = int8(5);
+               state = uint8(5);
 
                v_batt = int16(6);
                v_pyro = int16(8);
@@ -44,7 +44,7 @@ public class AltosTelemetryMegaData extends AltosTelemetryStandard {
                sense = new int[6];
 
                for (int i = 0; i < 6; i++) {
-                       sense[i] = int8(10 + i) << 4;
+                       sense[i] = uint8(10 + i) << 4;
                        sense[i] |= sense[i] >> 8;
                }
 
index 5ed501348b07174bb0debee09d499ada26bffb56..7539452d4ad9cf3cb609a48b58292b197e128911 100644 (file)
@@ -28,10 +28,17 @@ public class AltosTelemetryReader extends AltosFlightReader {
        int             telemetry;
        int             telemetry_rate;
        AltosState      state = null;
+       AltosFlightReader       stacked;
 
        LinkedBlockingQueue<AltosLine> telem;
 
        public AltosState read() throws InterruptedException, ParseException, AltosCRCException, IOException {
+               if (stacked != null) {
+                       state = stacked.read();
+                       if (state != null)
+                               return state;
+                       stacked = null;
+               }
                AltosLine l = telem.take();
                if (l.line == null)
                        throw new IOException("IO error");
@@ -53,6 +60,12 @@ public class AltosTelemetryReader extends AltosFlightReader {
        }
 
        public void close(boolean interrupted) {
+
+               if (stacked != null) {
+                       stacked.close(interrupted);
+                       stacked = null;
+               }
+
                link.remove_monitor(telem);
                log.close();
                try {
@@ -148,9 +161,10 @@ public class AltosTelemetryReader extends AltosFlightReader {
                return link.monitor_battery();
        }
 
-       public AltosTelemetryReader (AltosLink in_link)
+       public AltosTelemetryReader (AltosLink in_link, AltosFlightReader in_stacked)
                throws IOException, InterruptedException, TimeoutException {
                link = in_link;
+               stacked = in_stacked;
                boolean success = false;
                try {
                        log = new AltosLog(link);
@@ -169,4 +183,22 @@ public class AltosTelemetryReader extends AltosFlightReader {
                                close(true);
                }
        }
+
+       private static AltosFlightReader existing_data(AltosLink link) {
+               if (link == null)
+                       return null;
+
+               File    file = AltosPreferences.logfile(link.serial);
+               if (file != null) {
+                       AltosStateIterable      iterable = AltosStateIterable.iterable(file);
+                       if (iterable != null)
+                               return new AltosReplayReader(iterable.iterator(), file, false);
+               }
+               return null;
+       }
+
+       public AltosTelemetryReader(AltosLink link)
+               throws IOException, InterruptedException, TimeoutException {
+               this(link, existing_data(link));
+       }
 }
index dd4fb5055a92f353ae321e1b51928803f253f426..64336c8e06daa1ac0b5d72fedab5960ddd383762 100644 (file)
@@ -285,9 +285,13 @@ public class AltosConfigPyroUI
                "0.050", "0.100", "0.250", "0.500", "1.0", "2.0"
        };
 
+       boolean initializing;
+
        public void set_pyro_firing_time(double new_pyro_firing_time) {
+               initializing = true;
                pyro_firing_time_value.setSelectedItem(Double.toString(new_pyro_firing_time));
                pyro_firing_time_value.setEnabled(new_pyro_firing_time >= 0);
+               initializing = false;
        }
 
        public double get_pyro_firing_time() throws AltosConfigDataException {
@@ -301,23 +305,28 @@ public class AltosConfigPyroUI
        }
 
        public void set_dirty() {
-               owner.set_dirty();
+               if (!initializing)
+                       owner.set_dirty();
        }
 
        public void itemStateChanged(ItemEvent e) {
-               owner.set_dirty();
+               if (!initializing)
+                       owner.set_dirty();
        }
 
        public void changedUpdate(DocumentEvent e) {
-               owner.set_dirty();
+               if (!initializing)
+                       owner.set_dirty();
        }
 
        public void insertUpdate(DocumentEvent e) {
-               owner.set_dirty();
+               if (!initializing)
+                       owner.set_dirty();
        }
 
        public void removeUpdate(DocumentEvent e) {
-               owner.set_dirty();
+               if (!initializing)
+                       owner.set_dirty();
        }
 
        public void units_changed(boolean imperial_units) {
index 4389e49b0c57ac431512a5544b3243441390afa0..9020ed9ddc874f0d652c39aa491a74b641c2ceb2 100644 (file)
@@ -223,8 +223,10 @@ public class AltosConfigTD implements ActionListener {
                                        if (!config_version.get().equals("0.0"))
                                                break;
                                        been_there = true;
-                                       config.serial_line.printf("C\n ");
-                                       config.serial_line.flush_input();
+                                       if (config != null && config.serial_line != null) {
+                                               config.serial_line.printf("C\n ");
+                                               config.serial_line.flush_input();
+                                       }
                                }
                        } catch (InterruptedException ie) {
                        }
@@ -277,8 +279,10 @@ public class AltosConfigTD implements ActionListener {
        }
 
        void abort() {
-               serial_line.close();
-               serial_line = null;
+               if (serial_line != null) {
+                       serial_line.close();
+                       serial_line = null;
+               }
                JOptionPane.showMessageDialog(owner,
                                              String.format("Connection to \"%s\" failed",
                                                            device.toShortString()),
index 5c33fd16630fc03578c51e809345b93d7672e132..eb0c56448eb5a2bbd6a9ade8598797c7781ac7bc 100644 (file)
@@ -117,6 +117,17 @@ public class AltosPad extends AltosUIFlightTab {
                }
        }
 
+       boolean report_pad(AltosState state) {
+               if ((state.state == AltosLib.ao_flight_stateless ||
+                    state.state < AltosLib.ao_flight_pad) &&
+                   state.gps != null &&
+                   state.gps.lat != AltosLib.MISSING)
+               {
+                       return false;
+               }
+               return true;
+       }
+
        class PadLat extends AltosUIIndicator {
 
                double  last_lat = AltosLib.MISSING - 1;
@@ -126,12 +137,12 @@ public class AltosPad extends AltosUIFlightTab {
                        String label = null;
 
                        if (state != null) {
-                               if (state.state < AltosLib.ao_flight_pad && state.gps != null && state.gps.lat != AltosLib.MISSING) {
-                                       lat = state.gps.lat;
-                                       label = "Latitude";
-                               } else {
+                               if (report_pad(state)) {
                                        lat = state.pad_lat;
                                        label = "Pad Latitude";
+                               } else {
+                                       lat = state.gps.lat;
+                                       label = "Latitude";
                                }
                        }
                        if (lat != last_lat) {
@@ -163,12 +174,12 @@ public class AltosPad extends AltosUIFlightTab {
                        String label = null;
 
                        if (state != null) {
-                               if (state.state < AltosLib.ao_flight_pad && state.gps != null && state.gps.lon != AltosLib.MISSING) {
-                                       lon = state.gps.lon;
-                                       label = "Longitude";
-                               } else {
+                               if (report_pad(state)) {
                                        lon = state.pad_lon;
                                        label = "Pad Longitude";
+                               } else {
+                                       lon = state.gps.lon;
+                                       label = "Longitude";
                                }
                        }
                        if (lon != last_lon) {
@@ -200,12 +211,12 @@ public class AltosPad extends AltosUIFlightTab {
                        String label = null;
 
                        if (state != null) {
-                               if (state.state < AltosLib.ao_flight_pad && state.gps != null && state.gps.alt != AltosLib.MISSING) {
-                                       alt = state.gps.alt;
-                                       label = "Altitude";
-                               } else {
+                               if (report_pad(state)) {
                                        alt = state.pad_alt;
                                        label = "Pad Altitude";
+                               } else {
+                                       alt = state.gps.alt;
+                                       label = "Altitude";
                                }
                        }
                        if (alt != last_alt) {
diff --git a/ao-bringup/test-easymega b/ao-bringup/test-easymega
new file mode 100755 (executable)
index 0000000..eabe1ee
--- /dev/null
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+VERSION=1.0
+PRODUCT=EasyMega
+BASE=`echo $PRODUCT | tr 'A-Z' 'a-z'`
+
+echo "$PRODUCT-v$VERSION Test Program"
+echo "Copyright 2014 by Keith Packard.  Released under GPL v2"
+echo
+echo "Expectations:"
+echo "\t$PRODUCT v$VERSION powered from USB"
+echo
+
+ret=1
+ao-list | while read product serial dev; do
+    case "$product" in
+       "$PRODUCT-v$VERSION")
+
+           echo "Testing $product $serial $dev"
+
+           for igniter in drogue main 0 1 2 3; do
+               echo "Testing $igniter igniter."
+               echo -n "Press enter to continue..."
+               read foo < /dev/tty
+               ../ao-tools/ao-test-igniter/ao-test-igniter --tty="$dev" $igniter
+
+               case $? in
+                   0)
+                       ;;
+                   *)
+                       echo "failed"
+                       exit 1
+                       ;;
+               esac
+           done
+
+           echo "Testing baro sensor"
+           ../ao-tools/ao-test-baro/ao-test-baro --tty="$dev"
+
+           case $? in
+               0)
+                   ;;
+               *)
+                   echo "failed"
+                   exit 1
+           esac
+
+           FLASHSIZE=8388608
+
+           echo "Testing flash"
+           ../ao-tools/ao-test-flash/ao-test-flash --tty="$dev" "$FLASHSIZE"
+
+           case $? in
+               0)
+                   ;;
+               *)
+                   echo "failed"
+                   exit 1
+           esac
+
+           echo "$PRODUCT-v$VERSION" serial "$serial" is ready to ship
+           ret=0
+           ;;
+       *)
+           echo "Skipping $product $serial $dev"
+           ;;
+    esac
+done
index 7d23f5f7d9a9f7683a86786d7d9f5233a071a19f..b313e16286dd1ee2c83977bdd4535ec031b6c855 100755 (executable)
@@ -52,6 +52,10 @@ esac
 
 echo 'E 0' > $dev
 
-./cal-accel $dev
+../ao-tools/ao-cal-accel/ao-cal-accel $dev
 
 echo 'E 1' > $dev
+
+./test-easymega
+
+exit $?
index 46c254b6477bf48c2d50004237ac2b2853756875..7745a8e560abcddf71bddf50fc8281c0db75965d 100755 (executable)
@@ -54,6 +54,6 @@ echo 'E 0' > $dev
 
 SERIAL=$SERIAL ./cal-freq $dev
 
-./cal-accel $dev
+../ao-tools/ao-cal-accel/ao-cal-accel $dev
 
 echo 'E 1' > $dev
index c6e7d1cccb1799b509faf9a216792f0cba7e1d76..48ff1e276cb6083aeed292821b6e8469b186675f 100755 (executable)
@@ -20,7 +20,7 @@ echo "TeleMetrum v$VERSION Turn-On and Calibration Program"
 echo "Copyright 2014 by Bdale Garbee.  Released under GPL v2"
 echo
 echo "Expectations:"
-echo "\tTeleMetrum v$VERSIOn powered from USB"
+echo "\tTeleMetrum v$VERSION powered from USB"
 echo "\t\twith ST-Link-V2 cabled to debug header"
 echo "\t\twith coax from UHF to frequency counter"
 echo
@@ -53,6 +53,6 @@ echo 'E 0' > $dev
 
 SERIAL=$SERIAL ./cal-freq $dev
 
-./cal-accel $dev
+../ao-tools/ao-cal-accel/ao-cal-accel $dev
 
 echo 'E 1' > $dev
index 4526f5145c4d089c6de0ae3f0af749d449acf2a7..2d873e5049f97c469b1a977991a7d0fb95d98f1a 100644 (file)
@@ -2,7 +2,7 @@ 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-test-flash ao-cal-accel
 if LIBSTLINK
 SUBDIRS += ao-stmload
 endif
diff --git a/ao-tools/ao-cal-accel/.gitignore b/ao-tools/ao-cal-accel/.gitignore
new file mode 100644 (file)
index 0000000..73402b2
--- /dev/null
@@ -0,0 +1 @@
+ao-cal-accel
diff --git a/ao-tools/ao-cal-accel/Makefile.am b/ao-tools/ao-cal-accel/Makefile.am
new file mode 100644 (file)
index 0000000..d278097
--- /dev/null
@@ -0,0 +1,11 @@
+bin_PROGRAMS=ao-cal-accel
+
+AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS)
+
+ao_cal_accel_DEPENDENCIES = $(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_cal_accel_LDADD=$(top_builddir)/ao-tools/lib/libao-tools.a $(LIBUSB_LIBS)
+
+ao_cal_accel_SOURCES=ao-cal-accel.c
+
+man_MANS = ao-cal-accel.1
diff --git a/ao-tools/ao-cal-accel/ao-cal-accel.1 b/ao-tools/ao-cal-accel/ao-cal-accel.1
new file mode 100644 (file)
index 0000000..eb75d7c
--- /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-accel" ""
+.SH NAME
+ao-cal-accel \- Calibrate AltOS flight computer accelerometers
+.SH SYNOPSIS
+.B "ao-cal-accel"
+[\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
+.SH DESCRIPTION
+.I ao-cal-accel
+drives the built-in accelerometer calibration and validates the results.
+.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-accel
+opens the target device, executes the accelerometer calibration
+command, verifies that it executed correctly, then shows the resulting
+calibration values and saves them to configuration memory.
+.SH AUTHOR
+Keith Packard
diff --git a/ao-tools/ao-cal-accel/ao-cal-accel.c b/ao-tools/ao-cal-accel/ao-cal-accel.c
new file mode 100644 (file)
index 0000000..9a98864
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * 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 "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    **accel;
+       char    line[1024];
+       int     l;
+       int     running = 0;
+       int     worked = 1;
+
+       cc_usb_printf(usb, "E 1\nc a 0\n");
+
+       for (;;) {
+               int     c = cc_usb_getchar_timeout(usb, 20*1000);
+
+               if (c == '\n')
+                       l = 0;
+               else if (l < sizeof (line) - 1)
+                       line[l++] = c;
+               line[l] = '\0';
+               putchar(c); fflush(stdout);
+               if (strstr(line, "press a key...")) {
+                       await_key();
+                       cc_usb_printf(usb, " ");
+                       l = 0;
+                       running = 1;
+               }
+               else if (strstr(line, "Invalid"))
+                       worked = 0;
+               if (running && strstr(line, ">")) {
+                       printf("\n");
+                       break;
+               }
+       }
+       cc_usb_printf(usb, "E 0\n");
+
+       if (!worked) {
+               printf("Calibration failed\n");
+               return 0;
+       }
+
+       b = flash(usb);
+
+       accel = find_flash(b, "Accel cal");
+       if (!accel) {
+               printf("no response\n");
+               return 0;
+       }
+
+       printf ("Accel cal +1g: %s -1g: %s\n",
+               accel[3], accel[5]);
+
+       printf ("Saving..."); fflush(stdout);
+       cc_usb_printf (usb, "c w\n");
+       cc_usb_sync(usb);
+       b = flash(usb);
+       printf ("done\n");
+
+       return worked;
+}
+
+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 38bab8e12126dfc5bd38ad5e13b4f7eb173270b2..683da846f90fdae6b7fe47d26abf11d2798a0de0 100644 (file)
@@ -537,6 +537,7 @@ ao-tools/ao-flash/Makefile
 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-utils/Makefile
 src/Version
 ])
index 144cbd70a8834e4053e67c6bebec423e68ae8175..dc2c83fe0cf3c668668dfbf354cbae020d14533f 100644 (file)
@@ -362,14 +362,26 @@ ao_pad_test(void)
 void
 ao_pad_manual(void)
 {
+       uint8_t ignite;
+       int     repeat;
        ao_cmd_white();
        if (!ao_match_word("DoIt"))
                return;
        ao_cmd_decimal();
        if (ao_cmd_status != ao_cmd_success)
                return;
-       ao_pad_ignite = 1 << ao_cmd_lex_i;
-       ao_wakeup(&ao_pad_ignite);
+       ignite = 1 << ao_cmd_lex_i;
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success) {
+               repeat = 1;
+               ao_cmd_status = ao_cmd_success;
+       } else
+               repeat = ao_cmd_lex_i;
+       while (repeat-- > 0) {
+               ao_pad_ignite = ignite;
+               ao_wakeup(&ao_pad_ignite);
+               ao_delay(AO_PAD_FIRE_TIME>>1);
+       }
 }
 
 static __xdata struct ao_task ao_pad_task;
index dc3b6486caabea5355753cea8e446fd1eb213dac..40a96ef7f259fa4811d6faa11c9567fbd323b6ec 100644 (file)
@@ -192,12 +192,14 @@ ao_log_find_max_erase_flight(void) __reentrant
                ao_flight_number = 1;
 }
 
-void
+uint8_t
 ao_log_scan(void) __reentrant
 {
        uint8_t         log_slot;
        uint8_t         log_slots;
-#if !FLIGHT_LOG_APPEND
+#if FLIGHT_LOG_APPEND
+       uint8_t         ret;
+#else
        uint8_t         log_want;
 #endif
 
@@ -248,9 +250,13 @@ ao_log_scan(void) __reentrant
                                empty = ao_log_current_pos;
                        }
                }
+               ret = 1;
        } else {
                ao_log_find_max_erase_flight();
+               ret = 0;
        }
+       ao_wakeup(&ao_flight_number);
+       return ret;
 #else
 
        if (ao_flight_number)
@@ -278,8 +284,9 @@ ao_log_scan(void) __reentrant
                if (++log_slot >= log_slots)
                        log_slot = 0;
        } while (log_slot != log_want);
-#endif
        ao_wakeup(&ao_flight_number);
+       return 0;
+#endif
 }
 
 void
index c5fa7faba6d867bee81ab990fcdf35eafa0d69c1..c13a2580f71e3bdae2af7be7d978b71d6d921eff 100644 (file)
@@ -72,7 +72,7 @@ ao_log(void);
 /* functions provided in ao_log.c */
 
 /* Figure out the current flight number */
-void
+uint8_t
 ao_log_scan(void) __reentrant;
 
 /* Return the position of the start of the given log slot */
index 0b286466fa3a7495d91406bedb91c1ebbe6cd3e9..3044d56517aa06fceaf198fb31a4165e5d6b9370 100644 (file)
@@ -252,7 +252,7 @@ ao_pyro_check(void)
        struct ao_pyro  *pyro;
        uint8_t         p, any_waiting;
        uint16_t        fire = 0;
-       
+
        any_waiting = 0;
        for (p = 0; p < AO_PYRO_NUM; p++) {
                pyro = &ao_config.pyro[p];
@@ -288,6 +288,16 @@ ao_pyro_check(void)
                 * the delay to expire
                 */
                if (pyro->delay_done) {
+
+                       /* Check to make sure the required conditions
+                        * remain valid. If not, inhibit the channel
+                        * by setting the fired bit
+                        */
+                       if (!ao_pyro_ready(pyro)) {
+                               pyro->fired = 1;
+                               continue;
+                       }
+
                        if ((int16_t) (ao_time() - pyro->delay_done) < 0)
                                continue;
                }
@@ -465,7 +475,7 @@ ao_pyro_set(void)
                printf ("invalid pyro channel %d\n", p);
                return;
        }
-       pyro_tmp.flags = 0;
+       memset(&pyro_tmp, '\0', sizeof (pyro_tmp));
        for (;;) {
                ao_cmd_white();
                if (ao_cmd_lex_c == '\n')
@@ -489,13 +499,26 @@ ao_pyro_set(void)
                }
                pyro_tmp.flags |= ao_pyro_values[v].flag;
                if (ao_pyro_values[v].offset != NO_VALUE) {
+                       uint8_t negative = 0;
+                       ao_cmd_white();
+                       if (ao_cmd_lex_c == '-') {
+                               negative = 1;
+                               ao_cmd_lex();
+                       }
                        ao_cmd_decimal();
                        if (ao_cmd_status != ao_cmd_success)
                                return;
-                       if (ao_pyro_values[v].flag & AO_PYRO_8_BIT_VALUE)
+                       if (ao_pyro_values[v].flag & AO_PYRO_8_BIT_VALUE) {
+                               if (negative) {
+                                       ao_cmd_status = ao_cmd_syntax_error;
+                                       return;
+                               }
                                *((uint8_t *) ((char *) &pyro_tmp + ao_pyro_values[v].offset)) = ao_cmd_lex_i;
-                       else
+                       } else {
+                               if (negative)
+                                       ao_cmd_lex_i = -ao_cmd_lex_i;
                                *((int16_t *) ((char *) &pyro_tmp + ao_pyro_values[v].offset)) = ao_cmd_lex_i;
+                       }
                }
        }
        _ao_config_edit_start();
index d94340484a8221248d89068bad9320c141e3fbf0..9b007af82109defdc752e56cfcd375a374c5b1e6 100644 (file)
@@ -72,7 +72,7 @@ ao_tracker(void)
 #if !HAS_USB_CONNECT
        ao_tracker_force_telem = 1;
 #endif
-       ao_log_scan();
+       log_started = ao_log_scan();
 
        ao_rdf_set(1);
 
@@ -181,8 +181,7 @@ void
 ao_tracker_erase_end(void)
 {
        if (erasing_current) {
-               ao_log_scan();
-               log_started = 0;
+               log_started = ao_log_scan();
                ao_mutex_put(&tracker_mutex);
        }
 }