Merge branch 'master' into stm-flash
authorKeith Packard <keithp@keithp.com>
Mon, 22 Apr 2013 22:20:51 +0000 (17:20 -0500)
committerKeith Packard <keithp@keithp.com>
Mon, 22 Apr 2013 22:20:51 +0000 (17:20 -0500)
Conflicts:
ao-tools/ao-stmload/ao-stmload.c

160 files changed:
.gitignore
altosdroid/AndroidManifest.xml
altosdroid/Notebook [new file with mode: 0644]
altosdroid/res/layout/altosdroid.xml
altosdroid/res/layout/tab_ascent.xml
altosdroid/res/layout/tab_descent.xml
altosdroid/res/layout/tab_landed.xml
altosdroid/res/layout/tab_map.xml
altosdroid/res/layout/tab_pad.xml
altosdroid/res/values/strings.xml
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java
altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.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
altoslib/AltosConfigData.java
altoslib/AltosEepromIterable.java
altoslib/AltosFlightReader.java
altoslib/AltosGPS.java
altoslib/AltosGreatCircle.java
altoslib/AltosIdleMonitor.java
altoslib/AltosIdleMonitorListener.java
altoslib/AltosLib.java
altoslib/AltosLink.java
altoslib/AltosListenerState.java [new file with mode: 0644]
altoslib/AltosRecord.java
altoslib/AltosSensorMM.java
altoslib/AltosSensorTM.java
altoslib/AltosState.java
altoslib/AltosTelemetryReader.java
altoslib/AltosTelemetryRecord.java
altoslib/AltosTelemetryRecordCompanion.java
altoslib/AltosTelemetryRecordConfiguration.java
altoslib/AltosTelemetryRecordLocation.java
altoslib/AltosTelemetryRecordMegaData.java
altoslib/AltosTelemetryRecordMegaSensor.java
altoslib/AltosTelemetryRecordRaw.java
altoslib/AltosTelemetryRecordSatellite.java
altoslib/AltosTelemetryRecordSensor.java
altoslib/Makefile.am
altosui/AltosAscent.java
altosui/AltosCompanionInfo.java
altosui/AltosConfigUI.java
altosui/AltosDataChooser.java
altosui/AltosDescent.java
altosui/AltosDisplayThread.java
altosui/AltosEepromDownload.java
altosui/AltosFlightDisplay.java
altosui/AltosFlightStats.java
altosui/AltosFlightStatus.java
altosui/AltosFlightStatusUpdate.java
altosui/AltosFlightUI.java
altosui/AltosGraphUI.java
altosui/AltosIdleMonitorUI.java
altosui/AltosInfoTable.java
altosui/AltosLanded.java
altosui/AltosPad.java
altosui/AltosSiteMap.java
altosui/AltosSiteMapCache.java
altosui/AltosSiteMapTile.java
altosui/Makefile.am
altosui/altos-windows.nsi.in
altosuilib/AltosUSBDevice.java
ao-bringup/megametrum.cfg [deleted file]
ao-bringup/megametrum.gdb [deleted file]
ao-bringup/telemega.cfg [new file with mode: 0644]
ao-bringup/telemega.gdb [new file with mode: 0644]
ao-bringup/turnon_telebt
ao-tools/Makefile.am
ao-tools/ao-dump-up/Makefile.am [new file with mode: 0644]
ao-tools/ao-dump-up/ao-dump-up.1 [new file with mode: 0644]
ao-tools/ao-dump-up/ao-dump-up.c [new file with mode: 0644]
ao-tools/ao-telem/ao-telem.c
ao-tools/lib/cc-usb.c
ao-tools/lib/cc-usb.h
ao-tools/lib/cc-usbdev.c
configure.ac
debian/docs
doc/altos.xsl
doc/altusmetrum.xsl
doc/megametrum-outline.pdf [deleted file]
doc/megametrum-outline.svg [deleted file]
doc/micropeak.xsl
doc/release-notes-1.2.xsl
doc/telemega-outline.pdf [new file with mode: 0644]
doc/telemega-outline.svg [new file with mode: 0644]
libaltos/libaltos.c
micropeak/MicroDownload.java
src/Makefile
src/cc1111/ao_adc.c
src/core/ao.h
src/core/ao_config.c
src/core/ao_fec_rx.c
src/core/ao_gps_report_mega.c
src/core/ao_log.h
src/core/ao_log_mega.c
src/core/ao_telemetry.c
src/drivers/ao_aprs.c
src/drivers/ao_btm.c
src/drivers/ao_bufio.c [new file with mode: 0644]
src/drivers/ao_bufio.h [new file with mode: 0644]
src/drivers/ao_cc1120.c
src/drivers/ao_cc115l.c [new file with mode: 0644]
src/drivers/ao_cc115l.h [new file with mode: 0644]
src/drivers/ao_companion.c
src/drivers/ao_fat.c [new file with mode: 0644]
src/drivers/ao_fat.h [new file with mode: 0644]
src/drivers/ao_log_fat.c [new file with mode: 0644]
src/drivers/ao_rfpa0133.c [new file with mode: 0644]
src/drivers/ao_rfpa0133.h [new file with mode: 0644]
src/drivers/ao_sdcard.c [new file with mode: 0644]
src/drivers/ao_sdcard.h [new file with mode: 0644]
src/megametrum-v0.1/.gitignore [deleted file]
src/megametrum-v0.1/Makefile [deleted file]
src/megametrum-v0.1/ao_megametrum.c [deleted file]
src/megametrum-v0.1/ao_pins.h [deleted file]
src/megametrum-v0.1/stlink-pins [deleted file]
src/product/Makefile.telebt [deleted file]
src/product/ao_telebt.c [deleted file]
src/stm-bringup/Makefile
src/stm/Makefile.defs
src/stm/ao_eeprom_stm.c
src/stm/ao_i2c_stm.c
src/stm/ao_spi_stm.c
src/stm/ao_timer.c
src/telebt-v0.0/.gitignore [deleted file]
src/telebt-v0.0/.sdcdbrc [deleted file]
src/telebt-v0.0/Makefile [deleted file]
src/telebt-v0.1/.gitignore [deleted file]
src/telebt-v0.1/.sdcdbrc [deleted file]
src/telebt-v0.1/Makefile [deleted file]
src/telebt-v1.0/.gitignore [new file with mode: 0644]
src/telebt-v1.0/.sdcdbrc [new file with mode: 0644]
src/telebt-v1.0/Makefile [new file with mode: 0644]
src/telebt-v1.0/ao_pins.h [new file with mode: 0644]
src/telebt-v1.0/ao_telebt.c [new file with mode: 0644]
src/telegps-v0.1/.gitignore [new file with mode: 0644]
src/telegps-v0.1/Makefile [new file with mode: 0644]
src/telegps-v0.1/ao_pins.h [new file with mode: 0644]
src/telegps-v0.1/ao_telegps.c [new file with mode: 0644]
src/telelco-v0.1/Makefile
src/telemega-v0.1/.gitignore [new file with mode: 0644]
src/telemega-v0.1/Makefile [new file with mode: 0644]
src/telemega-v0.1/ao_pins.h [new file with mode: 0644]
src/telemega-v0.1/ao_telemega.c [new file with mode: 0644]
src/telemega-v0.1/stlink-pins [new file with mode: 0644]
src/teleshield-v0.1/ao_pins.h
src/test/.gitignore
src/test/Makefile
src/test/ao_aprs_test.c
src/test/ao_fat_test.c [new file with mode: 0644]
src/test/ao_flight_test.c
telemetrum.inf

index 6ae2b864a6c0b7c74541ee0ce92ee050f663da08..5dec09a5479055ebed7232343b0c1454fa57f5d6 100644 (file)
@@ -29,6 +29,7 @@ ao-tidongle.h
 ao-tools/ao-bitbang/ao-bitbang
 ao-tools/ao-dbg/ao-dbg
 ao-tools/ao-dumplog/ao-dumplog
+ao-tools/ao-dump-up/ao-dump-up
 ao-tools/ao-eeprom/ao-eeprom
 ao-tools/ao-edit-telem/ao-edit-telem
 ao-tools/ao-list/ao-list
index 237c6dcf40e1dbf01498337c169709233cbd8cff..04a679e1574823cb1e8b02eab7f604af282f8969 100644 (file)
@@ -17,8 +17,8 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="org.altusmetrum.AltosDroid"
-          android:versionCode="1"
-          android:versionName="1.1.9.3">
+          android:versionCode="3"
+          android:versionName="1.2">
     <uses-sdk android:targetSdkVersion="10" android:minSdkVersion="10"/>
     <!-- Google Maps -->
     <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
@@ -31,7 +31,6 @@
     <!-- Permissions needed for GoogleMaps -->
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
-    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
 
     <permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"
diff --git a/altosdroid/Notebook b/altosdroid/Notebook
new file mode 100644 (file)
index 0000000..912a835
--- /dev/null
@@ -0,0 +1,22 @@
+Desired AltosDroid feature list
+
+ *) GPS satellite status tab. Monitor GPS C/N0 numbers and
+    SVIDs. Provides more info before GPS lock is acquired.
+
+ *) Channel scanning. Provide the ability to search for telemetry
+    signals like AltosUI does.
+
+ *) Highlight current frequency in the frequency list.
+
+ *) 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,
+    or perhaps something better.
+  
+ *) TM configuration from the droid
+
+ *) Monitor-idle mode
+
+ *) Frequency scanning
+
+ *) Select satellite imaging mode
index 364f6ba6d62677b370c0b1eef289ee8f949064b8..71b7ad054bc95dd05dfaa7e6e29572656554f38b 100644 (file)
@@ -47,7 +47,7 @@
                                android:layout_height="wrap_content"
                                android:layout_below="@+id/callsign_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <RelativeLayout
@@ -67,7 +67,7 @@
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_below="@+id/serial_label"
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <RelativeLayout
@@ -87,7 +87,7 @@
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_below="@+id/flight_label"
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <RelativeLayout
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_below="@+id/state_label"
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <RelativeLayout
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_below="@+id/rssi_label"
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <RelativeLayout
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_below="@+id/age_label"
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
        </LinearLayout>
 
index b7e97086ce6e4f51caf39e8926570e0ed0edb121..b21ec426a73ccc71184b25e5cedf3018fa2832e0 100644 (file)
@@ -47,7 +47,7 @@
                                android:layout_alignParentRight="true"
                                android:layout_below="@id/height_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <RelativeLayout
@@ -68,7 +68,7 @@
                                android:layout_alignParentRight="true"
                                android:layout_below="@id/max_height_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
        </LinearLayout>
 
@@ -99,7 +99,7 @@
                                android:layout_alignParentRight="true"
                                android:layout_below="@id/speed_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <RelativeLayout
                                android:layout_alignParentRight="true"
                                android:layout_below="@id/max_speed_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
        </LinearLayout>
 
                                android:layout_alignParentRight="true"
                                android:layout_below="@id/accel_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <RelativeLayout
                                android:layout_alignParentRight="true"
                                android:layout_below="@id/max_accel_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
        </LinearLayout>
 
                        android:layout_alignParentRight="true"
                        android:layout_below="@id/lat_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                        android:layout_alignParentRight="true"
                        android:layout_below="@id/lon_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:contentDescription="@string/apogee_voltage_label"
-                       android:src="@drawable/redled" />
+                       android:src="@drawable/grayled" />
 
                <ImageView
                        android:id="@+id/apogee_greenled"
                        android:layout_below="@id/apogee_voltage_label"
                        android:layout_toRightOf="@id/apogee_greenled"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:contentDescription="@string/main_voltage_label"
-                       android:src="@drawable/redled" />
+                       android:src="@drawable/grayled" />
 
                <ImageView
                        android:id="@+id/main_greenled"
                        android:layout_below="@id/main_voltage_label"
                        android:layout_toRightOf="@id/main_greenled"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
 </LinearLayout>
\ No newline at end of file
index 4b62acf90b3a51ba77c4889c095ad4a3f960ec0c..9e1fc820f5150fd90c9740e4222b5c6243c8f86b 100644 (file)
@@ -46,7 +46,7 @@
                                android:layout_alignParentRight="true"
                                android:layout_below="@id/speed_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <RelativeLayout
@@ -67,7 +67,7 @@
                                android:layout_alignParentRight="true"
                                android:layout_below="@id/height_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
        </LinearLayout>
@@ -99,7 +99,7 @@
                                android:layout_alignParentRight="true"
                                android:layout_below="@id/elevation_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <RelativeLayout
                                android:layout_alignParentRight="true"
                                android:layout_below="@id/range_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
        </LinearLayout>
                                android:layout_alignParentRight="true"
                                android:layout_below="@id/bearing_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <RelativeLayout
                                android:layout_alignParentRight="true"
                                android:layout_below="@id/compass_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
        </LinearLayout>
                                android:layout_alignParentRight="true"
                                android:layout_below="@id/distance_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <TextView
                        android:layout_alignParentRight="true"
                        android:layout_below="@id/lat_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                        android:layout_alignParentRight="true"
                        android:layout_below="@id/lon_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:contentDescription="@string/apogee_voltage_label"
-                       android:src="@drawable/redled" />
+                       android:src="@drawable/grayled" />
 
                <ImageView
                        android:id="@+id/apogee_greenled"
                        android:layout_below="@id/apogee_voltage_label"
                        android:layout_toRightOf="@id/apogee_greenled"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:contentDescription="@string/main_voltage_label"
-                       android:src="@drawable/redled" />
+                       android:src="@drawable/grayled" />
 
                <ImageView
                        android:id="@+id/main_greenled"
                        android:layout_below="@id/main_voltage_label"
                        android:layout_toRightOf="@id/main_greenled"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
 </LinearLayout>
index b5c8d8c21e97d537e53489b736f9ac07c2c48842..f27baa9e7b87477b2ec6235757b28a2de61bbd84 100644 (file)
@@ -37,7 +37,7 @@
                        android:layout_alignParentRight="true"
                        android:layout_below="@+id/bearing_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
@@ -58,7 +58,7 @@
                        android:layout_alignParentRight="true"
                        android:layout_below="@+id/distance_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                android:paddingTop="5dp" >
 
                <TextView
-                       android:id="@+id/lat_label"
+                       android:id="@+id/target_lat_label"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
-                       android:text="@string/latitude_label" />
+                       android:text="@string/target_latitude_label" />
 
                <TextView
-                       android:id="@+id/lat_value"
+                       android:id="@+id/target_lat_value"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentRight="true"
-                       android:layout_below="@id/lat_label"
+                       android:layout_below="@id/target_lat_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                android:paddingTop="5dp" >
 
                <TextView
-                       android:id="@+id/lon_label"
+                       android:id="@+id/target_lon_label"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
-                       android:text="@string/longitude_label" />
+                       android:text="@string/target_longitude_label" />
 
                <TextView
-                       android:id="@+id/lon_value"
+                       android:id="@+id/target_lon_value"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentRight="true"
-                       android:layout_below="@id/lon_label"
+                       android:layout_below="@id/target_lon_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
+       </RelativeLayout>
+
+       <RelativeLayout
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:paddingTop="5dp" >
+
+               <TextView
+                       android:id="@+id/receiver_lat_label"
+                       android:layout_width="wrap_content"
+                       android:layout_height="wrap_content"
+                       android:text="@string/receiver_latitude_label" />
+
+               <TextView
+                       android:id="@+id/receiver_lat_value"
+                       android:layout_width="wrap_content"
+                       android:layout_height="wrap_content"
+                       android:layout_alignParentRight="true"
+                       android:layout_below="@id/receiver_lat_label"
+                       android:text=""
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
+       </RelativeLayout>
+
+       <RelativeLayout
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:paddingTop="5dp" >
+
+               <TextView
+                       android:id="@+id/receiver_lon_label"
+                       android:layout_width="wrap_content"
+                       android:layout_height="wrap_content"
+                       android:text="@string/receiver_longitude_label" />
+
+               <TextView
+                       android:id="@+id/receiver_lon_value"
+                       android:layout_width="wrap_content"
+                       android:layout_height="wrap_content"
+                       android:layout_alignParentRight="true"
+                       android:layout_below="@id/receiver_lon_label"
+                       android:text=""
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                        android:layout_alignParentRight="true"
                        android:layout_below="@id/max_height_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                        android:layout_alignParentRight="true"
                        android:layout_below="@id/max_speed_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                        android:layout_alignParentRight="true"
                        android:layout_below="@id/max_accel_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
 </LinearLayout>
\ No newline at end of file
index b9f4e69e7267d3d48dea9fca2d307ac0bf77cddc..f611ae481b423bb821f364d351f6fcec98c994d8 100644 (file)
                                android:id="@+id/distance_label"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
+                               android:paddingRight="4dp"
                                android:text="@string/distance_label" />
 
                        <TextView
                                android:id="@+id/distance_value"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
-                               android:layout_alignParentRight="true"
-                               android:layout_below="@+id/distance_label"
+                               android:layout_toRightOf="@+id/distance_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <RelativeLayout
                                android:id="@+id/bearing_label"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
+                               android:paddingRight="4dp"
                                android:text="@string/bearing_label" />
 
                        <TextView
                                android:id="@+id/bearing_value"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
-                               android:layout_alignParentRight="true"
-                               android:layout_below="@+id/bearing_label"
+                               android:layout_toRightOf="@+id/bearing_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
        </LinearLayout>
                        android:paddingTop="5dp" >
 
                        <TextView
-                               android:id="@+id/lat_label"
+                               android:id="@+id/target_lat_label"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
-                               android:text="@string/latitude_label" />
+                               android:paddingRight="4dp"
+                               android:text="@string/target_latitude_label" />
 
                        <TextView
-                               android:id="@+id/lat_value"
+                               android:id="@+id/target_lat_value"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
-                               android:layout_alignParentRight="true"
-                               android:layout_below="@id/lat_label"
+                               android:layout_toRightOf="@id/target_lat_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
 
                <RelativeLayout
                        android:paddingTop="5dp" >
 
                        <TextView
-                               android:id="@+id/lon_label"
+                               android:id="@+id/target_lon_label"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
-                               android:text="@string/longitude_label" />
+                               android:paddingRight="4dp"
+                               android:text="@string/target_longitude_label" />
 
                        <TextView
-                               android:id="@+id/lon_value"
+                               android:id="@+id/target_lon_value"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
-                               android:layout_alignParentRight="true"
-                               android:layout_below="@id/lon_label"
+                               android:layout_toRightOf="@id/target_lon_label"
                                android:text=""
-                               android:textAppearance="?android:attr/textAppearanceLarge" />
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
+               </RelativeLayout>
+       </LinearLayout>
+
+       <LinearLayout
+               xmlns:android="http://schemas.android.com/apk/res/android"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:baselineAligned="true"
+               android:orientation="horizontal" >
+
+               <RelativeLayout
+                       android:layout_width="0dp"
+                       android:layout_height="wrap_content"
+                       android:layout_weight="1"
+                       android:paddingTop="5dp" >
+
+                       <TextView
+                               android:id="@+id/receiver_lat_label"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:paddingRight="4dp"
+                               android:text="@string/receiver_latitude_label" />
+
+                       <TextView
+                               android:id="@+id/receiver_lat_value"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:layout_toRightOf="@id/receiver_lat_label"
+                               android:text=""
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
+               </RelativeLayout>
+
+               <RelativeLayout
+                       android:layout_width="0dp"
+                       android:layout_height="wrap_content"
+                       android:layout_weight="1"
+                       android:paddingTop="5dp" >
+
+                       <TextView
+                               android:id="@+id/receiver_lon_label"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:paddingRight="4dp"
+                               android:text="@string/receiver_longitude_label" />
+
+                       <TextView
+                               android:id="@+id/receiver_lon_value"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:layout_toRightOf="@id/receiver_lon_label"
+                               android:text=""
+                               android:textAppearance="?android:attr/textAppearanceSmall" />
                </RelativeLayout>
        </LinearLayout>
 </LinearLayout>
\ No newline at end of file
index f5ec46b544d70b8a76547646db144afe54b925df..38e61f836c2cb097201782f32d1811c4dc083d4a 100644 (file)
@@ -30,7 +30,7 @@
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:contentDescription="@string/battery_voltage_label"
-                       android:src="@drawable/redled" />
+                       android:src="@drawable/grayled" />
 
                <ImageView
                        android:id="@+id/battery_greenled"
@@ -55,7 +55,7 @@
                        android:layout_below="@id/battery_voltage_label"
                        android:layout_toRightOf="@id/battery_greenled"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
@@ -68,7 +68,7 @@
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:contentDescription="@string/apogee_voltage_label"
-                       android:src="@drawable/redled" />
+                       android:src="@drawable/grayled" />
 
                <ImageView
                        android:id="@+id/apogee_greenled"
@@ -93,7 +93,7 @@
                        android:layout_below="@id/apogee_voltage_label"
                        android:layout_toRightOf="@id/apogee_greenled"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:contentDescription="@string/main_voltage_label"
-                       android:src="@drawable/redled" />
+                       android:src="@drawable/grayled" />
 
                <ImageView
                        android:id="@+id/main_greenled"
                        android:layout_below="@id/main_voltage_label"
                        android:layout_toRightOf="@id/main_greenled"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:contentDescription="@string/logging_label"
-                       android:src="@drawable/redled" />
+                       android:src="@drawable/grayled" />
 
                <ImageView
                        android:id="@+id/logging_greenled"
                        android:layout_below="@id/logging_label"
                        android:layout_toRightOf="@id/logging_greenled"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:contentDescription="@string/gps_locked_label"
-                       android:src="@drawable/redled" />
+                       android:src="@drawable/grayled" />
 
                <ImageView
                        android:id="@+id/gps_locked_greenled"
                        android:layout_below="@id/gps_locked_label"
                        android:layout_toRightOf="@id/gps_locked_greenled"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:contentDescription="@string/gps_ready_label"
-                       android:src="@drawable/redled" />
+                       android:src="@drawable/grayled" />
 
                <ImageView
                        android:id="@+id/gps_ready_greenled"
                        android:layout_below="@id/gps_ready_label"
                        android:layout_toRightOf="@id/gps_ready_greenled"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
-               android:paddingLeft="69dp"
-               android:paddingTop="5dp" >
+               android:paddingLeft="69dp">
 
                <TextView
                        android:id="@+id/pad_lat_label"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
+                       android:width="100sp"
+                       android:paddingRight="10sp"
                        android:layout_toRightOf="@id/gps_ready_greenled"
                        android:text="@string/pad_lat_label" />
 
                        android:id="@+id/pad_lat_value"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
-                       android:layout_below="@id/pad_lat_label"
-                       android:layout_toRightOf="@id/gps_ready_greenled"
+                       android:layout_toRightOf="@id/pad_lat_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
-               android:paddingLeft="69dp"
-               android:paddingTop="5dp" >
+               android:paddingLeft="69dp">
 
                <TextView
                        android:id="@+id/pad_lon_label"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
+                       android:width="100sp"
+                       android:paddingRight="10sp"
                        android:layout_toRightOf="@id/gps_ready_greenled"
                        android:text="@string/pad_lon_label" />
 
                        android:id="@+id/pad_lon_value"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
-                       android:layout_below="@id/pad_lon_label"
-                       android:layout_toRightOf="@id/gps_ready_greenled"
+                       android:layout_toRightOf="@id/pad_lon_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
        <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
-               android:paddingLeft="69dp"
-               android:paddingTop="5dp" >
+               android:paddingLeft="69dp">
 
                <TextView
                        android:id="@+id/pad_alt_label"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
+                       android:width="100sp"
+                       android:paddingRight="10sp"
                        android:layout_toRightOf="@id/gps_ready_greenled"
                        android:text="@string/pad_alt_label" />
 
                        android:id="@+id/pad_alt_value"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
-                       android:layout_below="@id/pad_alt_label"
-                       android:layout_toRightOf="@id/gps_ready_greenled"
+                       android:layout_toRightOf="@id/pad_alt_label"
                        android:text=""
-                       android:textAppearance="?android:attr/textAppearanceLarge" />
+                       android:textAppearance="?android:attr/textAppearanceSmall" />
        </RelativeLayout>
 
 </LinearLayout>
\ No newline at end of file
index 639611c20053711007bca29fbc1d0d8fd3c22d77..90da617b2ffe9af6e29e49e5f476ebe600415a79 100644 (file)
        <string name="gps_ready_label">GPS Ready</string>
        <string name="latitude_label">Latitude</string>
        <string name="longitude_label">Longitude</string>
-       <string name="pad_lat_label">Pad Latitude</string>
-       <string name="pad_lon_label">Pad Longitude</string>
-       <string name="pad_alt_label">Pad Altitude</string>
+       <string name="target_latitude_label">Tar Lat</string>
+       <string name="target_longitude_label">Tar Lon</string>
+       <string name="receiver_latitude_label">My Lat</string>
+       <string name="receiver_longitude_label">My Lon</string>
+       <string name="pad_lat_label">Pad Lat</string>
+       <string name="pad_lon_label">Pad Lon</string>
+       <string name="pad_alt_label">Pad Alt</string>
 
 </resources>
index c9ce46a0d0c8487c075d6c95e6a8b6a845e32657..e10982f7cc8873832c99fa21e2937c260deab64f 100644 (file)
@@ -37,7 +37,7 @@ import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
 import android.support.v4.app.FragmentActivity;
-import android.support.v4.view.ViewPager;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -47,6 +47,7 @@ import android.widget.TabHost;
 import android.widget.TextView;
 import android.widget.Toast;
 import android.app.AlertDialog;
+import android.location.Location;
 
 import org.altusmetrum.altoslib_1.*;
 
@@ -59,6 +60,8 @@ public class AltosDroid extends FragmentActivity {
        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;
 
        // Intent request codes
        private static final int REQUEST_CONNECT_DEVICE = 1;
@@ -79,14 +82,16 @@ public class AltosDroid extends FragmentActivity {
        private TextView mVersion;
 
        // Tabs
-       TabHost     mTabHost;
-       AltosViewPager   mViewPager;
-       TabsAdapter mTabsAdapter;
+       TabHost         mTabHost;
+       AltosViewPager  mViewPager;
+       TabsAdapter     mTabsAdapter;
        ArrayList<AltosDroidTab> mTabs = new ArrayList<AltosDroidTab>();
+       int             tabHeight;
 
        // Timer and Saved flight state for Age calculation
        private Timer timer = new Timer();
        AltosState saved_state;
+       Location saved_location;
 
        // Service
        private boolean mIsBound   = false;
@@ -122,7 +127,6 @@ public class AltosDroid extends FragmentActivity {
                                        ad.mTitle.setText(R.string.title_connected_to);
                                        ad.mTitle.append(str);
                                        Toast.makeText(ad.getApplicationContext(), "Connected to " + str, Toast.LENGTH_SHORT).show();
-                                       ad.mAltosVoice.speak("Connected");
                                        break;
                                case TelemetryService.STATE_CONNECTING:
                                        ad.mTitle.setText(R.string.title_connecting);
@@ -137,6 +141,10 @@ public class AltosDroid extends FragmentActivity {
                        case MSG_TELEMETRY:
                                ad.update_ui((AltosState) msg.obj);
                                break;
+                       case MSG_LOCATION:
+                               ad.set_location((Location) msg.obj);
+                               break;
+                       case MSG_CRC_ERROR:
                        case MSG_UPDATE_AGE:
                                if (ad.saved_state != null) {
                                        ad.mAgeView.setText(String.format("%d", (System.currentTimeMillis() - ad.saved_state.report_time + 500) / 1000));
@@ -196,8 +204,13 @@ public class AltosDroid extends FragmentActivity {
                mTabs.remove(mTab);
        }
 
+       void set_location(Location location) {
+               saved_location = location;
+               update_ui(saved_state);
+       }
+
        void update_ui(AltosState state) {
-               if (saved_state != null) {
+               if (state != null && saved_state != null) {
                        if (saved_state.state != state.state) {
                                String currentTab = mTabHost.getCurrentTabTag();
                                switch (state.state) {
@@ -215,16 +228,33 @@ public class AltosDroid extends FragmentActivity {
                }
                saved_state = state;
 
-               mCallsignView.setText(state.data.callsign);
-               mSerialView.setText(String.format("%d", state.data.serial));
-               mFlightView.setText(String.format("%d", state.data.flight));
-               mStateView.setText(state.data.state());
-               mRSSIView.setText(String.format("%d", state.data.rssi));
+               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 (state != null) {
+                       mCallsignView.setText(state.data.callsign);
+                       mSerialView.setText(String.format("%d", state.data.serial));
+                       mFlightView.setText(String.format("%d", state.data.flight));
+                       mStateView.setText(state.data.state());
+                       mRSSIView.setText(String.format("%d", state.data.rssi));
+               }
 
                for (AltosDroidTab mTab : mTabs)
-                       mTab.update_ui(state);
+                       mTab.update_ui(state, from_receiver, saved_location);
 
-               mAltosVoice.tell(state);
+               if (state != null)
+                       mAltosVoice.tell(state);
        }
 
        private void onTimerTick() {
@@ -236,13 +266,27 @@ public class AltosDroid extends FragmentActivity {
 
        static String pos(double p, String pos, String neg) {
                String  h = pos;
+               if (p == AltosRecord.MISSING)
+                       return "";
                if (p < 0) {
                        h = neg;
                        p = -p;
                }
                int deg = (int) Math.floor(p);
                double min = (p - Math.floor(p)) * 60.0;
-               return String.format("%d° %9.6f\" %s", deg, min, h);
+               return String.format("%d°%9.4f\" %s", deg, min, h);
+       }
+
+       static String number(String format, double value) {
+               if (value == AltosRecord.MISSING)
+                       return "";
+               return String.format(format, value);
+       }
+
+       static String integer(String format, int value) {
+               if (value == AltosRecord.MISSING)
+                       return "";
+               return String.format(format, value);
        }
 
        @Override
@@ -269,6 +313,7 @@ public class AltosDroid extends FragmentActivity {
                setContentView(R.layout.altosdroid);
                getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);
 
+               // Create the Tabs and ViewPager
                mTabHost = (TabHost)findViewById(android.R.id.tabhost);
                mTabHost.setup();
 
@@ -284,6 +329,27 @@ public class AltosDroid extends FragmentActivity {
                mTabsAdapter.addTab(mTabHost.newTabSpec("map").setIndicator("Map"), TabMap.class, null);
 
 
+               // Scale the size of the Tab bar for different screen densities
+               // This probably won't be needed when we start supporting ICS+ tabs.
+               DisplayMetrics metrics = new DisplayMetrics();
+               getWindowManager().getDefaultDisplay().getMetrics(metrics);
+               int density = metrics.densityDpi;
+
+               if (density==DisplayMetrics.DENSITY_XHIGH)
+                       tabHeight = 65;
+               else if (density==DisplayMetrics.DENSITY_HIGH)
+                       tabHeight = 45;
+               else if (density==DisplayMetrics.DENSITY_MEDIUM)
+                       tabHeight = 35;
+               else if (density==DisplayMetrics.DENSITY_LOW)
+                       tabHeight = 25;
+               else
+                       tabHeight = 65;
+
+               for (int i = 0; i < 5; i++)
+                       mTabHost.getTabWidget().getChildAt(i).getLayoutParams().height = tabHeight;
+
+
                // Set up the custom title
                mTitle = (TextView) findViewById(R.id.title_left_text);
                mTitle.setText(R.string.app_name);
@@ -348,7 +414,7 @@ public class AltosDroid extends FragmentActivity {
                super.onDestroy();
                if(D) Log.e(TAG, "--- ON DESTROY ---");
 
-               mAltosVoice.stop();
+               if (mAltosVoice != null) mAltosVoice.stop();
        }
 
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
index 68bbe5935489b5875c94e21ca3273684104dce17..6ebb47f74e3b1ff9f3218f5655c5e1d43d6e2237 100644 (file)
@@ -17,8 +17,9 @@
 
 package org.altusmetrum.AltosDroid;
 
-import org.altusmetrum.altoslib_1.AltosState;
+import org.altusmetrum.altoslib_1.*;
+import android.location.Location;
 
 public interface AltosDroidTab {
-       public void update_ui(AltosState state);
+       public void update_ui(AltosState state, AltosGreatCircle from_receiver, Location receiver);
 }
index 7da5c4a982647120c39b9e00e7654c1e42b47b37..b3dba62693714403f95f0b312beeb890f6e88b1a 100644 (file)
@@ -38,7 +38,6 @@ public class AltosVoice {
                        public void onInit(int status) {\r
                                if (status == TextToSpeech.SUCCESS) tts_enabled = true;\r
                                if (tts_enabled) {\r
-                                       speak("AltosDroid ready");\r
                                        idle_thread = new IdleThread();\r
                                }\r
                        }\r
index 7b9cbde74a84f0f4721231b37252e7846d1ee872..71692122b8def9d96839a9f70414d0f74ecf20e3 100644 (file)
@@ -109,9 +109,10 @@ public class DeviceListActivity extends Activity {
                // If there are paired devices, add each one to the ArrayAdapter
                if (pairedDevices.size() > 0) {
                        findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
-                       for (BluetoothDevice device : pairedDevices) {
-                               mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
-                       }
+                       for (BluetoothDevice device : pairedDevices)
+                               if (device.getName().startsWith("TeleBT"))
+                                       mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
+
                } else {
                        String noDevices = getResources().getText(R.string.none_paired).toString();
                        mPairedDevicesArrayAdapter.add(noDevices);
@@ -185,7 +186,8 @@ public class DeviceListActivity extends Activity {
                                // Get the BluetoothDevice object from the Intent
                                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                                // If it's already paired, skip it, because it's been listed already
-                               if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
+                               if (   device.getBondState() != BluetoothDevice.BOND_BONDED
+                                   && device.getName().startsWith("TeleBT")               ) {
                                        mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
                                }
                        // When discovery is finished, change the Activity title
index 0f95bc220a78dcf51a62ed870bec1dfd88acd5f1..8e8d9c03a32c9105f0d6553b461a107d773a785e 100644 (file)
@@ -23,6 +23,8 @@ import android.widget.ImageView;
 
 public class GoNoGoLights {
        private Boolean state;
+       private Boolean missing;
+       private Boolean set;
 
        private ImageView red;
        private ImageView green;
@@ -35,16 +37,23 @@ public class GoNoGoLights {
                red = in_red;
                green = in_green;
                state = false;
+               missing = true;
+               set = false;
 
                dRed   = r.getDrawable(R.drawable.redled);
                dGreen = r.getDrawable(R.drawable.greenled);
                dGray  = r.getDrawable(R.drawable.grayled);
        }
 
-       public void set(Boolean s) {
-               if (s == state) return;
+       public void set(Boolean s, Boolean m) {
+               if (set && s == state && m == missing) return;
                state = s;
-               if (state) {
+               missing = m;
+               set = true;
+               if (missing) {
+                       red.setImageDrawable(dGray);
+                       green.setImageDrawable(dGray);
+               } else if (state) {
                        red.setImageDrawable(dGray);
                        green.setImageDrawable(dGreen);
                } else {
index bda6b1fd3b5dfdb4fe63b0b8d3ff8f919a7f1926..0e141ae47e942e2983333a1ef526c79b67b13c2d 100644 (file)
@@ -17,7 +17,7 @@
 
 package org.altusmetrum.AltosDroid;
 
-import org.altusmetrum.altoslib_1.AltosState;
+import org.altusmetrum.altoslib_1.*;
 
 import android.app.Activity;
 import android.os.Bundle;
@@ -27,6 +27,7 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
+import android.location.Location;
 
 public class TabAscent extends Fragment implements AltosDroidTab {
        AltosDroid mAltosDroid;
@@ -84,21 +85,28 @@ public class TabAscent extends Fragment implements AltosDroidTab {
                mAltosDroid = null;
        }
 
-       public void update_ui(AltosState state) {
-               mHeightView.setText(String.format("%6.0f m", state.height));
-               mMaxHeightView.setText(String.format("%6.0f m", state.max_height));
-               mSpeedView.setText(String.format("%6.0f m/s", state.speed()));
-               mMaxSpeedView.setText(String.format("%6.0f m/s", state.max_speed()));
-               mAccelView.setText(String.format("%6.0f m/s²", state.acceleration));
-               mMaxAccelView.setText(String.format("%6.0f m/s²", state.max_acceleration));
-
-               mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
-               mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
-
-               mApogeeVoltageView.setText(String.format("%4.2f V", state.drogue_sense));
-               mApogeeLights.set(state.drogue_sense > 3.2);
-
-               mMainVoltageView.setText(String.format("%4.2f V", state.main_sense));
-               mMainLights.set(state.main_sense > 3.2);
+       public void update_ui(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));
+
+                       if (state.gps != null) {
+                               mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+                               mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
+                       } else {
+                               mLatitudeView.setText("");
+                               mLongitudeView.setText("");
+                       }
+
+                       mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.drogue_sense));
+                       mApogeeLights.set(state.drogue_sense > 3.2, state.drogue_sense == AltosRecord.MISSING);
+
+                       mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_sense));
+                       mMainLights.set(state.main_sense > 3.2, state.main_sense == AltosRecord.MISSING);
+               }
        }
 }
index 3805b7e77b98757a3e518deacc6cbdb18f8630c4..09e7169b229ff1c126405ac9f8fe8a3fdbedabc6 100644 (file)
@@ -17,8 +17,7 @@
 
 package org.altusmetrum.AltosDroid;
 
-import org.altusmetrum.altoslib_1.AltosGreatCircle;
-import org.altusmetrum.altoslib_1.AltosState;
+import org.altusmetrum.altoslib_1.*;
 
 import android.app.Activity;
 import android.os.Bundle;
@@ -28,6 +27,7 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
+import android.location.Location;
 
 public class TabDescent extends Fragment implements AltosDroidTab {
        AltosDroid mAltosDroid;
@@ -89,24 +89,34 @@ public class TabDescent extends Fragment implements AltosDroidTab {
                mAltosDroid = null;
        }
 
-       public void update_ui(AltosState state) {
-               mSpeedView.setText(String.format("%6.0f m/s", state.speed()));
-               mHeightView.setText(String.format("%6.0f m", state.height));
-               mElevationView.setText(String.format("%3.0f°", state.elevation));
-               mRangeView.setText(String.format("%6.0f m", state.range));
-               if (state.from_pad != null) {
-                       mBearingView.setText(String.format("%3.0f°", state.from_pad.bearing));
-                       mCompassView.setText(state.from_pad.bearing_words(AltosGreatCircle.BEARING_LONG));
+       public void update_ui(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));
+                       if (from_receiver != null) {
+                               mElevationView.setText(AltosDroid.number("%3.0f°", from_receiver.elevation));
+                               mRangeView.setText(AltosDroid.number("%6.0f m", 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));
+                       } else { 
+                               mElevationView.setText("<unknown>");
+                               mRangeView.setText("<unknown>");
+                               mBearingView.setText("<unknown>");
+                               mCompassView.setText("<unknown>");
+                               mDistanceView.setText("<unknown>");
+                       }
+                       if (state.gps != null) {
+                               mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+                               mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
+                       }
+
+                       mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.drogue_sense));
+                       mApogeeLights.set(state.drogue_sense > 3.2, state.drogue_sense == AltosRecord.MISSING);
+
+                       mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_sense));
+                       mMainLights.set(state.main_sense > 3.2, state.main_sense == AltosRecord.MISSING);
                }
-               mDistanceView.setText(String.format("%6.0f m", state.range));
-               mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
-               mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
-
-               mApogeeVoltageView.setText(String.format("%4.2f V", state.drogue_sense));
-               mApogeeLights.set(state.drogue_sense > 3.2);
-
-               mMainVoltageView.setText(String.format("%4.2f V", state.main_sense));
-               mMainLights.set(state.main_sense > 3.2);
        }
 
 }
index a95e914557592b8de54eadd8c90067cbc1121e1d..f42b46b55520d7fbf8551e676ddda47dfebafe41 100644 (file)
@@ -17,7 +17,7 @@
 
 package org.altusmetrum.AltosDroid;
 
-import org.altusmetrum.altoslib_1.AltosState;
+import org.altusmetrum.altoslib_1.*;
 
 import android.app.Activity;
 import android.os.Bundle;
@@ -26,14 +26,17 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
+import android.location.Location;
 
 public class TabLanded extends Fragment implements AltosDroidTab {
        AltosDroid mAltosDroid;
 
        private TextView mBearingView;
        private TextView mDistanceView;
-       private TextView mLatitudeView;
-       private TextView mLongitudeView;
+       private TextView mTargetLatitudeView;
+       private TextView mTargetLongitudeView;
+       private TextView mReceiverLatitudeView;
+       private TextView mReceiverLongitudeView;
        private TextView mMaxHeightView;
        private TextView mMaxSpeedView;
        private TextView mMaxAccelView;
@@ -52,8 +55,10 @@ public class TabLanded extends Fragment implements AltosDroidTab {
 
                mBearingView   = (TextView) v.findViewById(R.id.bearing_value);
                mDistanceView  = (TextView) v.findViewById(R.id.distance_value);
-               mLatitudeView  = (TextView) v.findViewById(R.id.lat_value);
-               mLongitudeView = (TextView) v.findViewById(R.id.lon_value);
+               mTargetLatitudeView  = (TextView) v.findViewById(R.id.target_lat_value);
+               mTargetLongitudeView = (TextView) v.findViewById(R.id.target_lon_value);
+               mReceiverLatitudeView  = (TextView) v.findViewById(R.id.receiver_lat_value);
+               mReceiverLongitudeView = (TextView) v.findViewById(R.id.receiver_lon_value);
                mMaxHeightView = (TextView) v.findViewById(R.id.max_height_value);
                mMaxSpeedView  = (TextView) v.findViewById(R.id.max_speed_value);
                mMaxAccelView  = (TextView) v.findViewById(R.id.max_accel_value);
@@ -68,16 +73,26 @@ public class TabLanded extends Fragment implements AltosDroidTab {
                mAltosDroid = null;
        }
 
-       public void update_ui(AltosState state) {
-               if (state.from_pad != null) {
-                       mBearingView.setText(String.format("%3.0f°", state.from_pad.bearing));
-                       mDistanceView.setText(String.format("%6.0f m", state.from_pad.distance));
+       public void update_ui(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));
+               }
+               if (state != null && state.gps != null) {
+                       mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+                       mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
+               }
+
+               if (receiver != null) {
+                       mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
+                       mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "W", "E"));
+               }
+              
+               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()));
                }
-               mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
-               mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
-               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()));
        }
 
 }
index 8fc8f59278ac2c59062c8bcb80d1a27a15179e5c..d831f1172761cfabf52a5b404a8b1ba585d200c9 100644 (file)
@@ -35,11 +35,12 @@ import android.app.Activity;
 import android.graphics.Color;
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
+//import android.support.v4.app.FragmentTransaction;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
+import android.location.Location;
 
 public class TabMap extends Fragment implements AltosDroidTab {
        AltosDroid mAltosDroid;
@@ -54,8 +55,12 @@ public class TabMap extends Fragment implements AltosDroidTab {
 
        private TextView mDistanceView;
        private TextView mBearingView;
-       private TextView mLatitudeView;
-       private TextView mLongitudeView;
+       private TextView mTargetLatitudeView;
+       private TextView mTargetLongitudeView;
+       private TextView mReceiverLatitudeView;
+       private TextView mReceiverLongitudeView;
+
+       private double mapAccuracy = -1;
 
        @Override
        public void onAttach(Activity activity) {
@@ -83,8 +88,10 @@ public class TabMap extends Fragment implements AltosDroidTab {
                View v = inflater.inflate(R.layout.tab_map, container, false);
                mDistanceView  = (TextView)v.findViewById(R.id.distance_value);
                mBearingView   = (TextView)v.findViewById(R.id.bearing_value);
-               mLatitudeView  = (TextView)v.findViewById(R.id.lat_value);
-               mLongitudeView = (TextView)v.findViewById(R.id.lon_value);
+               mTargetLatitudeView  = (TextView)v.findViewById(R.id.target_lat_value);
+               mTargetLongitudeView = (TextView)v.findViewById(R.id.target_lon_value);
+               mReceiverLatitudeView  = (TextView)v.findViewById(R.id.receiver_lat_value);
+               mReceiverLongitudeView = (TextView)v.findViewById(R.id.receiver_lon_value);
                return v;
        }
 
@@ -113,7 +120,6 @@ public class TabMap extends Fragment implements AltosDroidTab {
                        mMap.setMyLocationEnabled(true);
                        mMap.getUiSettings().setTiltGesturesEnabled(false);
                        mMap.getUiSettings().setZoomControlsEnabled(false);
-                       mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(40.8,-104.7),8));
 
                        mRocketMarker = mMap.addMarker(
                                        // From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
@@ -139,26 +145,54 @@ public class TabMap extends Fragment implements AltosDroidTab {
                }
        }
 
-       public void update_ui(AltosState state) {
-               if (state.from_pad != null) {
-                       mDistanceView.setText(String.format("%6.0f m", state.from_pad.distance));
-                       mBearingView.setText(String.format("%3.0f°", state.from_pad.bearing));
+       private void center(double lat, double lon, double accuracy) {
+               if (mapAccuracy < 0 || accuracy < mapAccuracy/10) {
+                       mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lon),14));
+                       mapAccuracy = accuracy;
                }
-               mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
-               mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
+       }
 
-               if (mapLoaded) {
-                       mRocketMarker.setPosition(new LatLng(state.gps.lat, state.gps.lon));
-                       mRocketMarker.setVisible(true);
+       public void update_ui(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));
+               }
 
-                       mPolyline.setPoints(Arrays.asList(new LatLng(state.pad_lat, state.pad_lon), new LatLng(state.gps.lat, state.gps.lon)));
-                       mPolyline.setVisible(true);
+               if (state != null) {
+                       if (mapLoaded) {
+                               if (state.gps != null) {
+                                       mRocketMarker.setPosition(new LatLng(state.gps.lat, state.gps.lon));
+                                       mRocketMarker.setVisible(true);
 
-                       if (state.state == AltosLib.ao_flight_pad) {
-                               mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon));
-                               mPadMarker.setVisible(true);
+                                       mPolyline.setPoints(Arrays.asList(new LatLng(state.pad_lat, state.pad_lon), new LatLng(state.gps.lat, state.gps.lon)));
+                                       mPolyline.setVisible(true);
+                               }
+
+                               if (state.state == AltosLib.ao_flight_pad) {
+                                       mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon));
+                                       mPadMarker.setVisible(true);
+                               }
+                       }
+                       if (state.gps != null) {
+                               mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+                               mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
+                               if (state.gps.locked && state.gps.nsat >= 4)
+                                       center (state.gps.lat, state.gps.lon, 10);
                        }
                }
+
+               if (receiver != null) {
+                       double accuracy;
+
+                       if (receiver.hasAccuracy())
+                               accuracy = receiver.getAccuracy();
+                       else
+                               accuracy = 1000;
+                       mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
+                       mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "W", "E"));
+                       center (receiver.getLatitude(), receiver.getLongitude(), accuracy);
+               }
+
        }
 
 }
index 41776c1060a77b43fff4eaf61d2d034e295439cd..066c1353ef792a87896a18a3522d9623725a0e2d 100644 (file)
@@ -27,6 +27,7 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
+import android.location.Location;
 
 public class TabPad extends Fragment implements AltosDroidTab {
        AltosDroid mAltosDroid;
@@ -100,40 +101,49 @@ public class TabPad extends Fragment implements AltosDroidTab {
                mAltosDroid = null;
        }
 
-       public void update_ui(AltosState state) {
-               mBatteryVoltageView.setText(String.format("%4.2f V", state.battery));
-               mBatteryLights.set(state.battery > 3.7);
-
-               mApogeeVoltageView.setText(String.format("%4.2f V", state.drogue_sense));
-               mApogeeLights.set(state.drogue_sense > 3.2);
-
-               mMainVoltageView.setText(String.format("%4.2f V", state.main_sense));
-               mMainLights.set(state.main_sense > 3.2);
-
-               if (state.data.flight != 0) {
-                       if (state.data.state <= AltosLib.ao_flight_pad)
-                               mDataLoggingView.setText("Ready to record");
-                       else if (state.data.state < AltosLib.ao_flight_landed)
-                               mDataLoggingView.setText("Recording data");
-                       else
-                               mDataLoggingView.setText("Recorded data");
-               } else {
-                       mDataLoggingView.setText("Storage full");
+       public void update_ui(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+               if (state != null) {
+                       mBatteryVoltageView.setText(AltosDroid.number("%4.2f V", state.battery));
+                       mBatteryLights.set(state.battery > 3.7, state.battery == AltosRecord.MISSING);
+
+                       mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.drogue_sense));
+                       mApogeeLights.set(state.drogue_sense > 3.2, state.drogue_sense == AltosRecord.MISSING);
+
+                       mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_sense));
+                       mMainLights.set(state.main_sense > 3.2, state.main_sense == AltosRecord.MISSING);
+
+                       if (state.data.flight != 0) {
+                               if (state.data.state <= AltosLib.ao_flight_pad)
+                                       mDataLoggingView.setText("Ready to record");
+                               else if (state.data.state < AltosLib.ao_flight_landed)
+                                       mDataLoggingView.setText("Recording data");
+                               else
+                                       mDataLoggingView.setText("Recorded data");
+                       } else {
+                               mDataLoggingView.setText("Storage full");
+                       }
+                       mDataLoggingLights.set(state.data.flight != 0, state.data.flight == AltosRecord.MISSING);
+
+                       if (state.gps != null) {
+                               mGPSLockedView.setText(AltosDroid.integer("%4d sats", state.gps.nsat));
+                               mGPSLockedLights.set(state.gps.locked && state.gps.nsat >= 4, false);
+                               if (state.gps_ready)
+                                       mGPSReadyView.setText("Ready");
+                               else
+                                       mGPSReadyView.setText(AltosDroid.integer("Waiting %d", state.gps_waiting));
+                       } else
+                               mGPSLockedLights.set(false, true);
+                       mGPSReadyLights.set(state.gps_ready, state.gps == null);
                }
-               mDataLoggingLights.set(state.data.flight != 0);
 
-               mGPSLockedView.setText(String.format("%4d sats", state.gps.nsat));
-               mGPSLockedLights.set(state.gps.locked && state.gps.nsat >= 4);
-
-               if (state.gps_ready)
-                       mGPSReadyView.setText("Ready");
-               else
-                       mGPSReadyView.setText(String.format("Waiting %d", state.gps_waiting));
-               mGPSReadyLights.set(state.gps_ready);
-
-               mPadLatitudeView.setText(AltosDroid.pos(state.pad_lat, "N", "S"));
-               mPadLongitudeView.setText(AltosDroid.pos(state.pad_lon, "W", "E"));
-               mPadAltitudeView.setText(String.format("%4.0f m", state.pad_alt));
+               if (receiver != null) {
+                       double altitude = 0;
+                       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));
+               }
        }
 
 }
index 9460bdbc2817e904646a1430caf8e7944321cf78..716ec5894c9df42c5e0a975591ba8464c42e91bd 100644 (file)
@@ -68,12 +68,12 @@ public class TelemetryReader extends Thread {
                                        if (record == null)\r
                                                break;\r
                                        state = new AltosState(record, state);\r
-\r
                                        handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget();\r
                                } catch (ParseException pp) {\r
                                        Log.e(TAG, String.format("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage()));\r
                                } catch (AltosCRCException ce) {\r
                                        ++crc_errors;\r
+                                       handler.obtainMessage(TelemetryService.MSG_CRC_ERROR, new Integer(crc_errors)).sendToTarget();\r
                                }\r
                        }\r
                } catch (InterruptedException ee) {\r
index 5ff00a680a5d50c974419242a3be74fe054f8126..0236b537a2271d41028e9ad7d6ab48238a49853a 100644 (file)
@@ -29,7 +29,8 @@ import android.app.PendingIntent;
 import android.app.Service;
 import android.bluetooth.BluetoothDevice;
 import android.content.Intent;
-//import android.os.Bundle;
+import android.content.Context;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Handler;
 import android.os.Message;
@@ -37,10 +38,14 @@ import android.os.Messenger;
 import android.os.RemoteException;
 import android.util.Log;
 import android.widget.Toast;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationListener;
 
 import org.altusmetrum.altoslib_1.*;
 
-public class TelemetryService extends Service {
+
+public class TelemetryService extends Service implements LocationListener {
 
        private static final String TAG = "TelemetryService";
        private static final boolean D = true;
@@ -53,6 +58,7 @@ public class TelemetryService extends Service {
        static final int MSG_DISCONNECTED      = 6;
        static final int MSG_TELEMETRY         = 7;
        static final int MSG_SETFREQUENCY      = 8;
+       static final int MSG_CRC_ERROR         = 9;
 
        public static final int STATE_NONE       = 0;
        public static final int STATE_READY      = 1;
@@ -81,6 +87,12 @@ public class TelemetryService extends Service {
        // internally track state of bluetooth connection
        private int state = STATE_NONE;
 
+       // 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;
@@ -96,6 +108,10 @@ public class TelemetryService extends Service {
                                        // 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));
                                } catch (RemoteException e) {
                                        s.mClients.remove(msg.replyTo);
                                }
@@ -126,8 +142,15 @@ public class TelemetryService extends Service {
                                }
                                break;
                        case MSG_TELEMETRY:
+                               // forward telemetry messages
+                               s.last_state = (AltosState) msg.obj;
                                s.sendMessageToClients(Message.obtain(null, AltosDroid.MSG_TELEMETRY, msg.obj));
                                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));
+                               break;
                        case MSG_SETFREQUENCY:
                                if (s.state == STATE_CONNECTED) {
                                        try {
@@ -180,6 +203,9 @@ public class TelemetryService extends Service {
        }
 
        private void startAltosBluetooth() {
+               if (device == null) {
+                       return;
+               }
                if (mAltosBluetooth == null) {
                        if (D) Log.d(TAG, String.format("startAltosBluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress()));
                        mAltosBluetooth = new AltosBluetooth(device, mHandler);
@@ -209,6 +235,8 @@ public class TelemetryService extends Service {
 
        private void connected() {
                try {
+                       if (mAltosBluetooth == null)
+                               throw new InterruptedException("no bluetooth");
                        mConfigData = mAltosBluetooth.config_data();
                } catch (InterruptedException e) {
                } catch (TimeoutException e) {
@@ -249,6 +277,11 @@ public class TelemetryService extends Service {
                // 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, 0, 0, this);
+               locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
        }
 
        @Override
@@ -281,6 +314,9 @@ public class TelemetryService extends Service {
        @Override
        public void onDestroy() {
 
+               // Stop listening for location updates
+               ((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
+
                // Stop the bluetooth Comms threads
                stopAltosBluetooth();
 
@@ -300,4 +336,18 @@ public class TelemetryService extends Service {
        }
 
 
+       public void onLocationChanged(Location location) {
+               last_location = location;
+               sendMessageToClients(Message.obtain(null, AltosDroid.MSG_LOCATION, location));
+       }
+
+       public void onStatusChanged(String provider, int status, Bundle extras) {
+       }
+
+       public void onProviderEnabled(String provider) {
+       }
+
+       public void onProviderDisabled(String provider) {
+       }
+
 }
index 12659d88100322bffdca273180047356951551fa..2ca5a7a56f74093227285d72176f62b51307e304 100644 (file)
@@ -135,6 +135,12 @@ public class AltosConfigData implements Iterable<String> {
                }
        }
 
+       public boolean has_monitor_battery() {
+               if (product.startsWith("TeleBT"))
+                       return true;
+               return false;
+       }
+
        int[] parse_version(String v) {
                String[] parts = v.split("\\.");
                int r[] = new int[parts.length];
@@ -476,7 +482,7 @@ public class AltosConfigData implements Iterable<String> {
                /* UI doesn't support AES key config */
 
                /* AO_PYRO_NUM */
-               if (pyros.length > 0) {
+               if (npyro > 0) {
                        for (int p = 0; p < pyros.length; p++) {
                                link.printf("c P %s\n",
                                                   pyros[p].toString());
@@ -498,7 +504,7 @@ public class AltosConfigData implements Iterable<String> {
                switch (log_format) {
                case AltosLib.AO_LOG_FORMAT_FULL:
                case AltosLib.AO_LOG_FORMAT_TINY:
-               case AltosLib.AO_LOG_FORMAT_MEGAMETRUM:
+               case AltosLib.AO_LOG_FORMAT_TELEMEGA:
                        link.printf("l\n");
                        read_link(link, "done");
                default:
index bc698c8064a50723bf0d279f148b44c2043e6342..7a8bbdea2f4a055d90350c456afa33dbd8fbeba6 100644 (file)
@@ -131,24 +131,34 @@ public class AltosEepromIterable extends AltosRecordIterable {
                case AltosLib.AO_LOG_GPS_LAT:
                        eeprom.seen |= AltosRecord.seen_gps_lat;
                        int lat32 = record.a | (record.b << 16);
+                       if (state.gps == null)
+                               state.gps = new AltosGPS();
                        state.gps.lat = (double) lat32 / 1e7;
                        break;
                case AltosLib.AO_LOG_GPS_LON:
                        eeprom.seen |= AltosRecord.seen_gps_lon;
                        int lon32 = record.a | (record.b << 16);
+                       if (state.gps == null)
+                               state.gps = new AltosGPS();
                        state.gps.lon = (double) lon32 / 1e7;
                        break;
                case AltosLib.AO_LOG_GPS_ALT:
+                       if (state.gps == null)
+                               state.gps = new AltosGPS();
                        state.gps.alt = record.a;
                        break;
                case AltosLib.AO_LOG_GPS_SAT:
                        if (state.tick == eeprom.gps_tick) {
                                int svid = record.a;
                                int c_n0 = record.b >> 8;
+                               if (state.gps == null)
+                                       state.gps = new AltosGPS();
                                state.gps.add_sat(svid, c_n0);
                        }
                        break;
                case AltosLib.AO_LOG_GPS_DATE:
+                       if (state.gps == null)
+                               state.gps = new AltosGPS();
                        state.gps.year = (record.a & 0xff) + 2000;
                        state.gps.month = record.a >> 8;
                        state.gps.day = record.b & 0xff;
index 3039b4dcbaf22b018ee1ecf057a81224ebdf844b..345266588f5b16e75ff5d73b9b93761ececbf9d5 100644 (file)
@@ -45,4 +45,8 @@ public class AltosFlightReader {
        public boolean supports_telemetry(int telemetry) { return false; }
 
        public File backing_file() { return null; }
+
+       public boolean has_monitor_battery() { return false; }
+
+       public double monitor_battery() { return AltosRecord.MISSING; }
 }
index 068d8c9c75f5dd2c9315c456fcc2ba63b3c64d17..f23842f3d947c09b135bbfabef197111a1ab9a71 100644 (file)
@@ -217,33 +217,38 @@ public class AltosGPS {
        }
 
        public AltosGPS(AltosGPS old) {
-               nsat = old.nsat;
-               locked = old.locked;
-               connected = old.connected;
-               lat = old.lat;          /* degrees (+N -S) */
-               lon = old.lon;          /* degrees (+E -W) */
-               alt = old.alt;          /* m */
-               year = old.year;
-               month = old.month;
-               day = old.day;
-               hour = old.hour;
-               minute = old.minute;
-               second = old.second;
-
-               ground_speed = old.ground_speed;        /* m/s */
-               course = old.course;            /* degrees */
-               climb_rate = old.climb_rate;    /* m/s */
-               hdop = old.hdop;                /* unitless? */
-               h_error = old.h_error;  /* m */
-               v_error = old.v_error;  /* m */
-
-               if (old.cc_gps_sat != null) {
-                       cc_gps_sat = new AltosGPSSat[old.cc_gps_sat.length];
-                       for (int i = 0; i < old.cc_gps_sat.length; i++) {
-                               cc_gps_sat[i] = new AltosGPSSat();
-                               cc_gps_sat[i].svid = old.cc_gps_sat[i].svid;
-                               cc_gps_sat[i].c_n0 = old.cc_gps_sat[i].c_n0;
+               if (old != null) {
+                       nsat = old.nsat;
+                       locked = old.locked;
+                       connected = old.connected;
+                       lat = old.lat;          /* degrees (+N -S) */
+                       lon = old.lon;          /* degrees (+E -W) */
+                       alt = old.alt;          /* m */
+                       year = old.year;
+                       month = old.month;
+                       day = old.day;
+                       hour = old.hour;
+                       minute = old.minute;
+                       second = old.second;
+
+                       ground_speed = old.ground_speed;        /* m/s */
+                       course = old.course;            /* degrees */
+                       climb_rate = old.climb_rate;    /* m/s */
+                       hdop = old.hdop;                /* unitless? */
+                       h_error = old.h_error;  /* m */
+                       v_error = old.v_error;  /* m */
+
+                       if (old.cc_gps_sat != null) {
+                               cc_gps_sat = new AltosGPSSat[old.cc_gps_sat.length];
+                               for (int i = 0; i < old.cc_gps_sat.length; i++) {
+                                       cc_gps_sat[i] = new AltosGPSSat();
+                                       cc_gps_sat[i].svid = old.cc_gps_sat[i].svid;
+                                       cc_gps_sat[i].c_n0 = old.cc_gps_sat[i].c_n0;
+                               }
                        }
+               } else {
+                       ClearGPSTime();
+                       cc_gps_sat = null;
                }
        }
 }
index 921356a59f0cc36ae9bfda643b8f300ea423a096..f1cf0ae9089173de180b9b0e39108379ed807a53 100644 (file)
@@ -22,6 +22,8 @@ import java.lang.Math;
 public class AltosGreatCircle {
        public double   distance;
        public double   bearing;
+       public double   range;
+       public double   elevation;
 
        double sqr(double a) { return a * a; }
 
@@ -54,9 +56,8 @@ public class AltosGreatCircle {
                return bearing_string[length][(int)((bearing / 90 * 8 + 1) / 2)%16];
        }
 
-       public AltosGreatCircle (double start_lat, double start_lon,
-                                double end_lat, double end_lon)
-       {
+       public AltosGreatCircle (double start_lat, double start_lon, double start_alt,
+                                double end_lat, double end_lon, double end_alt) {
                double lat1 = rad * start_lat;
                double lon1 = rad * -start_lon;
                double lat2 = rad * end_lat;
@@ -88,14 +89,25 @@ public class AltosGreatCircle {
                }
                distance = d * earth_radius;
                bearing = course * 180/Math.PI;
+
+               double height_diff = end_alt - start_alt;
+               range = Math.sqrt(distance * distance + height_diff * height_diff);
+               elevation = Math.atan2(height_diff, distance) * 180 / Math.PI;
+       }
+
+       public AltosGreatCircle (double start_lat, double start_lon,
+                                double end_lat, double end_lon) {
+               this(start_lat, start_lon, 0, end_lat, end_lon, 0);
        }
 
        public AltosGreatCircle(AltosGPS start, AltosGPS end) {
-               this(start.lat, start.lon, end.lat, end.lon);
+               this(start.lat, start.lon, start.alt, end.lat, end.lon, end.alt);
        }
 
        public AltosGreatCircle() {
                distance = 0;
                bearing = 0;
+               range = 0;
+               elevation = 0;
        }
 }
index f2f75bbb60f80dc21cf388a70459e7c239c76b37..2e4ddef25705d5ab2cf40778c860b6db321783eb 100644 (file)
@@ -29,6 +29,7 @@ public class AltosIdleMonitor extends Thread {
        double                  frequency;
        String                  callsign;
        AltosState              previous_state;
+       AltosListenerState      listener_state;
        AltosConfigData         config_data;
        AltosGPS                gps;
 
@@ -51,11 +52,11 @@ public class AltosIdleMonitor extends Thread {
        }
 
        boolean has_sensor_mm(AltosConfigData config_data) {
-               return config_data.product.startsWith("MegaMetrum");
+               return config_data.product.startsWith("TeleMega");
        }
 
        boolean has_gps(AltosConfigData config_data) {
-               return config_data.product.startsWith("TeleMetrum") || config_data.product.startsWith("MegaMetrum");
+               return config_data.product.startsWith("TeleMetrum") || config_data.product.startsWith("TeleMega");
        }
 
        AltosRecord sensor_mm(AltosConfigData config_data) throws InterruptedException, TimeoutException {
@@ -116,8 +117,10 @@ public class AltosIdleMonitor extends Thread {
                } finally {
                        if (remote) {
                                link.stop_remote();
-                               if (record != null)
-                                       record.rssi = AltosRSSI();
+                               if (record != null) {
+                                       record.rssi = link.rssi();
+                                       listener_state.battery = link.monitor_battery();
+                               }
                        } else {
                                if (record != null)
                                        record.rssi = 0;
@@ -137,7 +140,7 @@ public class AltosIdleMonitor extends Thread {
        }
 
        public void post_state() {
-               listener.update(state);
+               listener.update(state, listener_state);
        }
 
        public void abort() {
@@ -172,5 +175,6 @@ public class AltosIdleMonitor extends Thread {
                link = in_link;
                remote = in_remote;
                state = null;
+               listener_state = new AltosListenerState();
        }
 }
index 7f58d61c5d78740464a78a8419b082b986249a7e..27e36dea2ce6b4f673bc47f3623c4e0e0d07810b 100644 (file)
@@ -18,5 +18,5 @@
 package org.altusmetrum.altoslib_1;
 
 public interface AltosIdleMonitorListener {
-       public void update(AltosState state);
+       public void update(AltosState state, AltosListenerState listener_state);
 }
\ No newline at end of file
index 192da0a9d765982aedb019eaadb3f33aeecbd149..25d17e726bf01727c2720d6aba01c1e6772eb722 100644 (file)
@@ -50,7 +50,7 @@ public class AltosLib {
        public static final int AO_LOG_SERIAL_NUMBER = 2002;
        public static final int AO_LOG_LOG_FORMAT = 2003;
 
-       /* Added for header fields in megametrum files */
+       /* Added for header fields in telemega files */
        public static final int AO_LOG_BARO_RESERVED = 3000;
        public static final int AO_LOG_BARO_SENS = 3001;
        public static final int AO_LOG_BARO_OFF = 3002;
@@ -89,10 +89,11 @@ public class AltosLib {
        public final static int product_telelco = 0x0010;
        public final static int product_telescience = 0x0011;
        public final static int product_telepyro =0x0012;
-       public final static int product_megametrum = 0x0023;
+       public final static int product_telemega = 0x0023;
        public final static int product_megadongle = 0x0024;
+       public final static int product_telegps = 0x0025;
        public final static int product_altusmetrum_min = 0x000a;
-       public final static int product_altusmetrum_max = 0x0024;
+       public final static int product_altusmetrum_max = 0x0025;
 
        public final static int product_any = 0x10000;
        public final static int product_basestation = 0x10000 + 1;
@@ -214,7 +215,7 @@ public class AltosLib {
        public static final int AO_LOG_FORMAT_TINY = 2;
        public static final int AO_LOG_FORMAT_TELEMETRY = 3;
        public static final int AO_LOG_FORMAT_TELESCIENCE = 4;
-       public static final int AO_LOG_FORMAT_MEGAMETRUM = 5;
+       public static final int AO_LOG_FORMAT_TELEMEGA = 5;
        public static final int AO_LOG_FORMAT_NONE = 127;
 
        public static boolean isspace(int c) {
index 9eb25ce0ac47b53092e706606e88be0104d15f20..159ebfe19c234b34b423a6619e56d18f7ca6f433 100644 (file)
@@ -364,6 +364,63 @@ public abstract class AltosLink implements Runnable {
                remote = false;
        }
 
+       public int rssi() throws TimeoutException, InterruptedException {
+               if (remote)
+                       return 0;
+               printf("s\n");
+               String line = get_reply_no_dialog(5000);
+               if (line == null)
+                       throw new TimeoutException();
+               String[] items = line.split("\\s+");
+               if (items.length < 2)
+                       return 0;
+               if (!items[0].equals("RSSI:"))
+                       return 0;
+               int rssi = Integer.parseInt(items[1]);
+               return rssi;
+       }
+
+       public String[] adc() throws TimeoutException, InterruptedException {
+               printf("a\n");
+               for (;;) {
+                       String line = get_reply_no_dialog(5000);
+                       if (line == null) {
+                               throw new TimeoutException();
+                       }
+                       if (!line.startsWith("tick:"))
+                               continue;
+                       String[] items = line.split("\\s+");
+                       return items;
+               }
+       }
+
+       public boolean has_monitor_battery() {
+               return config_data.has_monitor_battery();
+       }
+
+       public double monitor_battery() {
+               int monitor_batt = AltosRecord.MISSING;
+
+               if (config_data.has_monitor_battery()) {
+                       try {
+                       String[] items = adc();
+                       for (int i = 0; i < items.length;) {
+                               if (items[i].equals("batt")) {
+                                       monitor_batt = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               i++;
+                       }
+                       } catch (InterruptedException ie) {
+                       } catch (TimeoutException te) {
+                       }
+               }
+               if (monitor_batt == AltosRecord.MISSING)
+                       return AltosRecord.MISSING;
+               return AltosConvert.cc_battery_to_voltage(monitor_batt);
+       }
+
        public AltosLink() {
                callsign = "";
        }
diff --git a/altoslib/AltosListenerState.java b/altoslib/AltosListenerState.java
new file mode 100644 (file)
index 0000000..2fb4673
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_1;
+
+public class AltosListenerState {
+       public int      crc_errors;
+       public double   battery;
+
+       public AltosListenerState() {
+               crc_errors = 0;
+               battery = AltosRecord.MISSING;
+       }
+}
index f8c44cc5a06a9f2f5092bd7eef316eb334d6b9d3..07e910eb726e53fb28471521035f7f0a68b7665b 100644 (file)
@@ -17,7 +17,7 @@
 
 package org.altusmetrum.altoslib_1;
 
-public abstract class AltosRecord implements Comparable <AltosRecord>, Cloneable {
+public class AltosRecord implements Comparable <AltosRecord>, Cloneable {
 
        public static final int seen_flight = 1;
        public static final int seen_sensor = 2;
@@ -75,9 +75,9 @@ public abstract class AltosRecord implements Comparable <AltosRecord>, Cloneable
         *      temperature:    Â°C
         */
 
-       abstract public double pressure();
-       abstract public double ground_pressure();
-       abstract public double acceleration();
+       public double pressure() { return MISSING; }
+       public double ground_pressure() { return MISSING; }
+       public double acceleration() { return MISSING; }
 
        public double altitude() {
                double  p = pressure();
@@ -126,7 +126,11 @@ public abstract class AltosRecord implements Comparable <AltosRecord>, Cloneable
                return tick - o.tick;
        }
 
-       abstract public AltosRecord clone();
+       public AltosRecord clone() {
+               AltosRecord n = new AltosRecord();
+               n.copy(this);
+               return n;
+       }
 
        public void copy(AltosRecord old) {
                seen = old.seen;
@@ -150,13 +154,13 @@ public abstract class AltosRecord implements Comparable <AltosRecord>, Cloneable
                seen = 0;
                version = 0;
                callsign = "N0CALL";
-               serial = 0;
-               flight = 0;
+               serial = MISSING;
+               flight = MISSING;
                rssi = 0;
                status = 0;
                state = AltosLib.ao_flight_startup;
                tick = 0;
-               gps = new AltosGPS();
+               gps = null;
                new_gps = false;
                companion = null;
 
index 8372d0477ffdc3c827c91e956f9e7adfe9bca55f..6d1b61c079fa3d9305a661f871da2122651ffe1d 100644 (file)
@@ -28,75 +28,65 @@ class AltosSensorMM {
        int             accel_ref;
 
        public AltosSensorMM(AltosLink link) throws InterruptedException, TimeoutException {
-               link.printf("a\n");
-               for (;;) {
-                       String line = link.get_reply_no_dialog(5000);
-                       if (line == null) {
-                               throw new TimeoutException();
+               String[] items = link.adc();
+               sense = new int[6];
+               for (int i = 0; i < items.length;) {
+                       if (items[i].equals("tick:")) {
+                               tick = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("0:")) {
+                               sense[0] = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("1:")) {
+                               sense[1] = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("2:")) {
+                               sense[2] = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("3:")) {
+                               sense[3] = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("4:")) {
+                               sense[4] = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("5:")) {
+                               sense[5] = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("6:")) {
+                               v_batt = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("7:")) {
+                               v_pyro = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("8:")) {
+                               accel = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
                        }
-                       if (!line.startsWith("tick:"))
+                       if (items[i].equals("9:")) {
+                               accel_ref = Integer.parseInt(items[i+1]);
+                               i += 2;
                                continue;
-                       String[] items = line.split("\\s+");
-                       sense = new int[6];
-                       for (int i = 0; i < items.length;) {
-                               if (items[i].equals("tick:")) {
-                                       tick = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("0:")) {
-                                       sense[0] = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("1:")) {
-                                       sense[1] = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("2:")) {
-                                       sense[2] = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("3:")) {
-                                       sense[3] = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("4:")) {
-                                       sense[4] = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("5:")) {
-                                       sense[5] = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("6:")) {
-                                       v_batt = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("7:")) {
-                                       v_pyro = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("8:")) {
-                                       accel = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("9:")) {
-                                       accel_ref = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               i++;
                        }
-                       break;
+                       i++;
                }
        }
 }
index f5fa83a595a94a6cc7196d3159f1d9ab0234ebfa..754dc5bbbc85cec8d762a5ec8fc5be1da31b9fde 100644 (file)
@@ -23,54 +23,44 @@ class AltosSensorTM extends AltosRecordTM {
 
        public AltosSensorTM(AltosLink link, AltosConfigData config_data) throws InterruptedException, TimeoutException {
                super();
-               link.printf("a\n");
-               for (;;) {
-                       String line = link.get_reply_no_dialog(5000);
-                       if (line == null) {
-                               throw new TimeoutException();
+               String[] items = link.adc();
+               for (int i = 0; i < items.length;) {
+                       if (items[i].equals("tick:")) {
+                               tick = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("accel:")) {
+                               accel = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("pres:")) {
+                               pres = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("temp:")) {
+                               temp = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("batt:")) {
+                               batt = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("drogue:")) {
+                               drogue = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
                        }
-                       if (!line.startsWith("tick:"))
+                       if (items[i].equals("main:")) {
+                               main = Integer.parseInt(items[i+1]);
+                               i += 2;
                                continue;
-                       String[] items = line.split("\\s+");
-                       for (int i = 0; i < items.length;) {
-                               if (items[i].equals("tick:")) {
-                                       tick = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("accel:")) {
-                                       accel = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("pres:")) {
-                                       pres = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("temp:")) {
-                                       temp = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("batt:")) {
-                                       batt = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("drogue:")) {
-                                       drogue = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               if (items[i].equals("main:")) {
-                                       main = Integer.parseInt(items[i+1]);
-                                       i += 2;
-                                       continue;
-                               }
-                               i++;
                        }
-                       break;
+                       i++;
                }
                ground_accel = config_data.accel_cal_plus;
                ground_pres = pres;
index 5598a60353b9af204dd78634de0200c25d7eeb1c..a3b9a8c07efafdb54f889094c09e1b7ba6a505f9 100644 (file)
@@ -98,12 +98,20 @@ public class AltosState {
                ground_altitude = data.ground_altitude();
 
                altitude = data.altitude();
+               if (altitude == AltosRecord.MISSING && data.gps != null)
+                       altitude = data.gps.alt;
 
+               height = AltosRecord.MISSING;
                if (data.kalman_height != AltosRecord.MISSING)
                        height = data.kalman_height;
                else {
-                       if (prev_state != null)
-                               height = (prev_state.height * 15 + altitude - ground_altitude) / 16.0;
+                       if (altitude != AltosRecord.MISSING && ground_altitude != AltosRecord.MISSING) {
+                               double  cur_height = altitude - ground_altitude;
+                               if (prev_state == null || prev_state.height == AltosRecord.MISSING)
+                                       height = cur_height;
+                               else
+                                       height = (prev_state.height * 15 + cur_height) / 16.0;
+                       }
                }
 
                report_time = System.currentTimeMillis();
@@ -147,38 +155,51 @@ public class AltosState {
                                /* compute barometric speed */
 
                                double height_change = height - prev_state.height;
+
+                               double prev_baro_speed = prev_state.baro_speed;
+                               if (prev_baro_speed == AltosRecord.MISSING)
+                                       prev_baro_speed = 0;
+
                                if (time_change > 0)
-                                       baro_speed = (prev_state.baro_speed * 3 + (height_change / time_change)) / 4.0;
+                                       baro_speed = (prev_baro_speed * 3 + (height_change / time_change)) / 4.0;
                                else
                                        baro_speed = prev_state.baro_speed;
 
+                               double prev_accel_speed = prev_state.accel_speed;
+
+                               if (prev_accel_speed == AltosRecord.MISSING)
+                                       prev_accel_speed = 0;
+
                                if (acceleration == AltosRecord.MISSING) {
                                        /* Fill in mising acceleration value */
                                        accel_speed = baro_speed;
-                                       if (time_change > 0)
-                                               acceleration = (accel_speed - prev_state.accel_speed) / time_change;
+
+                                       if (time_change > 0 && accel_speed != AltosRecord.MISSING)
+                                               acceleration = (accel_speed - prev_accel_speed) / time_change;
                                        else
                                                acceleration = prev_state.acceleration;
                                } else {
                                        /* compute accelerometer speed */
-                                       accel_speed = prev_state.accel_speed + acceleration * time_change;
+                                       accel_speed = prev_accel_speed + acceleration * time_change;
                                }
                        }
-
                } else {
                        npad = 0;
                        ngps = 0;
                        gps = null;
-                       baro_speed = 0;
-                       accel_speed = 0;
+                       baro_speed = AltosRecord.MISSING;
+                       accel_speed = AltosRecord.MISSING;
+                       pad_alt = AltosRecord.MISSING;
+                       max_baro_speed = 0;
+                       max_accel_speed = 0;
+                       max_height = 0;
+                       max_acceleration = 0;
                        time_change = 0;
-                       if (acceleration == AltosRecord.MISSING)
-                               acceleration = 0;
                }
 
                time = tick / 100.0;
 
-               if (cur.new_gps && (state == AltosLib.ao_flight_pad || state == AltosLib.ao_flight_idle)) {
+               if (cur.new_gps && (state < AltosLib.ao_flight_boost)) {
 
                        /* Track consecutive 'good' gps reports, waiting for 10 of them */
                        if (data.gps != null && data.gps.locked && data.gps.nsat >= 4)
@@ -188,7 +209,7 @@ public class AltosState {
 
                        /* Average GPS data while on the pad */
                        if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) {
-                               if (ngps > 1) {
+                               if (ngps > 1 && state == AltosLib.ao_flight_pad) {
                                        /* filter pad position */
                                        pad_lat = (pad_lat * 31.0 + data.gps.lat) / 32.0;
                                        pad_lon = (pad_lon * 31.0 + data.gps.lon) / 32.0;
@@ -201,7 +222,7 @@ public class AltosState {
                                ngps++;
                        }
                } else {
-                       if (ngps == 0)
+                       if (ngps == 0 && ground_altitude != AltosRecord.MISSING)
                                pad_alt = ground_altitude;
                }
 
@@ -218,32 +239,30 @@ public class AltosState {
                boost = (AltosLib.ao_flight_boost == state);
 
                /* Only look at accelerometer data under boost */
-               if (boost && acceleration > max_acceleration && acceleration != AltosRecord.MISSING)
+               if (boost && acceleration != AltosRecord.MISSING && (max_acceleration == AltosRecord.MISSING || acceleration > max_acceleration))
                        max_acceleration = acceleration;
-               if (boost && accel_speed > max_accel_speed && accel_speed != AltosRecord.MISSING)
+               if (boost && accel_speed != AltosRecord.MISSING && accel_speed > max_accel_speed)
                        max_accel_speed = accel_speed;
-               if (boost && baro_speed > max_baro_speed && baro_speed != AltosRecord.MISSING)
+               if (boost && baro_speed != AltosRecord.MISSING && baro_speed > max_baro_speed)
                        max_baro_speed = baro_speed;
 
-               if (height > max_height && height != AltosRecord.MISSING)
+               if (height != AltosRecord.MISSING && height > max_height)
                        max_height = height;
+               elevation = 0;
+               range = -1;
+               gps_height = 0;
                if (data.gps != null) {
                        if (gps == null || !gps.locked || data.gps.locked)
                                gps = data.gps;
                        if (ngps > 0 && gps.locked) {
-                               from_pad = new AltosGreatCircle(pad_lat, pad_lon, gps.lat, gps.lon);
-                       }
-               }
-               elevation = 0;
-               range = -1;
-               if (ngps > 0) {
-                       gps_height = gps.alt - pad_alt;
-                       if (from_pad != null) {
-                               elevation = Math.atan2(height, from_pad.distance) * 180 / Math.PI;
-                               range = Math.sqrt(height * height + from_pad.distance * from_pad.distance);
+                               double h = height;
+
+                               if (h == AltosRecord.MISSING) h = 0;
+                               from_pad = new AltosGreatCircle(pad_lat, pad_lon, 0, gps.lat, gps.lon, h);
+                               elevation = from_pad.elevation;
+                               range = from_pad.range;
+                               gps_height = gps.alt - pad_alt;
                        }
-               } else {
-                       gps_height = 0;
                }
        }
 
index f365b82118eb320e6fd8421d804517db95818af6..b4293c7349d2d9fda70bd03942aa91c37e0ac031 100644 (file)
@@ -107,6 +107,14 @@ public class AltosTelemetryReader extends AltosFlightReader {
                return log.file();
        }
 
+       public boolean has_monitor_battery() {
+               return link.has_monitor_battery();
+       }
+
+       public double monitor_battery() {
+               return link.monitor_battery();
+       }
+
        public AltosTelemetryReader (AltosLink in_link)
                throws IOException, InterruptedException, TimeoutException {
                link = in_link;
index 01215968427069b36aa585090404f58ff9b57af6..fdc3c88eb6d25b773c1dbad62a6fba17564f5ef9 100644 (file)
@@ -80,25 +80,25 @@ public abstract class AltosTelemetryRecord {
                                r = new AltosTelemetryRecordSensor(bytes, rssi);
                                break;
                        case packet_type_configuration:
-                               r = new AltosTelemetryRecordConfiguration(bytes);
+                               r = new AltosTelemetryRecordConfiguration(bytes, rssi);
                                break;
                        case packet_type_location:
-                               r = new AltosTelemetryRecordLocation(bytes);
+                               r = new AltosTelemetryRecordLocation(bytes, rssi);
                                break;
                        case packet_type_satellite:
-                               r = new AltosTelemetryRecordSatellite(bytes);
+                               r = new AltosTelemetryRecordSatellite(bytes, rssi);
                                break;
                        case packet_type_companion:
-                               r = new AltosTelemetryRecordCompanion(bytes);
+                               r = new AltosTelemetryRecordCompanion(bytes, rssi);
                                break;
                        case packet_type_MM_sensor:
                                r = new AltosTelemetryRecordMegaSensor(bytes, rssi);
                                break;
                        case packet_type_MM_data:
-                               r = new AltosTelemetryRecordMegaData(bytes);
+                               r = new AltosTelemetryRecordMegaData(bytes, rssi);
                                break;
                        default:
-                               r = new AltosTelemetryRecordRaw(bytes);
+                               r = new AltosTelemetryRecordRaw(bytes, rssi);
                                break;
                        }
                        break;
index e016dd016e757a93b94715fe81f00c3228a186b7..2231df13a1c8ec403aa87532ce1cdcb303ca2de5 100644 (file)
@@ -21,8 +21,8 @@ public class AltosTelemetryRecordCompanion extends AltosTelemetryRecordRaw {
 
        AltosRecordCompanion    companion;
 
-       public AltosTelemetryRecordCompanion(int[] in_bytes) {
-               super(in_bytes);
+       public AltosTelemetryRecordCompanion(int[] in_bytes, int rssi) {
+               super(in_bytes, rssi);
 
                int     off = 0;
                if (uint8(6) == 0)
index 472a631860ea9c23cdd23f4f3d7a04db34b1479d..47fc34886a20a2be529708a2abd11bac72d01b14 100644 (file)
@@ -29,8 +29,8 @@ public class AltosTelemetryRecordConfiguration extends AltosTelemetryRecordRaw {
        String  callsign;
        String  version;
 
-       public AltosTelemetryRecordConfiguration(int[] in_bytes) {
-               super(in_bytes);
+       public AltosTelemetryRecordConfiguration(int[] in_bytes, int rssi) {
+               super(in_bytes, rssi);
 
                device_type    = uint8(5);
                flight         = uint16(6);
index 469a5400d28bf9f8a0e679463ad86c2621fa9d31..029996963b555f9a9f4b2648e463d48adb87b5e3 100644 (file)
@@ -37,8 +37,8 @@ public class AltosTelemetryRecordLocation extends AltosTelemetryRecordRaw {
        int     climb_rate;
        int     course;
 
-       public AltosTelemetryRecordLocation(int[] in_bytes) {
-               super(in_bytes);
+       public AltosTelemetryRecordLocation(int[] in_bytes, int rssi) {
+               super(in_bytes, rssi);
 
                flags          = uint8(5);
                altitude       = int16(6);
index 08df9ee1ef15105460e487e08cd1bd95206d77bf..a484ef4ee52bfec3db92471294ab009ab7b1b288 100644 (file)
@@ -35,8 +35,8 @@ public class AltosTelemetryRecordMegaData extends AltosTelemetryRecordRaw {
        int     speed;
        int     height;
 
-       public AltosTelemetryRecordMegaData(int[] in_bytes) {
-               super(in_bytes);
+       public AltosTelemetryRecordMegaData(int[] in_bytes, int rssi) {
+               super(in_bytes, rssi);
 
                state = int8(5);
 
index 7548d69942af15ce6a055a1e45a4989fe27cc28a..2a4b17a4811839fd5f88503a2bca43e86f105c30 100644 (file)
@@ -35,10 +35,8 @@ public class AltosTelemetryRecordMegaSensor extends AltosTelemetryRecordRaw {
        int     mag_y;
        int     mag_z;
 
-       int     rssi;
-
-       public AltosTelemetryRecordMegaSensor(int[] in_bytes, int in_rssi) {
-               super(in_bytes);
+       public AltosTelemetryRecordMegaSensor(int[] in_bytes, int rssi) {
+               super(in_bytes, rssi);
 
                accel         = int16(6);
                pres          = int32(8);
@@ -55,8 +53,6 @@ public class AltosTelemetryRecordMegaSensor extends AltosTelemetryRecordRaw {
                mag_x         = int16(26);
                mag_y         = int16(28);
                mag_z         = int16(30);
-
-               rssi          = in_rssi;
        }
 
        public AltosRecord update_state(AltosRecord previous) {
@@ -85,8 +81,6 @@ public class AltosTelemetryRecordMegaSensor extends AltosTelemetryRecordRaw {
                next.mag.y = mag_y;
                next.mag.z = mag_z;
 
-               next.rssi = rssi;
-
                next.seen |= AltosRecord.seen_sensor;
 
                return next;
index a06348c19d2e379a3733b7cd0e2214b034d34834..f94789bb57adb08d936bf26ba9806eeeb199ca12 100644 (file)
@@ -22,6 +22,7 @@ public class AltosTelemetryRecordRaw extends AltosTelemetryRecord {
        int     serial;
        int     tick;
        int     type;
+       int     rssi;
 
        long    received_time;
 
@@ -53,11 +54,12 @@ public class AltosTelemetryRecordRaw extends AltosTelemetryRecord {
                return AltosLib.string(bytes, off + 1, l);
        }
 
-       public AltosTelemetryRecordRaw(int[] in_bytes) {
+       public AltosTelemetryRecordRaw(int[] in_bytes, int in_rssi) {
                bytes = in_bytes;
                serial = uint16(0);
                tick   = uint16(2);
                type   = uint8(4);
+               rssi   = in_rssi;
        }
 
        public AltosRecord update_state(AltosRecord previous) {
@@ -69,6 +71,7 @@ public class AltosTelemetryRecordRaw extends AltosTelemetryRecord {
                        next = new AltosRecordNone();
                next.serial = serial;
                next.tick = tick;
+               next.rssi = rssi;
                return next;
        }
 
index 3e93b3377fb156941e1d53f158b8c400d90e1421..9835389b977a127a424c4563da13403efbdcdedb 100644 (file)
@@ -21,8 +21,8 @@ public class AltosTelemetryRecordSatellite extends AltosTelemetryRecordRaw {
        int             channels;
        AltosGPSSat[]   sats;
 
-       public AltosTelemetryRecordSatellite(int[] in_bytes) {
-               super(in_bytes);
+       public AltosTelemetryRecordSatellite(int[] in_bytes, int rssi) {
+               super(in_bytes, rssi);
 
                channels = uint8(5);
                if (channels > 12)
index 767a464a7e5e046e8534481d89f46e21345e1516..e0e92c13d5ba2218d17119b0241db34e58400da3 100644 (file)
@@ -36,10 +36,8 @@ public class AltosTelemetryRecordSensor extends AltosTelemetryRecordRaw {
        int     accel_plus_g;
        int     accel_minus_g;
 
-       int     rssi;
-
-       public AltosTelemetryRecordSensor(int[] in_bytes, int in_rssi) {
-               super(in_bytes);
+       public AltosTelemetryRecordSensor(int[] in_bytes, int rssi) {
+               super(in_bytes, rssi);
                state         = uint8(5);
 
                accel         = int16(6);
@@ -57,8 +55,6 @@ public class AltosTelemetryRecordSensor extends AltosTelemetryRecordRaw {
                ground_accel  = int16(26);
                accel_plus_g  = int16(28);
                accel_minus_g = int16(30);
-
-               rssi          = in_rssi;
        }
 
        public AltosRecord update_state(AltosRecord prev) {
@@ -101,8 +97,6 @@ public class AltosTelemetryRecordSensor extends AltosTelemetryRecordRaw {
                        next.accel_minus_g = AltosRecord.MISSING;
                }
 
-               next.rssi = rssi;
-
                next.seen |= AltosRecord.seen_sensor | AltosRecord.seen_temp_volt;
 
                return next;
index 5d6a5c088b7a3b11610e1c2273d2bde804058788..18b028d628776652ef2243e30ba8eb50ce22fc80 100644 (file)
@@ -41,6 +41,7 @@ altoslib_JAVA = \
        AltosIMUQuery.java \
        AltosLine.java \
        AltosLink.java \
+       AltosListenerState.java \
        AltosLog.java \
        AltosMs5607.java \
        AltosMs5607Query.java \
index e90e0e232003c99548bbaf7c5d84a2a7b6dab0f8..4da4d591d70edaf06a2921a734cb9af616d8b40f 100644 (file)
@@ -42,7 +42,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay {
                        label.setVisible(false);
                }
 
-               void show(AltosState state, int crc_errors) {}
+               void show(AltosState state, AltosListenerState listener_state) {}
 
                void show(String s) {
                        show();
@@ -107,7 +107,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay {
        public class AscentValue {
                JLabel          label;
                JTextField      value;
-               void show(AltosState state, int crc_errors) {}
+               void show(AltosState state, AltosListenerState listener_state) {}
 
                void reset() {
                        value.setText("");
@@ -174,7 +174,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay {
                JTextField      max_value;
                double          max;
 
-               void show(AltosState state, int crc_errors) {}
+               void show(AltosState state, AltosListenerState listener_state) {}
 
                void reset() {
                        value.setText("");
@@ -239,7 +239,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay {
 
 
        class Height extends AscentValueHold {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show(AltosConvert.height, state.height);
                }
                public Height (GridBagLayout layout, int y) {
@@ -250,7 +250,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay {
        Height  height;
 
        class Speed extends AscentValueHold {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        double speed = state.accel_speed;
                        if (!state.ascent)
                                speed = state.baro_speed;
@@ -264,7 +264,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay {
        Speed   speed;
 
        class Accel extends AscentValueHold {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show(AltosConvert.accel, state.acceleration);
                }
                public Accel (GridBagLayout layout, int y) {
@@ -286,7 +286,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay {
        }
 
        class Apogee extends AscentStatus {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show("%4.2f V", state.drogue_sense);
                        lights.set(state.drogue_sense > 3.2);
                }
@@ -298,7 +298,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay {
        Apogee apogee;
 
        class Main extends AscentStatus {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show("%4.2f V", state.main_sense);
                        lights.set(state.main_sense > 3.2);
                }
@@ -310,7 +310,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay {
        Main main;
 
        class Lat extends AscentValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        if (state.gps != null)
                                show(pos(state.gps.lat,"N", "S"));
                        else
@@ -324,7 +324,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay {
        Lat lat;
 
        class Lon extends AscentValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        if (state.gps != null)
                                show(pos(state.gps.lon,"E", "W"));
                        else
@@ -359,25 +359,25 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay {
                accel.set_font();
        }
 
-       public void show(AltosState state, int crc_errors) {
+       public void show(AltosState state, AltosListenerState listener_state) {
                if (state.gps != null && state.gps.connected) {
-                       lat.show(state, crc_errors);
-                       lon.show(state, crc_errors);
+                       lat.show(state, listener_state);
+                       lon.show(state, listener_state);
                } else {
                        lat.hide();
                        lon.hide();
                }
-               height.show(state, crc_errors);
+               height.show(state, listener_state);
                if (state.main_sense != AltosRecord.MISSING)
-                       main.show(state, crc_errors);
+                       main.show(state, listener_state);
                else
                        main.hide();
                if (state.drogue_sense != AltosRecord.MISSING)
-                       apogee.show(state, crc_errors);
+                       apogee.show(state, listener_state);
                else
                        apogee.hide();
-               speed.show(state, crc_errors);
-               accel.show(state, crc_errors);
+               speed.show(state, listener_state);
+               accel.show(state, listener_state);
        }
 
        public void labels(GridBagLayout layout, int y) {
@@ -398,6 +398,10 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay {
                add(max);
        }
 
+       public String getName() {
+               return "Ascent";
+       }
+
        public AltosAscent() {
                layout = new GridBagLayout();
 
index 7dd36aec08ecbf2835e9a6dfd9ea874d38f02c2d..ebe1d1f9826612cf004d23d224010da50d515aef 100644 (file)
@@ -83,7 +83,7 @@ public class AltosCompanionInfo extends JTable {
                }
        }
        
-       public void show(AltosState state, int crc_errors) {
+       public void show(AltosState state, AltosListenerState listener_state) {
                if (state == null)
                        return;
                if (state.data.companion != null)
index 4fd0647efa860a3d47f8d4732a479e69cea14314..11f405938ba385118eed7bbf57c6665f20d93b55 100644 (file)
@@ -719,6 +719,7 @@ public class AltosConfigUI
 
        public void set_main_deploy(int new_main_deploy) {
                main_deploy_value.setSelectedItem(Integer.toString(new_main_deploy));
+               main_deploy_value.setEnabled(new_main_deploy >= 0);
        }
 
        public int main_deploy() {
index 7de18afba43a871a7b73cdc6d77c396d546b73a6..f914f138f2fe7d2e2d116744a343910283ad8cc9 100644 (file)
@@ -75,7 +75,7 @@ public class AltosDataChooser extends JFileChooser {
                                                          "eeprom"));
                setFileFilter(new FileNameExtensionFilter("Telemetry file",
                                                          "telem"));
-               setFileFilter(new FileNameExtensionFilter("MegaMetrum eeprom file",
+               setFileFilter(new FileNameExtensionFilter("TeleMega eeprom file",
                                                          "mega"));
                setFileFilter(new FileNameExtensionFilter("Flight data file",
                                                          "telem", "eeprom", "mega"));
index 821e3963f976ac7ae296ee80c9df8f16bf66a898..29d33ddc9afac6085b9426bb8b37928274892987 100644 (file)
@@ -29,7 +29,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
                JTextField      value;
                AltosLights     lights;
 
-               abstract void show(AltosState state, int crc_errors);
+               abstract void show(AltosState state, AltosListenerState listener_state);
 
                void show() {
                        label.setVisible(true);
@@ -108,7 +108,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
                        value.setText("");
                }
 
-               abstract void show(AltosState state, int crc_errors);
+               abstract void show(AltosState state, AltosListenerState listener_state);
 
                void show() {
                        label.setVisible(true);
@@ -192,7 +192,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
                        value2.setFont(Altos.value_font);
                }
 
-               abstract void show(AltosState state, int crc_errors);
+               abstract void show(AltosState state, AltosListenerState listener_state);
 
                void show(String v1, String v2) {
                        show();
@@ -244,7 +244,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
        }
 
        class Height extends DescentValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show(AltosConvert.height, state.height);
                }
                public Height (GridBagLayout layout, int x, int y) {
@@ -255,7 +255,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
        Height  height;
 
        class Speed extends DescentValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        double speed = state.accel_speed;
                        if (!state.ascent)
                                speed = state.baro_speed;
@@ -280,7 +280,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
        }
 
        class Lat extends DescentValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        if (state.gps != null && state.gps.connected)
                                show(pos(state.gps.lat,"N", "S"));
                        else
@@ -294,7 +294,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
        Lat lat;
 
        class Lon extends DescentValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        if (state.gps != null && state.gps.connected)
                                show(pos(state.gps.lon,"W", "E"));
                        else
@@ -308,7 +308,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
        Lon lon;
 
        class Distance extends DescentValue {
-               void show(AltosState state, int crc_errors) {
+               void show(AltosState state, AltosListenerState listener_state) {
                        if (state.from_pad != null)
                                show(AltosConvert.distance, state.from_pad.distance);
                        else
@@ -324,7 +324,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
                
 
        class Apogee extends DescentStatus {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show("%4.2f V", state.drogue_sense);
                        lights.set(state.drogue_sense > 3.2);
                }
@@ -336,7 +336,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
        Apogee apogee;
 
        class Main extends DescentStatus {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show("%4.2f V", state.main_sense);
                        lights.set(state.main_sense > 3.2);
                }
@@ -348,7 +348,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
        Main main;
 
        class Bearing extends DescentDualValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        if (state.from_pad != null) {
                                show( String.format("%3.0f°", state.from_pad.bearing),
                                      state.from_pad.bearing_words(
@@ -365,7 +365,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
        Bearing bearing;
 
        class Range extends DescentValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show(AltosConvert.distance, state.range);
                }
                public Range (GridBagLayout layout, int x, int y) {
@@ -376,7 +376,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
        Range range;
 
        class Elevation extends DescentValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show("%3.0f°", state.elevation);
                }
                public Elevation (GridBagLayout layout, int x, int y) {
@@ -412,16 +412,16 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
                apogee.set_font();
        }
 
-       public void show(AltosState state, int crc_errors) {
-               height.show(state, crc_errors);
-               speed.show(state, crc_errors);
+       public void show(AltosState state, AltosListenerState listener_state) {
+               height.show(state, listener_state);
+               speed.show(state, listener_state);
                if (state.gps != null && state.gps.connected) {
-                       bearing.show(state, crc_errors);
-                       range.show(state, crc_errors);
-                       distance.show(state, crc_errors);
-                       elevation.show(state, crc_errors);
-                       lat.show(state, crc_errors);
-                       lon.show(state, crc_errors);
+                       bearing.show(state, listener_state);
+                       range.show(state, listener_state);
+                       distance.show(state, listener_state);
+                       elevation.show(state, listener_state);
+                       lat.show(state, listener_state);
+                       lon.show(state, listener_state);
                } else {
                        bearing.hide();
                        range.hide();
@@ -431,15 +431,19 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay {
                        lon.hide();
                }
                if (state.main_sense != AltosRecord.MISSING)
-                       main.show(state, crc_errors);
+                       main.show(state, listener_state);
                else
                        main.hide();
                if (state.drogue_sense != AltosRecord.MISSING)
-                       apogee.show(state, crc_errors);
+                       apogee.show(state, listener_state);
                else
                        apogee.hide();
        }
 
+       public String getName() {
+               return "Descent";
+       }
+
        public AltosDescent() {
                layout = new GridBagLayout();
 
index 6f8aa9ee3293556caba705c9bbb3aa55e7ca6f94..095bed992c08c524d5718e21066ece2b20ad5df6 100644 (file)
@@ -29,21 +29,17 @@ public class AltosDisplayThread extends Thread {
        IdleThread              idle_thread;
        AltosVoice              voice;
        AltosFlightReader       reader;
-       int                     crc_errors;
+       AltosState              old_state, state;
+       AltosListenerState      listener_state;
        AltosFlightDisplay      display;
 
-       void show_internal(AltosState state, int crc_errors) {
-               if (state != null)
-                       display.show(state, crc_errors);
-       }
-
-       void show_safely(AltosState in_state, int in_crc_errors) {
-               final AltosState state = in_state;
-               final int crc_errors = in_crc_errors;
+       synchronized void show_safely() {
+               final AltosState my_state = state;
+               final AltosListenerState my_listener_state = listener_state;
                Runnable r = new Runnable() {
                                public void run() {
                                        try {
-                                               show_internal(state, crc_errors);
+                                               display.show(my_state, my_listener_state);
                                        } catch (Exception ex) {
                                        }
                                }
@@ -73,7 +69,6 @@ public class AltosDisplayThread extends Thread {
        class IdleThread extends Thread {
 
                boolean started;
-               private AltosState state;
                int     reported_landing;
                int     report_interval;
                long    report_time;
@@ -129,7 +124,7 @@ public class AltosDisplayThread extends Thread {
                                ++reported_landing;
                                if (state.state != Altos.ao_flight_landed) {
                                        state.state = Altos.ao_flight_landed;
-                                       show_safely(state, 0);
+                                       show_safely();
                                }
                        }
                }
@@ -145,6 +140,10 @@ public class AltosDisplayThread extends Thread {
                public void run () {
                        try {
                                for (;;) {
+                                       if (reader.has_monitor_battery()) {
+                                               listener_state.battery = reader.monitor_battery();
+                                               show_safely();
+                                       }
                                        set_report_time();
                                        for (;;) {
                                                voice.drain();
@@ -155,6 +154,7 @@ public class AltosDisplayThread extends Thread {
                                                        wait(sleep_time);
                                                }
                                        }
+                                       
                                        report(false);
                                }
                        } catch (InterruptedException ie) {
@@ -164,18 +164,7 @@ public class AltosDisplayThread extends Thread {
                        }
                }
 
-               public synchronized void notice(AltosState new_state, boolean spoken) {
-                       AltosState old_state = state;
-                       state = new_state;
-                       if (!started && state.state > Altos.ao_flight_pad) {
-                               started = true;
-                               start();
-                       }
-
-                       if (state.state < Altos.ao_flight_drogue)
-                               report_interval = 10000;
-                       else
-                               report_interval = 20000;
+               public synchronized void notice(boolean spoken) {
                        if (old_state != null && old_state.state != state.state) {
                                report_time = now();
                                this.notify();
@@ -184,13 +173,12 @@ public class AltosDisplayThread extends Thread {
                }
 
                public IdleThread() {
-                       state = null;
                        reported_landing = 0;
                        report_interval = 10000;
                }
        }
 
-       boolean tell(AltosState state, AltosState old_state) {
+       synchronized boolean tell() {
                boolean ret = false;
                if (old_state == null || old_state.state != state.state) {
                        voice.speak(state.data.state());
@@ -222,12 +210,10 @@ public class AltosDisplayThread extends Thread {
 
        public void run() {
                boolean         interrupted = false;
-               //String                line;
-               AltosState      state = null;
-               AltosState      old_state = null;
                boolean         told;
 
                idle_thread = new IdleThread();
+               idle_thread.start();
 
                try {
                        for (;;) {
@@ -238,14 +224,14 @@ public class AltosDisplayThread extends Thread {
                                        old_state = state;
                                        state = new AltosState(record, state);
                                        reader.update(state);
-                                       show_safely(state, crc_errors);
-                                       told = tell(state, old_state);
-                                       idle_thread.notice(state, told);
+                                       show_safely();
+                                       told = tell();
+                                       idle_thread.notice(told);
                                } catch (ParseException pp) {
                                        System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage());
                                } catch (AltosCRCException ce) {
-                                       ++crc_errors;
-                                       show_safely(state, crc_errors);
+                                       ++listener_state.crc_errors;
+                                       show_safely();
                                }
                        }
                } catch (InterruptedException ee) {
@@ -264,6 +250,7 @@ public class AltosDisplayThread extends Thread {
        }
 
        public AltosDisplayThread(Frame in_parent, AltosVoice in_voice, AltosFlightDisplay in_display, AltosFlightReader in_reader) {
+               listener_state = new AltosListenerState();
                parent = in_parent;
                voice = in_voice;
                display = in_display;
index 801d4ec0ec4d874908d811e22f4d9eecba2faa47..a0523b58a2c00ea8734c614479994a617ef6249d 100644 (file)
@@ -366,7 +366,7 @@ public class AltosEepromDownload implements Runnable {
                                extension = "science";
                                CaptureTeleScience(eechunk);
                                break;
-                       case AltosLib.AO_LOG_FORMAT_MEGAMETRUM:
+                       case AltosLib.AO_LOG_FORMAT_TELEMEGA:
                                extension = "mega";
                                CaptureMega(eechunk);
                        }
index d1ed7d2faf4139da8e27cd2674484eeefd6a7f47..4f4c158e8e08aad3105165ca36b5865cde53163a 100644 (file)
@@ -22,7 +22,7 @@ import org.altusmetrum.altoslib_1.*;
 public interface AltosFlightDisplay {
        void reset();
 
-       void show(AltosState state, int crc_errors);
+       void show(AltosState state, AltosListenerState listener_state);
 
        void set_font();
 }
index da06bb3d3ea1f4de9d5de38fb0fa690503c3c357..dee31a8d3bcf6f8d3005803b110ead9cb6595236 100644 (file)
@@ -150,7 +150,7 @@ public class AltosFlightStats {
                                        max_speed = state.max_baro_speed;
                                max_acceleration = state.max_acceleration;
                        }
-                       if (state.gps.locked && state.gps.nsat >= 4) {
+                       if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) {
                                if (state.state <= Altos.ao_flight_pad) {
                                        pad_lat = state.gps.lat;
                                        pad_lon = state.gps.lon;
index 20539a9f6c2d98c4621cd088526bae2d1d133415..d2910414dec132af46e4fecb4e168052068073af 100644 (file)
@@ -28,7 +28,7 @@ public class AltosFlightStatus extends JComponent implements AltosFlightDisplay
                JLabel          label;
                JTextField      value;
 
-               void show(AltosState state, int crc_errors) {}
+               void show(AltosState state, AltosListenerState listener_state) {}
 
                void reset() {
                        value.setText("");
@@ -64,7 +64,7 @@ public class AltosFlightStatus extends JComponent implements AltosFlightDisplay
        }
 
        class Call extends FlightValue {
-               void show(AltosState state, int crc_errors) {
+               void show(AltosState state, AltosListenerState listener_state) {
                        value.setText(state.data.callsign);
                }
                public Call (GridBagLayout layout, int x) {
@@ -75,8 +75,11 @@ public class AltosFlightStatus extends JComponent implements AltosFlightDisplay
        Call call;
 
        class Serial extends FlightValue {
-               void show(AltosState state, int crc_errors) {
-                       value.setText(String.format("%d", state.data.serial));
+               void show(AltosState state, AltosListenerState listener_state) {
+                       if (state.data.serial == AltosRecord.MISSING)
+                               value.setText("none");
+                       else
+                               value.setText(String.format("%d", state.data.serial));
                }
                public Serial (GridBagLayout layout, int x) {
                        super (layout, x, "Serial");
@@ -86,8 +89,11 @@ public class AltosFlightStatus extends JComponent implements AltosFlightDisplay
        Serial serial;
 
        class Flight extends FlightValue {
-               void show(AltosState state, int crc_errors) {
-                       value.setText(String.format("%d", state.data.flight));
+               void show(AltosState state, AltosListenerState listener_state) {
+                       if (state.data.flight == AltosRecord.MISSING)
+                               value.setText("none");
+                       else
+                               value.setText(String.format("%d", state.data.flight));
                }
                public Flight (GridBagLayout layout, int x) {
                        super (layout, x, "Flight");
@@ -97,7 +103,7 @@ public class AltosFlightStatus extends JComponent implements AltosFlightDisplay
        Flight flight;
 
        class FlightState extends FlightValue {
-               void show(AltosState state, int crc_errors) {
+               void show(AltosState state, AltosListenerState listener_state) {
                        value.setText(state.data.state());
                }
                public FlightState (GridBagLayout layout, int x) {
@@ -108,7 +114,7 @@ public class AltosFlightStatus extends JComponent implements AltosFlightDisplay
        FlightState flight_state;
 
        class RSSI extends FlightValue {
-               void show(AltosState state, int crc_errors) {
+               void show(AltosState state, AltosListenerState listener_state) {
                        value.setText(String.format("%d", state.data.rssi));
                }
                public RSSI (GridBagLayout layout, int x) {
@@ -119,7 +125,7 @@ public class AltosFlightStatus extends JComponent implements AltosFlightDisplay
        RSSI rssi;
 
        class LastPacket extends FlightValue {
-               void show(AltosState state, int crc_errors) {
+               void show(AltosState state, AltosListenerState listener_state) {
                        long secs = (System.currentTimeMillis() - state.report_time + 500) / 1000;
                        value.setText(String.format("%d", secs));
                }
@@ -148,13 +154,13 @@ public class AltosFlightStatus extends JComponent implements AltosFlightDisplay
                last_packet.set_font();
        }
 
-       public void show (AltosState state, int crc_errors) {
-               call.show(state, crc_errors);
-               serial.show(state, crc_errors);
-               flight.show(state, crc_errors);
-               flight_state.show(state, crc_errors);
-               rssi.show(state, crc_errors);
-               last_packet.show(state, crc_errors);
+       public void show (AltosState state, AltosListenerState listener_state) {
+               call.show(state, listener_state);
+               serial.show(state, listener_state);
+               flight.show(state, listener_state);
+               flight_state.show(state, listener_state);
+               rssi.show(state, listener_state);
+               last_packet.show(state, listener_state);
        }
 
        public int height() {
index bf679b857cc4abfccd28d21f256acdbc84d52248..962a08f7821d3b88df57130eebd4679983ef9d6e 100644 (file)
@@ -22,12 +22,16 @@ import org.altusmetrum.altoslib_1.*;
 
 public class AltosFlightStatusUpdate implements ActionListener {
 
-       public AltosState       saved_state;
-       AltosFlightStatus       flightStatus;
+       public AltosState               saved_state;
+       public AltosListenerState       saved_listener_state;
+       AltosFlightStatus               flightStatus;
 
        public void actionPerformed (ActionEvent e) {
-               if (saved_state != null)
-                       flightStatus.show(saved_state, 0);
+               if (saved_state != null) {
+                       if (saved_listener_state == null)
+                               saved_listener_state = new AltosListenerState();
+                       flightStatus.show(saved_state, saved_listener_state);
+               }
        }
 
        public AltosFlightStatusUpdate (AltosFlightStatus in_flightStatus) {
index c8faab90f4086ad31f87cdae42bf037268f27255..6d010d23c14129ae53bc42ddc5149757d51e8db7 100644 (file)
@@ -39,6 +39,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay, A
        AltosSiteMap    sitemap;
        boolean         has_map;
        boolean         has_companion;
+       boolean         has_state;
 
        private AltosFlightStatus flightStatus;
        private AltosInfoTable flightInfo;
@@ -97,29 +98,44 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay, A
 
        AltosFlightStatusUpdate status_update;
 
-       public void show(AltosState state, int crc_errors) {
+       public void show(AltosState state, AltosListenerState listener_state) {
                status_update.saved_state = state;
+
+               if (state == null)
+                       state = new AltosState(new AltosRecord());
+
+               pad.show(state, listener_state);
+
+               if (state.state != Altos.ao_flight_startup) {
+                       if (!has_state) {
+                               pane.setTitleAt(0, "Launch Pad");
+                               pane.add(ascent, 1);
+                               pane.add(descent, 2);
+                               pane.add(landed, 3);
+                               has_state = true;
+                       }
+               }
+
+               ascent.show(state, listener_state);
+               descent.show(state, listener_state);
+               landed.show(state, listener_state);
+
                JComponent tab = which_tab(state);
-               try {
-               pad.show(state, crc_errors);
-               ascent.show(state, crc_errors);
-               descent.show(state, crc_errors);
-               landed.show(state, crc_errors);
                if (tab != cur_tab) {
                        if (cur_tab == pane.getSelectedComponent()) {
                                pane.setSelectedComponent(tab);
                        }
                        cur_tab = tab;
                }
-               flightStatus.show(state, crc_errors);
-               flightInfo.show(state, crc_errors);
+               flightStatus.show(state, listener_state);
+               flightInfo.show(state, listener_state);
 
                if (state.data.companion != null) {
                        if (!has_companion) {
                                pane.add("Companion", companion);
                                has_companion= true;
                        }
-                       companion.show(state, crc_errors);
+                       companion.show(state, listener_state);
                } else {
                        if (has_companion) {
                                pane.remove(companion);
@@ -131,17 +147,13 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay, A
                                pane.add("Site Map", sitemap);
                                has_map = true;
                        }
-                       sitemap.show(state, crc_errors);
+                       sitemap.show(state, listener_state);
                } else {
                        if (has_map) {
                                pane.remove(sitemap);
                                has_map = false;
                        }
                }
-               } catch (Exception e) {
-                       System.out.print("Show exception " + e + "\n");
-                       e.printStackTrace();
-               }
        }
 
        public void set_exit_on_close() {
@@ -260,22 +272,18 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay, A
                pane = new JTabbedPane();
 
                pad = new AltosPad();
-               pane.add("Launch Pad", pad);
+               pane.add("Status", pad);
 
                ascent = new AltosAscent();
-               pane.add("Ascent", ascent);
-
                descent = new AltosDescent();
-               pane.add("Descent", descent);
-
                landed = new AltosLanded(reader);
-               pane.add("Landed", landed);
 
                flightInfo = new AltosInfoTable();
                pane.add("Table", new JScrollPane(flightInfo));
 
                companion = new AltosCompanionInfo();
                has_companion = false;
+               has_state = false;
 
                sitemap = new AltosSiteMap();
                has_map = false;
index 2f3575a4387bbbd9e39f0e58a4a8394ce53a1f27..d8b8f6dd282d41b1bbce8b4724f7dac3212714a5 100644 (file)
@@ -26,17 +26,19 @@ public class AltosGraphUI extends AltosUIFrame
        AltosGraphDataSet       graphDataSet;
        AltosFlightStats        stats;
        AltosFlightStatsTable   statsTable;
+       boolean                 has_gps;
 
-       boolean fill_map(AltosRecordIterable records) {
+       void fill_map(AltosRecordIterable records) {
                boolean         any_gps = false;
                for (AltosRecord record : records) {
                        state = new AltosState(record, state);
-                       if (state.gps.locked && state.gps.nsat >= 4) {
-                               map.show(state, 0);
-                               any_gps = true;
+                       if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) {
+                               if (map == null)
+                                       map = new AltosSiteMap();
+                               map.show(state, null);
+                               has_gps = true;
                        }
                }
-               return any_gps;
        }
 
        AltosGraphUI(AltosRecordIterable records, File file) throws InterruptedException, IOException {
@@ -54,13 +56,13 @@ public class AltosGraphUI extends AltosUIFrame
 
                statsTable = new AltosFlightStatsTable(stats);
 
-               map = new AltosSiteMap();
-
                pane.add("Flight Graph", graph.panel);
                pane.add("Configure Graph", enable);
                pane.add("Flight Statistics", statsTable);
 
-               if (fill_map(records))
+               has_gps = false;
+               fill_map(records);
+               if (has_gps)
                        pane.add("Map", map);
 
                setContentPane (pane);
@@ -69,7 +71,7 @@ public class AltosGraphUI extends AltosUIFrame
 
                setDefaultCloseOperation(DISPOSE_ON_CLOSE);
                setVisible(true);
-               if (state != null)
+               if (state != null && has_gps)
                        map.centre(state);
        }
 }
index 8c883eebfc0cd8a5fdda2cf44d9e8095824c96e3..bbab017f1c525779c622e1623f1db541c8d72fba 100644 (file)
@@ -63,21 +63,21 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl
 
        AltosFlightStatusUpdate status_update;
 
-       public void show(AltosState state, int crc_errors) {
+       public void show(AltosState state, AltosListenerState listener_state) {
                status_update.saved_state = state;
                try {
-                       pad.show(state, crc_errors);
-                       flightStatus.show(state, crc_errors);
-                       flightInfo.show(state, crc_errors);
+                       pad.show(state, listener_state);
+                       flightStatus.show(state, listener_state);
+                       flightInfo.show(state, listener_state);
                } catch (Exception e) {
                        System.out.print("Show exception" + e);
                }
        }
 
-       public void update(final AltosState state) {
+       public void update(final AltosState state, final AltosListenerState listener_state) {
                Runnable r = new Runnable() {
                                public void run() {
-                                       show(state, 0);
+                                       show(state, listener_state);
                                }
                        };
                SwingUtilities.invokeLater(r);
index 2facf38ae21f76f95a6a9005804840f846eae321..3d16faf2cbf369f368563f8ce64dbd006015ccb5 100644 (file)
@@ -104,101 +104,118 @@ public class AltosInfoTable extends JTable {
                model.clear();
        }
 
-       public void show(AltosState state, int crc_errors) {
-               if (state == null)
-                       return;
+       public void show(AltosState state, AltosListenerState listener_state) {
                info_reset();
-               info_add_row(0, "Altitude", "%6.0f    m", state.altitude);
-               info_add_row(0, "Pad altitude", "%6.0f    m", state.ground_altitude);
-               info_add_row(0, "Height", "%6.0f    m", state.height);
-               info_add_row(0, "Max height", "%6.0f    m", state.max_height);
-               info_add_row(0, "Acceleration", "%8.1f  m/s²", state.acceleration);
-               info_add_row(0, "Max acceleration", "%8.1f  m/s²", state.max_acceleration);
-               info_add_row(0, "Speed", "%8.1f  m/s", state.speed());
-               info_add_row(0, "Max Speed", "%8.1f  m/s", state.max_accel_speed);
-               info_add_row(0, "Temperature", "%9.2f Â°C", state.temperature);
-               info_add_row(0, "Battery", "%9.2f V", state.battery);
-               if (state.drogue_sense != AltosRecord.MISSING)
-                       info_add_row(0, "Drogue", "%9.2f V", state.drogue_sense);
-               if (state.main_sense != AltosRecord.MISSING)
-                       info_add_row(0, "Main", "%9.2f V", state.main_sense);
-               info_add_row(0, "CRC Errors", "%6d", crc_errors);
-
-               if (state.gps == null || !state.gps.connected) {
-                       info_add_row(1, "GPS", "not available");
-               } else {
-                       if (state.gps_ready)
-                               info_add_row(1, "GPS state", "%s", "ready");
-                       else
-                               info_add_row(1, "GPS state", "wait (%d)",
-                                            state.gps_waiting);
-                       if (state.data.gps.locked)
-                               info_add_row(1, "GPS", "   locked");
-                       else if (state.data.gps.connected)
-                               info_add_row(1, "GPS", " unlocked");
-                       else
-                               info_add_row(1, "GPS", "  missing");
-                       info_add_row(1, "Satellites", "%6d", state.data.gps.nsat);
-                       info_add_deg(1, "Latitude", state.gps.lat, 'N', 'S');
-                       info_add_deg(1, "Longitude", state.gps.lon, 'E', 'W');
-                       info_add_row(1, "GPS altitude", "%6d", state.gps.alt);
-                       info_add_row(1, "GPS height", "%6.0f", state.gps_height);
-
-                       /* The SkyTraq GPS doesn't report these values */
-                       /*
-                       if (false) {
-                               info_add_row(1, "GPS ground speed", "%8.1f m/s %3d°",
-                                            state.gps.ground_speed,
-                                            state.gps.course);
-                               info_add_row(1, "GPS climb rate", "%8.1f m/s",
-                                            state.gps.climb_rate);
-                               info_add_row(1, "GPS error", "%6d m(h)%3d m(v)",
-                                            state.gps.h_error, state.gps.v_error);
-                       }
-                       */
-
-                       info_add_row(1, "GPS hdop", "%8.1f", state.gps.hdop);
-
-                       if (state.npad > 0) {
-                               if (state.from_pad != null) {
-                                       info_add_row(1, "Distance from pad", "%6d m",
-                                                    (int) (state.from_pad.distance + 0.5));
-                                       info_add_row(1, "Direction from pad", "%6d°",
-                                                    (int) (state.from_pad.bearing + 0.5));
-                                       info_add_row(1, "Elevation from pad", "%6d°",
-                                                    (int) (state.elevation + 0.5));
-                                       info_add_row(1, "Range from pad", "%6d m",
-                                                    (int) (state.range + 0.5));
-                               } else {
-                                       info_add_row(1, "Distance from pad", "unknown");
-                                       info_add_row(1, "Direction from pad", "unknown");
-                                       info_add_row(1, "Elevation from pad", "unknown");
-                                       info_add_row(1, "Range from pad", "unknown");
+               if (state != null) {
+                       if (state.altitude != AltosRecord.MISSING)
+                               info_add_row(0, "Altitude", "%6.0f    m", state.altitude);
+                       if (state.ground_altitude != AltosRecord.MISSING)
+                               info_add_row(0, "Pad altitude", "%6.0f    m", state.ground_altitude);
+                       if (state.height != AltosRecord.MISSING)
+                               info_add_row(0, "Height", "%6.0f    m", state.height);
+                       if (state.height != AltosRecord.MISSING)
+                               info_add_row(0, "Max height", "%6.0f    m", state.max_height);
+                       if (state.acceleration != AltosRecord.MISSING)
+                               info_add_row(0, "Acceleration", "%8.1f  m/s²", state.acceleration);
+                       if (state.acceleration != AltosRecord.MISSING)
+                               info_add_row(0, "Max acceleration", "%8.1f  m/s²", state.max_acceleration);
+                       if (state.speed() != AltosRecord.MISSING)
+                               info_add_row(0, "Speed", "%8.1f  m/s", state.speed());
+                       if (state.speed() != AltosRecord.MISSING)
+                               info_add_row(0, "Max Speed", "%8.1f  m/s", state.max_accel_speed);
+                       if (state.temperature != AltosRecord.MISSING)
+                               info_add_row(0, "Temperature", "%9.2f Â°C", state.temperature);
+                       if (state.battery != AltosRecord.MISSING)
+                               info_add_row(0, "Battery", "%9.2f V", state.battery);
+                       if (state.drogue_sense != AltosRecord.MISSING)
+                               info_add_row(0, "Drogue", "%9.2f V", state.drogue_sense);
+                       if (state.main_sense != AltosRecord.MISSING)
+                               info_add_row(0, "Main", "%9.2f V", state.main_sense);
+               }
+               if (listener_state != null) {
+                       info_add_row(0, "CRC Errors", "%6d", listener_state.crc_errors);
+
+                       if (listener_state.battery != AltosRecord.MISSING)
+                               info_add_row(0, "Receiver Battery", "%9.2f", listener_state.battery);
+               }
+
+               if (state != null) {
+                       if (state.gps == null || !state.gps.connected) {
+                               info_add_row(1, "GPS", "not available");
+                       } else {
+                               if (state.gps_ready)
+                                       info_add_row(1, "GPS state", "%s", "ready");
+                               else
+                                       info_add_row(1, "GPS state", "wait (%d)",
+                                                    state.gps_waiting);
+                               if (state.data.gps.locked)
+                                       info_add_row(1, "GPS", "   locked");
+                               else if (state.data.gps.connected)
+                                       info_add_row(1, "GPS", " unlocked");
+                               else
+                                       info_add_row(1, "GPS", "  missing");
+                               info_add_row(1, "Satellites", "%6d", state.data.gps.nsat);
+                               info_add_deg(1, "Latitude", state.gps.lat, 'N', 'S');
+                               info_add_deg(1, "Longitude", state.gps.lon, 'E', 'W');
+                               info_add_row(1, "GPS altitude", "%6d", state.gps.alt);
+                               info_add_row(1, "GPS height", "%6.0f", state.gps_height);
+
+                               /* The SkyTraq GPS doesn't report these values */
+                               /*
+                                 if (false) {
+                                 info_add_row(1, "GPS ground speed", "%8.1f m/s %3d°",
+                                 state.gps.ground_speed,
+                                 state.gps.course);
+                                 info_add_row(1, "GPS climb rate", "%8.1f m/s",
+                                 state.gps.climb_rate);
+                                 info_add_row(1, "GPS error", "%6d m(h)%3d m(v)",
+                                 state.gps.h_error, state.gps.v_error);
+                                 }
+                               */
+
+                               info_add_row(1, "GPS hdop", "%8.1f", state.gps.hdop);
+
+                               if (state.npad > 0) {
+                                       if (state.from_pad != null) {
+                                               info_add_row(1, "Distance from pad", "%6d m",
+                                                            (int) (state.from_pad.distance + 0.5));
+                                               info_add_row(1, "Direction from pad", "%6d°",
+                                                            (int) (state.from_pad.bearing + 0.5));
+                                               info_add_row(1, "Elevation from pad", "%6d°",
+                                                            (int) (state.elevation + 0.5));
+                                               info_add_row(1, "Range from pad", "%6d m",
+                                                            (int) (state.range + 0.5));
+                                       } else {
+                                               info_add_row(1, "Distance from pad", "unknown");
+                                               info_add_row(1, "Direction from pad", "unknown");
+                                               info_add_row(1, "Elevation from pad", "unknown");
+                                               info_add_row(1, "Range from pad", "unknown");
+                                       }
+                                       info_add_deg(1, "Pad latitude", state.pad_lat, 'N', 'S');
+                                       info_add_deg(1, "Pad longitude", state.pad_lon, 'E', 'W');
+                                       info_add_row(1, "Pad GPS alt", "%6.0f m", state.pad_alt);
                                }
-                               info_add_deg(1, "Pad latitude", state.pad_lat, 'N', 'S');
-                               info_add_deg(1, "Pad longitude", state.pad_lon, 'E', 'W');
-                               info_add_row(1, "Pad GPS alt", "%6.0f m", state.pad_alt);
-                       }
-                       info_add_row(1, "GPS date", "%04d-%02d-%02d",
-                                      state.gps.year,
-                                      state.gps.month,
-                                      state.gps.day);
-                       info_add_row(1, "GPS time", "  %02d:%02d:%02d",
-                                      state.gps.hour,
-                                      state.gps.minute,
-                                      state.gps.second);
-                       //int   nsat_vis = 0;
-                       int     c;
-
-                       if (state.gps.cc_gps_sat == null)
-                               info_add_row(2, "Satellites Visible", "%4d", 0);
-                       else {
-                               info_add_row(2, "Satellites Visible", "%4d", state.gps.cc_gps_sat.length);
-                               for (c = 0; c < state.gps.cc_gps_sat.length; c++) {
-                                       info_add_row(2, "Satellite id,C/N0",
-                                                    "%4d, %4d",
-                                                    state.gps.cc_gps_sat[c].svid,
-                                                    state.gps.cc_gps_sat[c].c_n0);
+                               info_add_row(1, "GPS date", "%04d-%02d-%02d",
+                                            state.gps.year,
+                                            state.gps.month,
+                                            state.gps.day);
+                               info_add_row(1, "GPS time", "  %02d:%02d:%02d",
+                                            state.gps.hour,
+                                            state.gps.minute,
+                                            state.gps.second);
+                               //int   nsat_vis = 0;
+                               int     c;
+
+                               if (state.gps.cc_gps_sat == null)
+                                       info_add_row(2, "Satellites Visible", "%4d", 0);
+                               else {
+                                       info_add_row(2, "Satellites Visible", "%4d", state.gps.cc_gps_sat.length);
+                                       for (c = 0; c < state.gps.cc_gps_sat.length; c++) {
+                                               info_add_row(2, "Satellite id,C/N0",
+                                                            "%4d, %4d",
+                                                            state.gps.cc_gps_sat[c].svid,
+                                                            state.gps.cc_gps_sat[c].c_n0);
+                                       }
                                }
                        }
                }
index e13229a898150dd3fcca91fded2ab52316af6593..1d209bda8ccaa493371d104126efb2d1c6ca21c2 100644 (file)
@@ -29,7 +29,7 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay, Actio
        public class LandedValue {
                JLabel          label;
                JTextField      value;
-               void show(AltosState state, int crc_errors) {}
+               void show(AltosState state, AltosListenerState listener_state) {}
 
                void reset() {
                        value.setText("");
@@ -102,7 +102,7 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay, Actio
        }
 
        class Lat extends LandedValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        if (state.gps != null && state.gps.connected)
                                show(pos(state.gps.lat,"N", "S"));
                        else
@@ -116,7 +116,7 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay, Actio
        Lat lat;
 
        class Lon extends LandedValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show();
                        if (state.gps != null && state.gps.connected)
                                show(pos(state.gps.lon,"E", "W"));
@@ -131,7 +131,7 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay, Actio
        Lon lon;
 
        class Bearing extends LandedValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show();
                        if (state.from_pad != null)
                                show("%3.0f°", state.from_pad.bearing);
@@ -146,7 +146,7 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay, Actio
        Bearing bearing;
 
        class Distance extends LandedValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show();
                        if (state.from_pad != null)
                                show(AltosConvert.distance, state.from_pad.distance);
@@ -161,7 +161,7 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay, Actio
        Distance distance;
 
        class Height extends LandedValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show(AltosConvert.height, state.max_height);
                }
                public Height (GridBagLayout layout, int y) {
@@ -172,7 +172,7 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay, Actio
        Height  height;
 
        class Speed extends LandedValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show(AltosConvert.speed, state.max_speed());
                }
                public Speed (GridBagLayout layout, int y) {
@@ -183,7 +183,7 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay, Actio
        Speed   speed;
 
        class Accel extends LandedValue {
-               void show (AltosState state, int crc_errors) {
+               void show (AltosState state, AltosListenerState listener_state) {
                        show(AltosConvert.accel, state.max_acceleration);
                }
                public Accel (GridBagLayout layout, int y) {
@@ -213,21 +213,21 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay, Actio
                accel.set_font();
        }
 
-       public void show(AltosState state, int crc_errors) {
+       public void show(AltosState state, AltosListenerState listener_state) {
                if (state.gps != null && state.gps.connected) {
-                       bearing.show(state, crc_errors);
-                       distance.show(state, crc_errors);
-                       lat.show(state, crc_errors);
-                       lon.show(state, crc_errors);
+                       bearing.show(state, listener_state);
+                       distance.show(state, listener_state);
+                       lat.show(state, listener_state);
+                       lon.show(state, listener_state);
                } else {
                        bearing.hide();
                        distance.hide();
                        lat.hide();
                        lon.hide();
                }
-               height.show(state, crc_errors);
-               speed.show(state, crc_errors);
-               accel.show(state, crc_errors);
+               height.show(state, listener_state);
+               speed.show(state, listener_state);
+               accel.show(state, listener_state);
                if (reader.backing_file() != null)
                        graph.setEnabled(true);
        }
@@ -271,6 +271,10 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay, Actio
                }
        }
 
+       public String getName() {
+               return "Landed";
+       }
+
        public AltosLanded(AltosFlightReader in_reader) {
                layout = new GridBagLayout();
 
index d13f69458c4705470cfc6cf398af73aee44f7d45..e2316a13c813a26fd248f5bb0737040391dbde0e 100644 (file)
@@ -29,7 +29,7 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
                JTextField      value;
                AltosLights     lights;
 
-               void show(AltosState state, int crc_errors) {}
+               void show(AltosState state, AltosListenerState listener_state) {}
 
                void reset() {
                        value.setText("");
@@ -66,6 +66,10 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
                        value.setFont(Altos.value_font);
                }
 
+               public void set_label(String text) {
+                       label.setText(text);
+               }
+               
                public LaunchStatus (GridBagLayout layout, int y, String text) {
                        GridBagConstraints      c = new GridBagConstraints();
                        c.weighty = 1;
@@ -105,7 +109,7 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
        public class LaunchValue {
                JLabel          label;
                JTextField      value;
-               void show(AltosState state, int crc_errors) {}
+               void show(AltosState state, AltosListenerState listener_state) {}
 
                void show() {
                        label.setVisible(true);
@@ -135,6 +139,10 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
                        show(String.format(format, v));
                }
 
+               public void set_label(String text) {
+                       label.setText(text);
+               }
+               
                void reset() {
                        value.setText("");
                }
@@ -167,9 +175,13 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
        }
 
        class Battery extends LaunchStatus {
-               void show (AltosState state, int crc_errors) {
-                       show("%4.2f V", state.battery);
-                       lights.set(state.battery > 3.7);
+               void show (AltosState state, AltosListenerState listener_state) {
+                       if (state == null || state.battery == AltosRecord.MISSING)
+                               hide();
+                       else {
+                               show("%4.2f V", state.battery);
+                               lights.set(state.battery > 3.7);
+                       }
                }
                public Battery (GridBagLayout layout, int y) {
                        super(layout, y, "Battery Voltage");
@@ -179,9 +191,13 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
        Battery battery;
 
        class Apogee extends LaunchStatus {
-               void show (AltosState state, int crc_errors) {
-                       show("%4.2f V", state.drogue_sense);
-                       lights.set(state.drogue_sense > 3.2);
+               void show (AltosState state, AltosListenerState listener_state) {
+                       if (state == null || state.drogue_sense == AltosRecord.MISSING)
+                               hide();
+                       else {
+                               show("%4.2f V", state.drogue_sense);
+                               lights.set(state.drogue_sense > 3.2);
+                       }
                }
                public Apogee (GridBagLayout layout, int y) {
                        super(layout, y, "Apogee Igniter Voltage");
@@ -191,9 +207,13 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
        Apogee apogee;
 
        class Main extends LaunchStatus {
-               void show (AltosState state, int crc_errors) {
-                       show("%4.2f V", state.main_sense);
-                       lights.set(state.main_sense > 3.2);
+               void show (AltosState state, AltosListenerState listener_state) {
+                       if (state == null || state.main_sense == AltosRecord.MISSING)
+                               hide();
+                       else {
+                               show("%4.2f V", state.main_sense);
+                               lights.set(state.main_sense > 3.2);
+                       }
                }
                public Main (GridBagLayout layout, int y) {
                        super(layout, y, "Main Igniter Voltage");
@@ -203,18 +223,21 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
        Main main;
 
        class LoggingReady extends LaunchStatus {
-               void show (AltosState state, int crc_errors) {
-                       if (state.data.flight != 0) {
-                               if (state.data.state <= Altos.ao_flight_pad)
-                                       show("Ready to record");
-                               else if (state.data.state < Altos.ao_flight_landed)
-                                       show("Recording data");
-                               else
-                                       show("Recorded data");
+               void show (AltosState state, AltosListenerState listener_state) {
+                       if (state == null || state.data.flight == AltosRecord.MISSING) {
+                               hide();
+                       } else {
+                               if (state.data.flight != 0) {
+                                       if (state.data.state <= Altos.ao_flight_pad)
+                                               show("Ready to record");
+                                       else if (state.data.state < Altos.ao_flight_landed)
+                                               show("Recording data");
+                                       else
+                                               show("Recorded data");
+                               } else
+                                       show("Storage full");
+                               lights.set(state.data.flight != 0);
                        }
-                       else
-                               show("Storage full");
-                       lights.set(state.data.flight != 0);
                }
                public LoggingReady (GridBagLayout layout, int y) {
                        super(layout, y, "On-board Data Logging");
@@ -224,9 +247,13 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
        LoggingReady logging_ready;
 
        class GPSLocked extends LaunchStatus {
-               void show (AltosState state, int crc_errors) {
-                       show("%4d sats", state.gps.nsat);
-                       lights.set(state.gps.locked && state.gps.nsat >= 4);
+               void show (AltosState state, AltosListenerState listener_state) {
+                       if (state == null || state.gps == null)
+                               hide();
+                       else {
+                               show("%4d sats", state.gps.nsat);
+                               lights.set(state.gps.locked && state.gps.nsat >= 4);
+                       }
                }
                public GPSLocked (GridBagLayout layout, int y) {
                        super (layout, y, "GPS Locked");
@@ -236,12 +263,16 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
        GPSLocked gps_locked;
 
        class GPSReady extends LaunchStatus {
-               void show (AltosState state, int crc_errors) {
-                       if (state.gps_ready)
-                               show("Ready");
-                       else
-                               show("Waiting %d", state.gps_waiting);
-                       lights.set(state.gps_ready);
+               void show (AltosState state, AltosListenerState listener_state) {
+                       if (state == null || state.gps == null)
+                               hide();
+                       else {
+                               if (state.gps_ready)
+                                       show("Ready");
+                               else
+                                       show("Waiting %d", state.gps_waiting);
+                               lights.set(state.gps_ready);
+                       }
                }
                public GPSReady (GridBagLayout layout, int y) {
                        super (layout, y, "GPS Ready");
@@ -250,6 +281,22 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
 
        GPSReady gps_ready;
 
+       class ReceiverBattery extends LaunchStatus {
+               void show (AltosState state, AltosListenerState listener_state) {
+                       if (listener_state == null || listener_state.battery == AltosRecord.MISSING)
+                               hide();
+                       else {
+                               show("%4.2f V", listener_state.battery);
+                               lights.set(listener_state.battery > 3.7);
+                       }
+               }
+               public ReceiverBattery (GridBagLayout layout, int y) {
+                       super(layout, y, "Receiver Battery");
+               }
+       }
+
+       ReceiverBattery receiver_battery;
+
        String pos(double p, String pos, String neg) {
                String  h = pos;
                if (p < 0) {
@@ -262,8 +309,18 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
        }
 
        class PadLat extends LaunchValue {
-               void show (AltosState state, int crc_errors) {
-                       show(pos(state.pad_lat,"N", "S"));
+               void show (AltosState state, AltosListenerState listener_state) {
+                       if (state == null || state.gps == null) {
+                               hide();
+                       } else {
+                               if (state.state < AltosLib.ao_flight_pad) {
+                                       show(pos(state.gps.lat,"N", "S"));
+                                       set_label("Latitude");
+                               } else { 
+                                       show(pos(state.pad_lat,"N", "S"));
+                                       set_label("Pad Latitude");
+                               }
+                       }
                }
                public PadLat (GridBagLayout layout, int y) {
                        super (layout, y, "Pad Latitude");
@@ -273,8 +330,18 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
        PadLat pad_lat;
 
        class PadLon extends LaunchValue {
-               void show (AltosState state, int crc_errors) {
-                       show(pos(state.pad_lon,"E", "W"));
+               void show (AltosState state, AltosListenerState listener_state) {
+                       if (state == null || state.gps == null) {
+                               hide();
+                       } else {
+                               if (state.state < AltosLib.ao_flight_pad) {
+                                       show(pos(state.gps.lon,"E", "W"));
+                                       set_label("Longitude");
+                               } else { 
+                                       show(pos(state.pad_lon,"E", "W"));
+                                       set_label("Pad Longitude");
+                               }
+                       }
                }
                public PadLon (GridBagLayout layout, int y) {
                        super (layout, y, "Pad Longitude");
@@ -284,8 +351,22 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
        PadLon pad_lon;
 
        class PadAlt extends LaunchValue {
-               void show (AltosState state, int crc_errors) {
-                       show("%4.0f m", state.pad_alt);
+               void show (AltosState state, AltosListenerState listener_state) {
+                       if (state == null)
+                               hide();
+                       else {
+                               if (state.state < AltosLib.ao_flight_pad && state.gps != null) {
+                                       show("%4.0f m", state.gps.alt);
+                                       set_label("Altitude");
+                               } else {
+                                       if (state.pad_alt == AltosRecord.MISSING)
+                                               hide();
+                                       else {
+                                               show("%4.0f m", state.pad_alt);
+                                               set_label("Pad Altitude");
+                                       }
+                               }
+                       }
                }
                public PadAlt (GridBagLayout layout, int y) {
                        super (layout, y, "Pad Altitude");
@@ -301,6 +382,7 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
                logging_ready.reset();
                gps_locked.reset();
                gps_ready.reset();
+               receiver_battery.reset();
                pad_lat.reset();
                pad_lon.reset();
                pad_alt.reset();
@@ -313,34 +395,23 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
                logging_ready.set_font();
                gps_locked.set_font();
                gps_ready.set_font();
+               receiver_battery.set_font();
                pad_lat.set_font();
                pad_lon.set_font();
                pad_alt.set_font();
        }
        
-       public void show(AltosState state, int crc_errors) {
-               battery.show(state, crc_errors);
-               if (state.drogue_sense == AltosRecord.MISSING)
-                       apogee.hide();
-               else
-                       apogee.show(state, crc_errors);
-               if (state.main_sense == AltosRecord.MISSING)
-                       main.hide();
-               else
-                       main.show(state, crc_errors);
-               logging_ready.show(state, crc_errors);
-               pad_alt.show(state, crc_errors);
-               if (state.gps != null && state.gps.connected) {
-                       gps_locked.show(state, crc_errors);
-                       gps_ready.show(state, crc_errors);
-                       pad_lat.show(state, crc_errors);
-                       pad_lon.show(state, crc_errors);
-               } else {
-                       gps_locked.hide();
-                       gps_ready.hide();
-                       pad_lat.hide();
-                       pad_lon.hide();
-               }
+       public void show(AltosState state, AltosListenerState listener_state) {
+               battery.show(state, listener_state);
+               apogee.show(state, listener_state);
+               main.show(state, listener_state);
+               logging_ready.show(state, listener_state);
+               pad_alt.show(state, listener_state);
+               receiver_battery.show(state, listener_state);
+               gps_locked.show(state, listener_state);
+               gps_ready.show(state, listener_state);
+               pad_lat.show(state, listener_state);
+               pad_lon.show(state, listener_state);
        }
 
        public AltosPad() {
@@ -364,8 +435,10 @@ public class AltosPad extends JComponent implements AltosFlightDisplay {
                logging_ready = new LoggingReady(layout, 3);
                gps_locked = new GPSLocked(layout, 4);
                gps_ready = new GPSReady(layout, 5);
-               pad_lat = new PadLat(layout, 6);
-               pad_lon = new PadLon(layout, 7);
-               pad_alt = new PadAlt(layout, 8);
+               receiver_battery = new ReceiverBattery(layout, 6);
+               pad_lat = new PadLat(layout, 7);
+               pad_lon = new PadLon(layout, 8);
+               pad_alt = new PadAlt(layout, 9);
+               show(null, null);
        }
 }
index f614eae65fc526370b1508b45a6477cd9ccd077d..23085f3ec5e270645b8c44760c34fb9202d61271 100644 (file)
@@ -220,6 +220,16 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
                return pngfile.toString();
        }
 
+       public void initAndFinishMapAsync (final AltosSiteMapTile tile, final Point offset) {
+               Thread thread = new Thread() {
+                               public void run() {
+                                       initMap(offset);
+                                       finishTileLater(tile, offset);
+                               }
+                       };
+               thread.start();
+       }
+
        public void setBaseLocation(double lat, double lng) {
                for (Point k : mapTiles.keySet()) {
                        AltosSiteMapTile tile = mapTiles.get(k);
@@ -264,7 +274,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
                initMaps(lat, lon);
                scrollRocketToVisible(pt(lat, lon));
        }
-       public void show(final AltosState state, final int crc_errors) {
+       public void show(final AltosState state, final AltosListenerState listener_state) {
                // if insufficient gps data, nothing to update
                if (!state.gps.locked && state.gps.nsat < 4)
                        return;
@@ -294,7 +304,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
                        Point2D.Double ref, lref;
                        ref = translatePoint(pt, tileCoordOffset(offset));
                        lref = translatePoint(last_pt, tileCoordOffset(offset));
-                       tile.show(state, crc_errors, lref, ref);
+                       tile.show(state, listener_state, lref, ref);
                        if (0 <= ref.x && ref.x < px_size)
                                if (0 <= ref.y && ref.y < px_size)
                                        in_any = true;
@@ -307,9 +317,8 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
                        lref = translatePoint(last_pt, tileCoordOffset(offset));
 
                        AltosSiteMapTile tile = createTile(offset);
-                       tile.show(state, crc_errors, lref, ref);
-                       initMap(offset);
-                       finishTileLater(tile, offset);
+                       tile.show(state, listener_state, lref, ref);
+                       initAndFinishMapAsync(tile, offset);
                }
 
                scrollRocketToVisible(pt);
@@ -370,8 +379,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
                                if (mapTiles.containsKey(offset))
                                        continue;
                                AltosSiteMapTile tile = createTile(offset);
-                               initMap(offset);
-                               finishTileLater(tile, offset);
+                               initAndFinishMapAsync(tile, offset);
                        }
                }
        }
index 617ed4a9a3331193271b03f4dd4b56b6eb406eb3..40c8ff6bf0dc078beff6f8b5016f8910ffe2f4c4 100644 (file)
@@ -19,6 +19,7 @@ package altosui;
 
 import javax.swing.*;
 import javax.imageio.ImageIO;
+import java.awt.image.*;
 import java.io.*;
 import java.net.URL;
 import java.net.URLConnection;
@@ -87,7 +88,14 @@ public class AltosSiteMapCache extends JLabel {
                }
 
                try {
-                       return new ImageIcon(ImageIO.read(pngfile));
+                       BufferedImage   img;
+
+                       img = ImageIO.read(pngfile);
+                       if (img == null) {
+                               System.out.printf("# Can't read pngfile %s\n", pngfile);
+                               return null;
+                       }
+                       return new ImageIcon(img);
                } catch (IOException e) {
                        System.out.printf("# IO error trying to load %s\n", pngfile);
                        return null;
index 10e65bcda81ed5410d9056fc1434572528a1a47a..365e4b6c3c36846a96e9fdc91ffed0fec6e072af 100644 (file)
@@ -56,7 +56,7 @@ public class AltosSiteMapTile extends JLayeredPane {
 
        private boolean drawn_landed_circle = false;
        private boolean drawn_boost_circle = false;
-       public synchronized void show(AltosState state, int crc_errors,
+       public synchronized void show(AltosState state, AltosListenerState listener_state,
                                      Point2D.Double last_pt, Point2D.Double pt)
        {
                if (0 <= state.state && state.state < stateColors.length) {
index a8292c648cf9e80d07f7f77e31ec315105ede523..d59e308248000cf98c3c16d358f7770337710463 100644 (file)
@@ -153,7 +153,7 @@ FIRMWARE=$(FIRMWARE_TM) $(FIRMWARE_TELEMINI) $(FIRMWARE_TD)
 ALTUSMETRUM_DOC=$(top_srcdir)/doc/altusmetrum.pdf
 ALTOS_DOC=$(top_srcdir)/doc/altos.pdf
 TELEMETRY_DOC=$(top_srcdir)/doc/telemetry.pdf
-TEMPLATE_DOC=$(top_srcdir)/doc/telemetrum-outline.pdf $(top_srcdir)/doc/megametrum-outline.pdf
+TEMPLATE_DOC=$(top_srcdir)/doc/telemetrum-outline.pdf $(top_srcdir)/doc/telemega-outline.pdf
 
 DOC=$(ALTUSMETRUM_DOC) $(ALTOS_DOC) $(TELEMETRY_DOC) $(TEMPLATE_DOC)
 
index cde54b416459614d1039aef2067f5813c9c65b66..9886e4a218b2a07f36799097c58a05dee3129ed7 100644 (file)
@@ -131,7 +131,7 @@ Section "Documentation"
        File "../doc/altos.pdf"
        File "../doc/telemetry.pdf"
        File "../doc/telemetrum-outline.pdf"
-       File "../doc/megametrum-outline.pdf"
+       File "../doc/telemega-outline.pdf"
 SectionEnd
 
 Section "Uninstaller"
index a54965977cacd1903c103465aa3559ef3b89682a..0f6cbd10e2b63dcc9a5d552f9b122655633decb0 100644 (file)
@@ -72,11 +72,12 @@ public class AltosUSBDevice  extends altos_device implements AltosDevice {
                        return matchProduct(AltosUILib.product_teledongle) ||
                                matchProduct(AltosUILib.product_teleterra) ||
                                matchProduct(AltosUILib.product_telebt) ||
-                               matchProduct(AltosUILib.product_megadongle);
+                               matchProduct(AltosUILib.product_telemega);
 
                if (want_product == AltosUILib.product_altimeter)
                        return matchProduct(AltosUILib.product_telemetrum) ||
-                               matchProduct(AltosUILib.product_megametrum);
+                               matchProduct(AltosUILib.product_telemega) ||
+                               matchProduct(AltosUILib.product_telegps);
 
                int have_product = getProduct();
 
@@ -109,4 +110,4 @@ public class AltosUSBDevice  extends altos_device implements AltosDevice {
 
                return device_list;
        }
-}
\ No newline at end of file
+}
diff --git a/ao-bringup/megametrum.cfg b/ao-bringup/megametrum.cfg
deleted file mode 100644 (file)
index e95c6f2..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-# openocd config for MegaMetrum using the Olimex ARM-USB-OCD dongle
-
-source /opt/stm32/share/openocd/scripts/interface/olimex-arm-usb-ocd.cfg
-source /opt/stm32/share/openocd/scripts/target/stm32l.cfg
diff --git a/ao-bringup/megametrum.gdb b/ao-bringup/megametrum.gdb
deleted file mode 100644 (file)
index 964ae1f..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-target remote localhost:3333
-monitor poll
diff --git a/ao-bringup/telemega.cfg b/ao-bringup/telemega.cfg
new file mode 100644 (file)
index 0000000..f6b96c1
--- /dev/null
@@ -0,0 +1,4 @@
+# openocd config for TeleMega using the Olimex ARM-USB-OCD dongle
+
+source /opt/stm32/share/openocd/scripts/interface/olimex-arm-usb-ocd.cfg
+source /opt/stm32/share/openocd/scripts/target/stm32l.cfg
diff --git a/ao-bringup/telemega.gdb b/ao-bringup/telemega.gdb
new file mode 100644 (file)
index 0000000..964ae1f
--- /dev/null
@@ -0,0 +1,2 @@
+target remote localhost:3333
+monitor poll
index 3b8bd3e449fbd654b4e944dcc3c17754b6b74a7d..6c52721c8e3a765ec4d999a634c8161eb1fe6356 100755 (executable)
@@ -45,4 +45,4 @@ echo "Programming flash with cal value " $CAL_VALUE
 $AOLOAD -D 100 --cal $CAL_VALUE /usr/share/altos/telebt-v0.1*.ihx $SERIAL
 
 echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE
-echo "Unplug and replug USB, cu to the board, confirm freq and record power"
+echo "Unplug debug cable, power cycle, cu to the board, confirm freq and record power"
index e4df2e636d7638e366190671fdcde698f8cb812a..4600f1d62221bf73ffe348ce532b6423d99aa4a4 100644 (file)
@@ -1,3 +1,3 @@
 SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-list \
        ao-load ao-telem ao-stmload ao-send-telem ao-sky-flash \
-       ao-dumpflash ao-edit-telem
+       ao-dumpflash ao-edit-telem ao-dump-up
diff --git a/ao-tools/ao-dump-up/Makefile.am b/ao-tools/ao-dump-up/Makefile.am
new file mode 100644 (file)
index 0000000..94bb94a
--- /dev/null
@@ -0,0 +1,12 @@
+bin_PROGRAMS=ao-dump-up
+
+AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS) $(GNOME_CFLAGS)
+AO_DUMP_LOG_LIBS=$(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_dump_up_DEPENDENCIES = $(AO_DUMP_LOG_LIBS)
+
+ao_dump_up_LDADD=$(AO_DUMP_LOG_LIBS) $(LIBUSB_LIBS) $(GNOME_LIBS)
+
+ao_dump_up_SOURCES = ao-dump-up.c
+
+man_MANS = ao-dump-up.1
diff --git a/ao-tools/ao-dump-up/ao-dump-up.1 b/ao-tools/ao-dump-up/ao-dump-up.1
new file mode 100644 (file)
index 0000000..cfa81b4
--- /dev/null
@@ -0,0 +1,45 @@
+.\"
+.\" 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-DUMPLOG 1 "ao-dump-up" ""
+.SH NAME
+ao-dump-up \- Dump flight log from MicroPeak flight computer
+.SH SYNOPSIS
+.B "ao-dump-up"
+[\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
+.SH OPTIONS
+.TP
+\-T tty-device | --tty tty-device
+This selects which tty device ao-dump-up uses to communicate with
+the target device.
+.TP
+\-D AltOS-device | --device AltOS-device
+Search for a connected device. This forces the program to look
+for a specific USB device name.
+.SH DESCRIPTION
+.I ao-dump-up
+downloads a MicroPeak flight log from a connected MicroPeak USB adapter.
+.SH USAGE
+.I ao-dump-up
+connects to the specified target device and dumps the stored flight
+log.
+.SH AUTHOR
+Keith Packard
diff --git a/ao-tools/ao-dump-up/ao-dump-up.c b/ao-tools/ao-dump-up/ao-dump-up.c
new file mode 100644 (file)
index 0000000..6268dc8
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include "cc-usb.h"
+#include "cc.h"
+
+#define NUM_BLOCK      512
+
+static const struct option options[] = {
+       { .name = "tty", .has_arg = 1, .val = 'T' },
+       { .name = "device", .has_arg = 1, .val = 'D' },
+       { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+       fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>]\n", program);
+       exit(1);
+}
+
+static uint8_t
+log_checksum(int d[8])
+{
+       uint8_t sum = 0x5a;
+       int     i;
+
+       for (i = 0; i < 8; i++)
+               sum += (uint8_t) d[i];
+       return -sum;
+}
+
+static int get_nonwhite(struct cc_usb *cc, int timeout)
+{
+       int     c;
+
+       for (;;) {
+               c = cc_usb_getchar_timeout(cc, timeout);
+               putchar(c);
+               if (!isspace(c))
+                       return c;
+       }
+}
+
+static uint8_t
+get_hexc(struct cc_usb *cc)
+{
+       int     c = get_nonwhite(cc, 1000);
+
+       if ('0' <= c && c <= '9')
+               return c - '0';
+       if ('a' <= c && c <= 'f')
+               return c - 'a' + 10;
+       if ('A' <= c && c <= 'F')
+               return c - 'A' + 10;
+       fprintf(stderr, "Non-hex char '%c'\n", c);
+       exit(1);
+}
+
+static int file_crc;
+
+static const int POLY = 0x8408;
+
+static int
+log_crc(int crc, int b)
+{
+       int     i;
+
+       for (i = 0; i < 8; i++) {
+               if (((crc & 0x0001) ^ (b & 0x0001)) != 0)
+                       crc = (crc >> 1) ^ POLY;
+               else
+                       crc = crc >> 1;
+               b >>= 1;
+       }
+       return crc & 0xffff;
+}
+
+static uint8_t
+get_hex(struct cc_usb *cc)
+{
+       int     a = get_hexc(cc);
+       int     b = get_hexc(cc);
+       int     h = (a << 4) + b;
+
+       file_crc = log_crc(file_crc, h);
+       return h;
+}
+
+static int get_32(struct cc_usb *cc)
+{
+       int     v = 0;
+       int     i;
+       for (i = 0; i < 4; i++) {
+               v += get_hex(cc) << (i * 8);
+       }
+       return v;
+}
+
+static int get_16(struct cc_usb *cc)
+{
+       int     v = 0;
+       int     i;
+       for (i = 0; i < 2; i++) {
+               v += get_hex(cc) << (i * 8);
+       }
+       return v;
+}
+
+static int swap16(int i)
+{
+       return ((i << 8) & 0xff00) | ((i >> 8) & 0xff);
+}
+
+static int find_header(struct cc_usb *cc)
+{
+       for (;;) {
+               if (get_nonwhite(cc, 0) == 'M' && get_nonwhite(cc, 1000) == 'P')
+                       return 1;
+       }
+}
+
+static const char *state_names[] = {
+       "startup",
+       "idle",
+       "pad",
+       "boost",
+       "fast",
+       "coast",
+       "drogue",
+       "main",
+       "landed",
+       "invalid"
+};
+
+
+int
+main (int argc, char **argv)
+{
+       struct cc_usb   *cc;
+       char            *tty = NULL;
+       char            *device = NULL;
+       int             c;
+       char            line[8192];
+       int             nsamples;
+       int             i;
+       int             crc;
+       int             current_crc;
+
+       while ((c = getopt_long(argc, argv, "T:D:", options, NULL)) != -1) {
+               switch (c) {
+               case 'T':
+                       tty = optarg;
+                       break;
+               case 'D':
+                       device = optarg;
+                       break;
+               default:
+                       usage(argv[0]);
+                       break;
+               }
+       }
+       if (!tty)
+               tty = cc_usbdevs_find_by_arg(device, "FT230X Basic UART");
+       if (!tty)
+               tty = getenv("ALTOS_TTY");
+       if (!tty)
+               tty="/dev/ttyUSB0";
+       cc = cc_usb_open(tty);
+       if (!cc)
+               exit(1);
+       find_header(cc);
+       file_crc = 0xffff;
+       get_32(cc);     /* ground pressure */
+       get_32(cc);     /* min pressure */
+       nsamples = get_16(cc);  /* nsamples */
+       for (i = 0; i < nsamples; i++)
+               get_16(cc);     /* sample i */
+       current_crc = swap16(~file_crc & 0xffff);
+       crc = get_16(cc);       /* crc */
+       putchar ('\n');
+       if (crc == current_crc)
+               printf("CRC valid\n");
+       else
+               printf("CRC invalid\n");
+       cc_usb_close(cc);
+       exit (0);
+}
index e7fc8e268712c5251aa1cbf126d6c13b76b7efe6..d2dae5a7a14068cbdcc5e21552bc64e61f79b31e 100644 (file)
@@ -24,6 +24,7 @@
 #include "cc.h"
 
 static const struct option options[] = {
+       { .name = "crc", .has_arg = 0, .val = 'c' },
        { 0, 0, 0, 0},
 };
 
@@ -44,8 +45,12 @@ main (int argc, char **argv)
        char *s;
        FILE *file;
        int serial;
-       while ((c = getopt_long(argc, argv, "", options, NULL)) != -1) {
+       int ignore_crc = 0;
+       while ((c = getopt_long(argc, argv, "c", options, NULL)) != -1) {
                switch (c) {
+               case 'c':
+                       ignore_crc = 1;
+                       break;
                default:
                        usage(argv[0]);
                        break;
@@ -74,7 +79,7 @@ main (int argc, char **argv)
                                printf ("serial %5d rssi %d status %02x tick %5d type %3d ",
                                        telem.generic.serial, rssi, telem.generic.status,
                                        telem.generic.tick, telem.generic.type);
-                               if ((telem.generic.status & (1 << 7)) == 0) {
+                               if (!ignore_crc && (telem.generic.status & (1 << 7)) == 0) {
                                        printf ("CRC error\n");
                                        continue;
                                }
index 621a15c1722e01536f6e5506e38ee5a9a424784f..485583f98b3897c1816547ffc4b7ae95ee32a6b1 100644 (file)
@@ -258,10 +258,10 @@ cc_usb_printf(struct cc_usb *cc, char *format, ...)
 }
 
 int
-cc_usb_getchar(struct cc_usb *cc)
+cc_usb_getchar_timeout(struct cc_usb *cc, int timeout)
 {
        while (cc->in_pos == cc->in_count) {
-               if (_cc_usb_sync(cc, 5000) < 0) {
+               if (_cc_usb_sync(cc, timeout) < 0) {
                        fprintf(stderr, "USB link timeout\n");
                        exit(1);
                }
@@ -269,6 +269,12 @@ cc_usb_getchar(struct cc_usb *cc)
        return cc->in_buf[cc->in_pos++];
 }
 
+int
+cc_usb_getchar(struct cc_usb *cc)
+{
+       return cc_usb_getchar_timeout(cc, 5000);
+}
+
 void
 cc_usb_getline(struct cc_usb *cc, char *line, int max)
 {
@@ -424,6 +430,8 @@ cc_usb_open(char *tty)
        tcgetattr(cc->fd, &termios);
        save_termios = termios;
        cfmakeraw(&termios);
+       cfsetospeed(&termios, B9600);
+       cfsetispeed(&termios, B9600);
        tcsetattr(cc->fd, TCSAFLUSH, &termios);
        cc_usb_printf(cc, "\nE 0\nm 0\n");
        do {
index e90e1195b100f34a5858bb8b6e2500609e66fe95..f11934560a917119a2b6a0d2377e23a412bf1cc5 100644 (file)
@@ -53,6 +53,9 @@ cc_usb_sync(struct cc_usb *cc);
 void
 cc_queue_read(struct cc_usb *cc, uint8_t *buf, int len);
 
+int
+cc_usb_getchar_timeout(struct cc_usb *cc, int timeout);
+
 int
 cc_usb_getchar(struct cc_usb *cc);
 
index a19e231c9fe724cd4b473d6f7ca3c6037acdd850..95bfa244b6a5f9dfe0fa567b992d02221e47f149 100644 (file)
@@ -132,11 +132,23 @@ usb_tty(char *sys)
                        /* Check for tty/ttyACMx style names
                         */
                        tty_dir = cc_fullname(endpoint_full, "tty");
-                       free(endpoint_full);
                        ntty = scandir(tty_dir, &namelist,
                                       dir_filter_tty,
                                       alphasort);
                        free (tty_dir);
+                       if (ntty > 0) {
+                               tty = cc_fullname("/dev", namelist[0]->d_name);
+                               free(endpoint_full);
+                               free(namelist);
+                               return tty;
+                       }
+
+                       /* Check for ttyACMx style names
+                        */
+                       ntty = scandir(endpoint_full, &namelist,
+                                      dir_filter_tty,
+                                      alphasort);
+                       free(endpoint_full);
                        if (ntty > 0) {
                                tty = cc_fullname("/dev", namelist[0]->d_name);
                                free(namelist);
@@ -197,6 +209,15 @@ dir_filter_dev(const struct dirent *d)
        return 1;
 }
 
+static int
+is_am(int idVendor, int idProduct) {
+       if (idVendor == 0xfffe)
+               return 1;
+       if (idVendor == 0x0403 && idProduct == 0x6015)
+               return 1;
+       return 0;
+}
+
 struct cc_usbdevs *
 cc_usbdevs_scan(void)
 {
@@ -220,7 +241,7 @@ cc_usbdevs_scan(void)
                dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
                dev = usb_scan_device(dir);
                free(dir);
-               if (dev->idVendor == 0xfffe && dev->tty) {
+               if (is_am(dev->idVendor, dev->idProduct) && dev->tty) {
                        if (devs->dev)
                                devs->dev = realloc(devs->dev,
                                                    (devs->ndev + 1) * sizeof (struct usbdev *));
index 977678f39d740e932a4eb72c921b0200c943cc5a..f54a8b4c9480ef70e90265edb1ab0097718be1a5 100644 (file)
@@ -223,6 +223,7 @@ ao-tools/ao-send-telem/Makefile
 ao-tools/ao-sky-flash/Makefile
 ao-tools/ao-dumpflash/Makefile
 ao-tools/ao-edit-telem/Makefile
+ao-tools/ao-dump-up/Makefile
 ao-utils/Makefile
 src/Version
 ])
index 3ac75ad424cc8c6a23b9019db8c4cfd2c9d494e0..dcdb776345f07fc3988f2e3d8b6562aa001e3b5b 100644 (file)
@@ -7,4 +7,4 @@ doc/telemetry.pdf
 doc/altos.html
 doc/altos.pdf
 doc/telemetrum-outline.pdf
-doc/megametrum-outline.pdf
+doc/telemega-outline.pdf
index c301adde2b9860f7e3ad3d2c1f1c3a547688ec07..5af94725603705ccc429beac72dadf272a40fe88 100644 (file)
@@ -50,7 +50,7 @@
            STM32L series from ST Microelectronics. This ARM Cortex-M3
            based microcontroller offers low power consumption and a
            wide variety of built-in peripherals. Altus Metrum uses
-           this in the MegaMetrum, MegaDongle and TeleLCO projects.
+           this in the TeleMega, MegaDongle and TeleLCO projects.
          </para>
        </listitem>
        <listitem>
index c5c08d4e0e1a78073361210bc49ddb135823c2c6..294f30ac08bab770e821e445a8567d199eb15234 100644 (file)
@@ -3,7 +3,7 @@
   "/usr/share/xml/docbook/schema/dtd/4.5/docbookx.dtd">
 <book>
   <title>The Altus Metrum System</title>
-  <subtitle>An Owner's Manual for TeleMetrum, TeleMini and TeleDongle Devices</subtitle>
+  <subtitle>An Owner's Manual for TeleMetrum, TeleMini, TeleDongle and TeleBT Devices</subtitle>
   <bookinfo>
     <author>
       <firstname>Bdale</firstname>
       </para>
     </legalnotice>
     <revhistory>
+      <revision>
+       <revnumber>1.2</revnumber>
+       <date>14 April 2013</date>
+       <revremark>
+         Updated for software version 1.2. Version 1.2 adds support
+         for TeleBT and AltosDroid. It also adds a few minor features
+         and fixes a few minor bugs in AltosUI and the AltOS firmware.
+       </revremark>
+      </revision>
       <revision>
        <revnumber>1.1.1</revnumber>
        <date>16 September 2012</date>
@@ -128,14 +137,21 @@ NAR #88757, TRA #12200
       air-frame.
     </para>
     <para>
-      Complementing TeleMetrum and TeleMini is TeleDongle, a USB to RF 
-      interface for communicating with the altimeters.  Combined with your 
-      choice of antenna and
-      notebook computer, TeleDongle and our associated user interface software
-      form a complete ground station capable of logging and displaying in-flight
-      telemetry, aiding rocket recovery, then processing and archiving flight
+      TeleDongle was our first ground station, providing a USB to RF
+      interfaces for communicating with the altimeters. Combined with
+      your choice of antenna and notebook computer, TeleDongle and our
+      associated user interface software form a complete ground
+      station capable of logging and displaying in-flight telemetry,
+      aiding rocket recovery, then processing and archiving flight
       data for analysis and review.
     </para>
+    <para>
+      For a slightly more portable ground station experience that also
+      provides direct rocket recovery support, TeleBT offers flight
+      monitoring and data logging using a Bluetooth connection between
+      the receiver and an Android device that has the Altos Droid
+      application installed from the Google Play store.
+    </para>
     <para>
       More products will be added to the Altus Metrum family over time, and
       we currently envision that this will be a single, comprehensive manual
@@ -197,6 +213,16 @@ NAR #88757, TRA #12200
       The latest version may always be downloaded from
       <ulink url="http://altusmetrum.org/AltOS"/>.
     </para>
+    <para>
+      If you're using a TeleBT instead of the TeleDongle, you'll want
+      to go install the Altos Droid application from the Google Play
+      store. You don't need a data plan to use Altos Droid, but
+      without network access, the Map view will be less useful as it
+      won't contain any map data. You can also use TeleBT connected
+      over USB with your laptop computer; it acts exactly like a
+      TeleDongle. Anywhere this manual talks about TeleDongle, you can
+      also read that as 'and TeleBT when connected via USB'.
+    </para>
   </chapter>
   <chapter>
     <title>Handling Precautions</title>
@@ -364,15 +390,15 @@ NAR #88757, TRA #12200
         flights, do what makes sense.
       </para>
       <para>
-        If idle mode is entered, you will hear an audible "di-dit" or see 
-        two short flashes ("I" for idle), and the flight state machine is 
-        disengaged, thus no ejection charges will fire.  The altimeters also 
-        listen for the radio link when in idle mode for requests sent via 
-        TeleDongle.  Commands can be issued to a TeleMetrum in idle mode 
-        over either
-        USB or the radio link equivalently. TeleMini only has the radio link.
-        Idle mode is useful for configuring the altimeter, for extracting data
-        from the on-board storage chip after flight, and for ground testing
+        If idle mode is entered, you will hear an audible "di-dit" or
+        see two short flashes ("I" for idle), and the flight state
+        machine is disengaged, thus no ejection charges will fire.
+        The altimeters also listen for the radio link when in idle
+        mode for requests sent via TeleDongle.  Commands can be issued
+        to a TeleMetrum in idle mode over either USB or the radio link
+        equivalently. TeleMini only has the radio link.  Idle mode is
+        useful for configuring the altimeter, for extracting data from
+        the on-board storage chip after flight, and for ground testing
         pyro charges.
       </para>
       <para>
@@ -443,11 +469,12 @@ NAR #88757, TRA #12200
     <section>
       <title>Controlling An Altimeter Over The Radio Link</title>
       <para>
-        One of the unique features of the Altus Metrum system is
-        the ability to create a two way command link between TeleDongle
-        and an altimeter using the digital radio transceivers built into
-        each device. This allows you to interact with the altimeter from
-        afar, as if it were directly connected to the computer.
+        One of the unique features of the Altus Metrum system is the
+        ability to create a two way command link between TeleDongle
+        and an altimeter using the digital radio transceivers
+        built into each device. This allows you to interact with the
+        altimeter from afar, as if it were directly connected to the
+        computer.
       </para>
       <para>
         Any operation which can be performed with TeleMetrum can
@@ -1581,6 +1608,151 @@ NAR #88757, TRA #12200
       </para>
     </section>
   </chapter>
+  <chapter>
+    <title>AltosDroid</title>
+    <para>
+      AltosDroid provides the same flight monitoring capabilities as
+      AltosUI, but runs on Android devices and is designed to connect
+      to a TeleBT receiver over Bluetoothâ„¢. Altos Droid monitors
+      telemetry data, logging it to internal storage in the Android
+      device, and presents that data in a UI the same way the 'Monitor
+      Flight' window does in AltosUI.
+    </para>
+    <para>
+      This manual will explain how to configure AltosDroid, connect
+      to TeleBT, operate the flight monitoring interface and describe
+      what the displayed data means.
+    </para>
+    <section>
+      <title>Installing AltosDroid</title>
+      <para>
+       AltosDroid is included in the Google Play store. To install
+       it on your Android device, open open the Google Play Store
+       application and search for "altosdroid". Make sure you don't
+       have a space between "altos" and "droid" or you probably won't
+       find what you want. That should bring you to the right page
+       from which you can download and install the application.
+      </para>
+    </section>
+    <section>
+      <title>Connecting to TeleBT</title>
+      <para>
+       Press the Android 'Menu' button or soft-key to see the
+       configuration options available. Select the 'Connect a device'
+       option and then the 'Scan for devices' entry at the bottom to
+       look for your TeleBT device. Select your device, and when it
+       asks for the code, enter '1234'.
+      </para>
+      <para>
+       Subsequent connections will not require you to enter that
+       code, and your 'paired' device will appear in the list without
+       scanning.
+      </para>
+    </section>
+    <section>
+      <title>Configuring AltosDroid</title>
+      <para>
+       The only configuration option available for AltosDroid is
+       which frequency to listen on. Press the Android 'Menu' button
+       or soft-key and pick the 'Select radio frequency' entry. That
+       brings up a menu of pre-set radio frequencies; pick the one
+       which matches your altimeter.
+      </para>
+    </section>
+    <section>
+      <title>Altos Droid Flight Monitoring</title>
+      <para>
+       Altos Droid is designed to mimic the AltosUI flight monitoring
+       display, providing separate tabs for each stage of your rocket
+       flight along with a tab containing a map of the local area
+       with icons marking the current location of the altimeter and
+       the Android device.
+      </para>
+      <section>
+       <title>Pad</title>
+        <para>
+          The 'Launch Pad' tab shows information used to decide when the
+          rocket is ready for flight. The first elements include red/green
+          indicators, if any of these is red, you'll want to evaluate
+          whether the rocket is ready to launch:
+          <itemizedlist>
+            <listitem>
+              <para>
+                Battery Voltage. This indicates whether the Li-Po battery
+                powering the TeleMetrum has sufficient charge to last for
+                the duration of the flight. A value of more than
+                3.7V is required for a 'GO' status.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                Apogee Igniter Voltage. This indicates whether the apogee
+                igniter has continuity. If the igniter has a low
+                resistance, then the voltage measured here will be close
+                to the Li-Po battery voltage. A value greater than 3.2V is
+                required for a 'GO' status.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                Main Igniter Voltage. This indicates whether the main
+                igniter has continuity. If the igniter has a low
+                resistance, then the voltage measured here will be close
+                to the Li-Po battery voltage. A value greater than 3.2V is
+                required for a 'GO' status.
+              </para>
+            </listitem>
+           <listitem>
+             <para>
+               On-board Data Logging. This indicates whether there is
+               space remaining on-board to store flight data for the
+               upcoming flight. If you've downloaded data, but failed
+               to erase flights, there may not be any space
+               left. TeleMetrum can store multiple flights, depending
+               on the configured maximum flight log size. TeleMini
+               stores only a single flight, so it will need to be
+               downloaded and erased after each flight to capture
+               data. This only affects on-board flight logging; the
+               altimeter will still transmit telemetry and fire
+               ejection charges at the proper times.
+             </para>
+           </listitem>
+            <listitem>
+              <para>
+                GPS Locked. For a TeleMetrum device, this indicates whether the GPS receiver is
+                currently able to compute position information. GPS requires
+                at least 4 satellites to compute an accurate position.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                GPS Ready. For a TeleMetrum device, this indicates whether GPS has reported at least
+                10 consecutive positions without losing lock. This ensures
+                that the GPS receiver has reliable reception from the
+                satellites.
+              </para>
+            </listitem>
+          </itemizedlist>
+          <para>
+            The Launchpad tab also shows the computed launch pad position
+            and altitude, averaging many reported positions to improve the
+            accuracy of the fix.
+          </para>
+        </para>
+      </section>
+    </section>
+    <section>
+      <title>Downloading Flight Logs</title>
+      <para>
+       Altos Droid always saves every bit of telemetry data it
+       receives. To download that to a computer for use with AltosUI,
+       simply remove the SD card from your Android device, or connect
+       your device to your computer's USB port and browse the files
+       on that device. You will find '.telem' files in the TeleMetrum
+       directory that will work with AltosUI directly.
+      </para>
+    </section>
+  </chapter>
   <chapter>
     <title>Using Altus Metrum Products</title>
     <section>
diff --git a/doc/megametrum-outline.pdf b/doc/megametrum-outline.pdf
deleted file mode 100644 (file)
index f8fc26e..0000000
Binary files a/doc/megametrum-outline.pdf and /dev/null differ
diff --git a/doc/megametrum-outline.svg b/doc/megametrum-outline.svg
deleted file mode 100644 (file)
index e8d74d3..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="427.5"
-   height="270"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.48.3.1 r9886"
-   sodipodi:docname="megametrum-outline.svg">
-  <defs
-     id="defs4">
-    <marker
-       inkscape:stockid="Arrow2Lend"
-       orient="auto"
-       refY="0.0"
-       refX="0.0"
-       id="Arrow2Lend"
-       style="overflow:visible;">
-      <path
-         id="path3866"
-         style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
-         d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
-         transform="scale(1.1) rotate(180) translate(1,0)" />
-    </marker>
-    <inkscape:perspective
-       sodipodi:type="inkscape:persp3d"
-       inkscape:vp_x="0 : 526.18109 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_z="744.09448 : 526.18109 : 1"
-       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
-       id="perspective10" />
-  </defs>
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="2.1783851"
-     inkscape:cx="315.40175"
-     inkscape:cy="122.33575"
-     inkscape:document-units="in"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     inkscape:window-width="1527"
-     inkscape:window-height="1313"
-     inkscape:window-x="813"
-     inkscape:window-y="166"
-     inkscape:window-maximized="0"
-     units="in"
-     showguides="true"
-     inkscape:guide-bbox="true">
-    <sodipodi:guide
-       position="0,0"
-       orientation="0,427.5"
-       id="guide3005" />
-    <sodipodi:guide
-       position="427.5,0"
-       orientation="-270,0"
-       id="guide3007" />
-    <sodipodi:guide
-       position="427.5,270"
-       orientation="0,-427.5"
-       id="guide3009" />
-    <sodipodi:guide
-       position="0,270"
-       orientation="270,0"
-       id="guide3011" />
-    <inkscape:grid
-       type="xygrid"
-       id="grid3013"
-       empspacing="4"
-       visible="true"
-       enabled="true"
-       snapvisiblegridlinesonly="true"
-       units="in"
-       spacingx="0.025in"
-       spacingy="0.025in" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-782.35975)">
-    <rect
-       style="fill:none;stroke:#000000;stroke-width:0.54555845;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       id="rect2816"
-       width="291.95444"
-       height="111.95444"
-       x="90.272781"
-       y="850.13251" />
-    <g
-       inkscape:tile-y0="681.11218"
-       inkscape:tile-x0="90"
-       id="use3601"
-       transform="matrix(0.97131843,0,0,0.97528987,8.397686,191.32255)">
-      <path
-         sodipodi:type="arc"
-         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-         id="path3611"
-         sodipodi:cx="116"
-         sodipodi:cy="739.36218"
-         sodipodi:rx="17"
-         sodipodi:ry="18"
-         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
-         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
-      <path
-         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         d="m 95.625,692.36218 0,-11.25"
-         id="path3613"
-         sodipodi:nodetypes="cc"
-         inkscape:connector-curvature="0" />
-      <path
-         sodipodi:nodetypes="cc"
-         id="path3615"
-         d="m 90,686.73718 11.25,0"
-         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         inkscape:connector-curvature="0" />
-    </g>
-    <g
-       inkscape:tile-y0="681.11218"
-       inkscape:tile-x0="90"
-       id="use3603"
-       transform="matrix(0.97186116,0,0,0.97241431,278.34851,193.3134)">
-      <path
-         sodipodi:type="arc"
-         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-         id="path3619"
-         sodipodi:cx="116"
-         sodipodi:cy="739.36218"
-         sodipodi:rx="17"
-         sodipodi:ry="18"
-         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
-         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
-      <path
-         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         d="m 95.625,692.36218 0,-11.25"
-         id="path3621"
-         sodipodi:nodetypes="cc"
-         inkscape:connector-curvature="0" />
-      <path
-         sodipodi:nodetypes="cc"
-         id="path3623"
-         d="m 90,686.73718 11.25,0"
-         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         inkscape:connector-curvature="0" />
-    </g>
-    <g
-       inkscape:tile-y0="681.11218"
-       inkscape:tile-x0="90"
-       id="use3605"
-       transform="matrix(0.97475506,0,0,0.97241431,278.08835,283.31323)">
-      <path
-         sodipodi:type="arc"
-         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-         id="path3627"
-         sodipodi:cx="116"
-         sodipodi:cy="739.36218"
-         sodipodi:rx="17"
-         sodipodi:ry="18"
-         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
-         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
-      <path
-         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         d="m 95.625,692.36218 0,-11.25"
-         id="path3629"
-         sodipodi:nodetypes="cc"
-         inkscape:connector-curvature="0" />
-      <path
-         sodipodi:nodetypes="cc"
-         id="path3631"
-         d="m 90,686.73718 11.25,0"
-         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         inkscape:connector-curvature="0" />
-    </g>
-    <g
-       inkscape:tile-y0="681.11218"
-       inkscape:tile-x0="90"
-       id="use3607"
-       transform="matrix(0.97186116,0,0,0.97241431,8.3485628,283.31356)">
-      <path
-         sodipodi:type="arc"
-         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-         id="path3635"
-         sodipodi:cx="116"
-         sodipodi:cy="739.36218"
-         sodipodi:rx="17"
-         sodipodi:ry="18"
-         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
-         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
-      <path
-         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         d="m 95.625,692.36218 0,-11.25"
-         id="path3637"
-         sodipodi:nodetypes="cc"
-         inkscape:connector-curvature="0" />
-      <path
-         sodipodi:nodetypes="cc"
-         id="path3639"
-         d="m 90,686.73718 11.25,0"
-         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         inkscape:connector-curvature="0" />
-    </g>
-    <path
-       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow2Lend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       d="m 135,903.85975 157.5,0"
-       id="path2829"
-       sodipodi:nodetypes="cc"
-       inkscape:connector-curvature="0" />
-    <text
-       xml:space="preserve"
-       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Minion Pro;-inkscape-font-specification:Minion Pro"
-       x="888.10974"
-       y="-303.75"
-       id="text4236"
-       sodipodi:linespacing="125%"
-       transform="matrix(0,1,-1,0,0,0)"><tspan
-         sodipodi:role="line"
-         x="888.10974"
-         y="-303.75"
-         id="tspan4242">UP</tspan></text>
-  </g>
-</svg>
index 2120acb2942b3a1ee40c8f521703df244abb1056..96179d019340c46205f435cc380f4e9034ede523 100644 (file)
@@ -224,12 +224,17 @@ NAR #88757, TRA #12200
        </listitem>
        <listitem>
          <para>
-           The MicroPeak USB adapter has a small phototransistor on the 
-           end of the board furthest from the USB connector. Locate
-           this and place the LED on the MicroPeak right over
-           it. Turn on the MicroPeak board and adjust the position
-           until the blue LED on the MicroPeak USB adapter blinks in
-           time with the orange LED on the MicroPeak board.
+           The MicroPeak USB adapter has a small phototransistor on
+           the end of the board furthest from the USB
+           connector. Locate this and place the LED on the MicroPeak
+           directly in contact with it. The MicroPeak LED and the
+           MicroPeak USB adapter photo need to be touching—even a
+           millimeters of space between them will reduce the light
+           intensity from the LED enough that the phototransistor
+           will not sense it. Turn on the MicroPeak board and adjust
+           the position until the blue LED on the MicroPeak USB
+           adapter blinks in time with the orange LED on the
+           MicroPeak board.
          </para>
        </listitem>
        <listitem>
index 610fa1a218f07d026e2189f495cce89dce430f6d..b254c7b5affeaaa236ac2d8e3eb69333c628c5f3 100644 (file)
@@ -41,7 +41,7 @@
        raised, breaking the Descent tab contents.
       </listitem>
       <listitem>
-       Add preliminary MegaMetrum support, including configuration,
+       Add preliminary TeleMega support, including configuration,
        data download and analysis.
       </listitem>
       <listitem>
diff --git a/doc/telemega-outline.pdf b/doc/telemega-outline.pdf
new file mode 100644 (file)
index 0000000..f8fc26e
Binary files /dev/null and b/doc/telemega-outline.pdf differ
diff --git a/doc/telemega-outline.svg b/doc/telemega-outline.svg
new file mode 100644 (file)
index 0000000..e8d74d3
--- /dev/null
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="427.5"
+   height="270"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.3.1 r9886"
+   sodipodi:docname="megametrum-outline.svg">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow2Lend"
+       style="overflow:visible;">
+      <path
+         id="path3866"
+         style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+         d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+         transform="scale(1.1) rotate(180) translate(1,0)" />
+    </marker>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.1783851"
+     inkscape:cx="315.40175"
+     inkscape:cy="122.33575"
+     inkscape:document-units="in"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1527"
+     inkscape:window-height="1313"
+     inkscape:window-x="813"
+     inkscape:window-y="166"
+     inkscape:window-maximized="0"
+     units="in"
+     showguides="true"
+     inkscape:guide-bbox="true">
+    <sodipodi:guide
+       position="0,0"
+       orientation="0,427.5"
+       id="guide3005" />
+    <sodipodi:guide
+       position="427.5,0"
+       orientation="-270,0"
+       id="guide3007" />
+    <sodipodi:guide
+       position="427.5,270"
+       orientation="0,-427.5"
+       id="guide3009" />
+    <sodipodi:guide
+       position="0,270"
+       orientation="270,0"
+       id="guide3011" />
+    <inkscape:grid
+       type="xygrid"
+       id="grid3013"
+       empspacing="4"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true"
+       units="in"
+       spacingx="0.025in"
+       spacingy="0.025in" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-782.35975)">
+    <rect
+       style="fill:none;stroke:#000000;stroke-width:0.54555845;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect2816"
+       width="291.95444"
+       height="111.95444"
+       x="90.272781"
+       y="850.13251" />
+    <g
+       inkscape:tile-y0="681.11218"
+       inkscape:tile-x0="90"
+       id="use3601"
+       transform="matrix(0.97131843,0,0,0.97528987,8.397686,191.32255)">
+      <path
+         sodipodi:type="arc"
+         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="path3611"
+         sodipodi:cx="116"
+         sodipodi:cy="739.36218"
+         sodipodi:rx="17"
+         sodipodi:ry="18"
+         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+      <path
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         d="m 95.625,692.36218 0,-11.25"
+         id="path3613"
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3615"
+         d="m 90,686.73718 11.25,0"
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       inkscape:tile-y0="681.11218"
+       inkscape:tile-x0="90"
+       id="use3603"
+       transform="matrix(0.97186116,0,0,0.97241431,278.34851,193.3134)">
+      <path
+         sodipodi:type="arc"
+         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="path3619"
+         sodipodi:cx="116"
+         sodipodi:cy="739.36218"
+         sodipodi:rx="17"
+         sodipodi:ry="18"
+         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+      <path
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         d="m 95.625,692.36218 0,-11.25"
+         id="path3621"
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3623"
+         d="m 90,686.73718 11.25,0"
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       inkscape:tile-y0="681.11218"
+       inkscape:tile-x0="90"
+       id="use3605"
+       transform="matrix(0.97475506,0,0,0.97241431,278.08835,283.31323)">
+      <path
+         sodipodi:type="arc"
+         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="path3627"
+         sodipodi:cx="116"
+         sodipodi:cy="739.36218"
+         sodipodi:rx="17"
+         sodipodi:ry="18"
+         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+      <path
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         d="m 95.625,692.36218 0,-11.25"
+         id="path3629"
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3631"
+         d="m 90,686.73718 11.25,0"
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       inkscape:tile-y0="681.11218"
+       inkscape:tile-x0="90"
+       id="use3607"
+       transform="matrix(0.97186116,0,0,0.97241431,8.3485628,283.31356)">
+      <path
+         sodipodi:type="arc"
+         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="path3635"
+         sodipodi:cx="116"
+         sodipodi:cy="739.36218"
+         sodipodi:rx="17"
+         sodipodi:ry="18"
+         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+      <path
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         d="m 95.625,692.36218 0,-11.25"
+         id="path3637"
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3639"
+         d="m 90,686.73718 11.25,0"
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         inkscape:connector-curvature="0" />
+    </g>
+    <path
+       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow2Lend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="m 135,903.85975 157.5,0"
+       id="path2829"
+       sodipodi:nodetypes="cc"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Minion Pro;-inkscape-font-specification:Minion Pro"
+       x="888.10974"
+       y="-303.75"
+       id="text4236"
+       sodipodi:linespacing="125%"
+       transform="matrix(0,1,-1,0,0,0)"><tspan
+         sodipodi:role="line"
+         x="888.10974"
+         y="-303.75"
+         id="tspan4242">UP</tspan></text>
+  </g>
+</svg>
index 2a41ef80159e1813f7573672fe049a69d1c78a65..fc949c7020a2e8143f5c5f83238ebda3cb4b1003 100644 (file)
@@ -736,30 +736,36 @@ struct altos_file *
 altos_bt_open(struct altos_bt_device *device)
 {
        struct sockaddr_rc addr = { 0 };
-       int     s, status;
+       int     status, i;
        struct altos_file *file;
 
        file = calloc(1, sizeof (struct altos_file));
        if (!file)
                goto no_file;
-       file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-       if (file->fd < 0) {
-               altos_set_last_posix_error();
-               goto no_sock;
-       }
-
        addr.rc_family = AF_BLUETOOTH;
        addr.rc_channel = 1;
        str2ba(device->addr, &addr.rc_bdaddr);
 
-       status = connect(file->fd,
-                        (struct sockaddr *)&addr,
-                        sizeof(addr));
+       for (i = 0; i < 5; i++) {
+               file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+               if (file->fd < 0) {
+                       altos_set_last_posix_error();
+                       goto no_sock;
+               }
+
+               status = connect(file->fd,
+                                (struct sockaddr *)&addr,
+                                sizeof(addr));
+               if (status >= 0 || errno != EBUSY)
+                       break;
+               close(file->fd);
+               usleep(100 * 1000);
+       }
        if (status < 0) {
                altos_set_last_posix_error();
                goto no_link;
        }
-       sleep(1);
+       usleep(100 * 1000);
 
 #ifdef USE_POLL
        pipe(file->pipe);
@@ -768,7 +774,7 @@ altos_bt_open(struct altos_bt_device *device)
 #endif
        return file;
 no_link:
-       close(s);
+       close(file->fd);
 no_sock:
        free(file);
 no_file:
@@ -1319,6 +1325,7 @@ altos_open(struct altos_device *device)
        struct altos_file       *file = calloc (1, sizeof (struct altos_file));
        char    full_name[64];
        COMMTIMEOUTS timeouts;
+       int i;
 
        if (!file)
                return NULL;
@@ -1326,7 +1333,15 @@ altos_open(struct altos_device *device)
        strcpy(full_name, "\\\\.\\");
        strcat(full_name, device->path);
 
-       file->handle = open_serial(full_name);
+       file->handle = INVALID_HANDLE_VALUE;
+
+       for (i = 0; i < 5; i++) {
+               file->handle = open_serial(full_name);
+               if (file->handle != INVALID_HANDLE_VALUE)
+                       break;
+               Sleep(100);
+       }
+       
        if (file->handle == INVALID_HANDLE_VALUE) {
                free(file);
                return NULL;
index 6f0ca4f653b400ef5a817baed849d877bb14e2e0..bd6795f85f633fb8e573dac8eb02cbe3c444f472 100644 (file)
@@ -92,11 +92,14 @@ public class MicroDownload extends AltosUIDialog implements Runnable, ActionList
                        serial.close();
                        serial_thread.interrupt();
                }
+               setVisible(false);
        }
 
        public MicroDownload(MicroPeak owner, AltosDevice device) {
                super (owner, "Download MicroPeak Data", false);
 
+               int y = 0;
+
                GridBagConstraints c;
                Insets il = new Insets(4,4,4,4);
                Insets ir = new Insets(4,4,4,4);
@@ -108,7 +111,7 @@ public class MicroDownload extends AltosUIDialog implements Runnable, ActionList
                pane.setLayout(new GridBagLayout());
 
                c = new GridBagConstraints();
-               c.gridx = 0; c.gridy = 0;
+               c.gridx = 0; c.gridy = y;
                c.fill = GridBagConstraints.NONE;
                c.anchor = GridBagConstraints.LINE_START;
                c.insets = il;
@@ -116,29 +119,66 @@ public class MicroDownload extends AltosUIDialog implements Runnable, ActionList
                pane.add(device_label, c);
 
                c = new GridBagConstraints();
-               c.gridx = 1; c.gridy = 0;
+               c.gridx = 1; c.gridy = y;
                c.fill = GridBagConstraints.HORIZONTAL;
                c.weightx = 1;
                c.anchor = GridBagConstraints.LINE_START;
                c.insets = ir;
                JLabel device_value = new JLabel(device.toString());
                pane.add(device_value, c);
+               y++;
+
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = y;
+               c.gridwidth = GridBagConstraints.REMAINDER;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               JTextArea help_text = new JTextArea(
+
+                       "Locate the photo transistor on the MicroPeak USB adapter\n" +
+                       "and place the LED on the MicroPeak directly in contact\n" +
+                       "with it.\n" +
+                       "\n" +
+                       "The MicroPeak LED and the MicroPeak USB adapter\n" +
+                       "photo need to be touching—even a millimeters of space\n" +
+                       "between them will reduce the light intensity from the LED\n" +
+                       "enough that the phototransistor will not sense it.\n" +
+                       "\n" +
+                       "Turn on the MicroPeak board and adjust the position until\n" +
+                       "the blue LED on the MicroPeak USB adapter blinks in time\n" +
+                       "with the orange LED on the MicroPeak board.");
+
+               pane.add(help_text, c);
+               y++;
+
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = y;
+               c.gridwidth = 1;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               JLabel waiting_value = new JLabel("Waiting for MicroPeak data...");
+               pane.add(waiting_value, c);
 
                cancel = new JButton("Cancel");
                c = new GridBagConstraints();
                c.fill = GridBagConstraints.NONE;
                c.anchor = GridBagConstraints.CENTER;
-               c.gridx = 0; c.gridy = 1;
+               c.gridx = 1; c.gridy = y;
                c.gridwidth = GridBagConstraints.REMAINDER;
                Insets ic = new Insets(4,4,4,4);
                c.insets = ic;
                pane.add(cancel, c);
+               y++;
 
                cancel.addActionListener(this);
 
                pack();
                setLocationRelativeTo(owner);
                setVisible(true);
-               this.start();
+               start();
        }
 }
index 9e31e3ea914eb9e587310f17bc0072f95d001101..e271ddf3f3f9d9c4608e5664cd6fe5c1403c5c31 100644 (file)
@@ -18,7 +18,7 @@ SDCCDIRS=\
        telemetrum-v1.2 telemetrum-v1.1 telemetrum-v1.0 \
        teledongle-v0.2 teledongle-v0.1 \
        telemini-v1.0 telenano-v0.1 \
-       telebt-v0.0 telebt-v0.1 \
+       telebt-v1.0 \
        telemetrum-v0.1-sky telemetrum-v0.1-sirf \
        telelaunch-v0.1 tidongle test \
        teleterra-v0.2 teleshield-v0.1 \
@@ -29,7 +29,7 @@ AVRDIRS=\
        telescience-v0.1 telescience-pwm telepyro-v0.1 micropeak
 
 ARMDIRS=\
-       megametrum-v0.1 megadongle-v0.1 stm-bringup stm-demo telelco-v0.1 \
+       telemega-v0.1 megadongle-v0.1 stm-bringup stm-demo telelco-v0.1 \
        telescience-v0.2
 
 ifneq ($(shell which sdcc),)
index bfdc418a6ca2ae204c4f80aaa14726c67c0cf65c..ed76179b5ebeec1813edad23b2f10dc8a6e6624c 100644 (file)
@@ -153,6 +153,15 @@ ao_adc_isr(void) __interrupt 1
 #define GOT_ADC
 #endif /* TELEFIRE_V_0_1 */
 
+#ifdef TELEBT_V_1_0
+       a = (uint8_t __xdata *) (&ao_data_ring[ao_data_head].adc.batt);
+       a[0] = ADCL;
+       a[1] = ADCH;
+       if (0)
+               ;
+#define GOT_ADC
+#endif 
+
 #ifndef GOT_ADC
 #error No known ADC configuration set
 #endif
index 7ff49dd560b26d079712bfcf7a6c7150148043ae..548e87381525161692844c03b476defe1bf7e1e4 100644 (file)
@@ -65,6 +65,7 @@
 #define AO_PANIC_STACK         12      /* Stack overflow */
 #define AO_PANIC_SPI           13      /* SPI communication failure */
 #define AO_PANIC_CRASH         14      /* Processor crashed */
+#define AO_PANIC_BUFIO         15      /* Mis-using bufio API */
 #define AO_PANIC_SELF_TEST_CC1120      0x40 | 1        /* Self test failure */
 #define AO_PANIC_SELF_TEST_HMC5883     0x40 | 2        /* Self test failure */
 #define AO_PANIC_SELF_TEST_MPU6000     0x40 | 3        /* Self test failure */
@@ -93,7 +94,7 @@ extern volatile __data AO_TICK_TYPE ao_tick_count;
 #define AO_SEC_TO_TICKS(s)     ((s) * AO_HERTZ)
 
 /* Returns the current time in ticks */
-uint16_t
+AO_TICK_TYPE
 ao_time(void);
 
 /* Suspend the current task until ticks time has passed */
@@ -517,17 +518,28 @@ extern __xdata uint8_t    ao_radio_dma;
 #define AO_RADIO_STATUS_CRC_OK AO_FEC_DECODE_CRC_OK
 #endif
 
+#ifndef HAS_RADIO_RECV
+#define HAS_RADIO_RECV HAS_RADIO
+#endif
+#ifndef HAS_RADIO_XMIT
+#define HAS_RADIO_XMIT HAS_RADIO
+#endif
+
 void
 ao_radio_general_isr(void) ao_arch_interrupt(16);
 
+#if HAS_RADIO_XMIT
 void
 ao_radio_send(const __xdata void *d, uint8_t size) __reentrant;
+#endif
 
+#if HAS_RADIO_RECV
 uint8_t
 ao_radio_recv(__xdata void *d, uint8_t size) __reentrant;
 
 void
 ao_radio_recv_abort(void);
+#endif
 
 void
 ao_radio_test(uint8_t on);
@@ -535,7 +547,26 @@ ao_radio_test(uint8_t on);
 typedef int16_t (*ao_radio_fill_func)(uint8_t *buffer, int16_t len);
 
 void
-ao_radio_send_lots(ao_radio_fill_func fill);
+ao_radio_send_aprs(ao_radio_fill_func fill);
+
+/*
+ * ao_radio_pa
+ */
+
+#if HAS_RADIO_AMP
+void
+ao_radio_pa_on(void);
+
+void
+ao_radio_pa_off(void);
+
+void
+ao_radio_pa_init(void);
+#else
+#define ao_radio_pa_on()
+#define ao_radio_pa_off()
+#define ao_radio_pa_init()
+#endif
 
 /*
  * Compute the packet length as follows:
@@ -687,7 +718,7 @@ extern __xdata uint8_t ao_force_freq;
 #endif
 
 #define AO_CONFIG_MAJOR        1
-#define AO_CONFIG_MINOR        13
+#define AO_CONFIG_MINOR        14
 
 #define AO_AES_LEN 16
 
@@ -715,6 +746,12 @@ struct ao_config {
        struct ao_pyro  pyro[AO_PYRO_NUM];      /* minor version 12 */
 #endif
        uint16_t        aprs_interval;          /* minor version 13 */
+#if HAS_RADIO_POWER
+       uint8_t         radio_power;            /* minor version 14 */
+#endif
+#if HAS_RADIO_AMP
+       uint8_t         radio_amp;              /* minor version 14 */
+#endif
 };
 
 #define AO_IGNITE_MODE_DUAL            0
index 0aac16a678ad1a6296bb1c4278bf5985bfbc0576..73608a55c542923b9692458838f0e20f91106c3e 100644 (file)
@@ -47,6 +47,8 @@ __xdata uint8_t ao_config_mutex;
 #define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX       ((uint32_t) 192 * (uint32_t) 1024)
 #endif
 #endif
+#define AO_CONFIG_DEFAULT_RADIO_POWER          0x60
+#define AO_CONFIG_DEFAULT_RADIO_AMP            0
 
 #if HAS_EEPROM
 static void
@@ -141,6 +143,14 @@ _ao_config_get(void)
 #endif
                if (minor < 13)
                        ao_config.aprs_interval = 0;
+#if HAS_RADIO_POWER
+               if (minor < 14)
+                       ao_config.radio_power = AO_CONFIG_DEFAULT_RADIO_POWER;
+               #endif
+#if HAS_RADIO_AMP
+               if (minor  < 14)
+                       ao_config.radio_amp = AO_CONFIG_DEFAULT_RADIO_AMP;
+#endif
                ao_config.minor = AO_CONFIG_MINOR;
                ao_config_dirty = 1;
        }
@@ -210,6 +220,7 @@ ao_config_callsign_set(void) __reentrant
 }
 
 #if HAS_RADIO
+
 void
 ao_config_frequency_show(void) __reentrant
 {
@@ -227,7 +238,9 @@ ao_config_frequency_set(void) __reentrant
        ao_config.frequency = ao_cmd_lex_u32;
        ao_config_set_radio();
        _ao_config_edit_finish();
+#if HAS_RADIO_RECV
        ao_radio_recv_abort();
+#endif
 }
 #endif
 
@@ -521,6 +534,48 @@ ao_config_aprs_set(void)
 
 #endif /* HAS_APRS */
 
+#if HAS_RADIO_AMP
+
+void
+ao_config_radio_amp_show(void)
+{
+       printf ("Radio amp setting: %d\n", ao_config.radio_amp);
+}
+
+void
+ao_config_radio_amp_set(void)
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       _ao_config_edit_start();
+       ao_config.radio_amp = ao_cmd_lex_i;
+       _ao_config_edit_finish();
+}
+
+#endif
+
+#if HAS_RADIO_POWER
+
+void
+ao_config_radio_power_show(void)
+{
+       printf ("Radio power setting: %d\n", ao_config.radio_power);
+}
+
+void
+ao_config_radio_power_set(void)
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       _ao_config_edit_start();
+       ao_config.radio_power = ao_cmd_lex_i;
+       _ao_config_edit_finish();
+}
+
+#endif
+
 struct ao_config_var {
        __code char     *str;
        void            (*set)(void) __reentrant;
@@ -554,6 +609,14 @@ __code struct ao_config_var ao_config_vars[] = {
          ao_config_radio_enable_set, ao_config_radio_enable_show },
        { "f <cal>\0Radio calib (cal = rf/(xtal/2^16))",
          ao_config_radio_cal_set,      ao_config_radio_cal_show },
+#if HAS_RADIO_POWER
+       { "p <setting>\0Radio power setting (0-255)",
+         ao_config_radio_power_set,    ao_config_radio_power_show },
+#endif
+#if HAS_RADIO_AMP
+       { "d <setting>\0Radio amplifier setting (0-3)",
+         ao_config_radio_amp_set,      ao_config_radio_amp_show },
+#endif
 #endif /* HAS_RADIO */
 #if HAS_ACCEL
        { "a <+g> <-g>\0Accel calib (0 for auto)",
index 072a9e904c1e0411ab26c38353dccf53ab4ba7a3..c4f5559a11f8ea79b07d069e99ee60a2208fe58f 100644 (file)
@@ -18,7 +18,7 @@
 #include <ao_fec.h>
 #include <stdio.h>
 
-#ifdef MEGAMETRUM
+#ifdef TELEMEGA
 #include <ao.h>
 #endif
 
index 47891cabe9db8c3f878e85c9941d7b6c8828c5d4..e3af43079728033740513c6c7dc057c0eb313bec 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include "ao.h"
+#include "ao_log.h"
 
 void
 ao_gps_report_mega(void)
index 036d6f2de56c134ab445c85b5b709c7e019a5076..a68a40dddc0f4f8370c7edc7cd109408a1579af6 100644 (file)
@@ -43,7 +43,7 @@ extern __pdata enum ao_flight_state ao_log_state;
 #define AO_LOG_FORMAT_TINY             2       /* two byte state/baro records */
 #define AO_LOG_FORMAT_TELEMETRY                3       /* 32 byte ao_telemetry records */
 #define AO_LOG_FORMAT_TELESCIENCE      4       /* 32 byte typed telescience records */
-#define AO_LOG_FORMAT_MEGAMETRUM       5       /* 32 byte typed megametrum records */
+#define AO_LOG_FORMAT_TELEMEGA         5       /* 32 byte typed telemega records */
 #define AO_LOG_FORMAT_NONE             127     /* No log at all */
 
 extern __code uint8_t ao_log_format;
@@ -268,4 +268,7 @@ ao_log_data(__xdata struct ao_log_record *log) __reentrant;
 uint8_t
 ao_log_mega(__xdata struct ao_log_mega *log) __reentrant;
 
+void
+ao_log_flush(void);
+
 #endif /* _AO_LOG_H_ */
index e03687ada912d8352527179caa9117e8645bd630..abf953a6b7c051337eea644c0b0fe49758589141 100644 (file)
@@ -23,7 +23,7 @@
 static __xdata uint8_t ao_log_mutex;
 static __xdata struct ao_log_mega log;
 
-__code uint8_t ao_log_format = AO_LOG_FORMAT_MEGAMETRUM;
+__code uint8_t ao_log_format = AO_LOG_FORMAT_TELEMEGA;
 
 static uint8_t
 ao_log_csum(__xdata uint8_t *b) __reentrant
@@ -171,6 +171,8 @@ ao_log(void)
                }
 #endif
 
+               ao_log_flush();
+
                /* Wait for a while */
                ao_delay(AO_MS_TO_TICKS(100));
 
index 8d440e15410ff4bd7833bcd5da6e1380d8ba46f9..03aa48d87b54deb11ad33fb931839b8ea0482164 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include "ao.h"
+#include "ao_log.h"
 #include "ao_product.h"
 
 static __pdata uint16_t ao_telemetry_interval;
@@ -28,7 +29,7 @@ static __pdata uint16_t ao_aprs_time;
 #include <ao_aprs.h>
 #endif
 
-#if defined(MEGAMETRUM)
+#if defined(TELEMEGA)
 #define AO_SEND_MEGA   1
 #endif
 
@@ -306,12 +307,14 @@ ao_telemetry(void)
 #ifdef AO_SEND_ALL_BARO
                                ao_send_baro();
 #endif
+#if HAS_FLIGHT
 #ifdef AO_SEND_MEGA
                                ao_send_mega_sensor();
                                ao_send_mega_data();
 #else
                                ao_send_sensor();
 #endif
+#endif
 
 #if HAS_COMPANION
                                if (ao_companion_running)
index 03bcab051aa771134344c3e8084dc8cc4f9a6ad6..6ab61e6a3018f9d945bce57be25eb5176e251a3c 100644 (file)
@@ -593,7 +593,7 @@ void ao_aprs_send(void)
     tncIndex = 0;
     tncMode = TNC_TX_SYNC;
 
-    ao_radio_send_lots(tncFill);
+    ao_radio_send_aprs(tncFill);
 }
 
 /** @} */
index de1f31a3c53248d0c05a43abd2e974c2be360e77..3b6028a030f49abc5ef9cae9133b6bdc72659ae2 100644 (file)
@@ -302,7 +302,7 @@ ao_btm(void)
                while (!ao_btm_connected)
                        ao_sleep(&ao_btm_connected);
                while (ao_btm_connected) {
-                       ao_led_for(AO_LED_GREEN, AO_MS_TO_TICKS(20));
+                       ao_led_for(AO_BT_LED, AO_MS_TO_TICKS(20));
                        ao_delay(AO_SEC_TO_TICKS(3));
                }
        }
diff --git a/src/drivers/ao_bufio.c b/src/drivers/ao_bufio.c
new file mode 100644 (file)
index 0000000..c0fe604
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef AO_FAT_TEST
+#include "ao.h"
+#endif
+
+/* Include bufio commands */
+#ifndef AO_FAT_TEST
+#define BUFIO_COMMANDS 0
+#endif
+
+#include "ao_sdcard.h"
+#include "ao_bufio.h"
+
+#define AO_NUM_BUF             16
+#define AO_BUFSIZ              512
+
+struct ao_bufio {
+       uint32_t        block;
+       int16_t         seqno;
+       uint8_t         busy;   
+       uint8_t         dirty;
+};
+
+static struct ao_bufio ao_bufio[AO_NUM_BUF];
+static uint8_t         ao_buffer[AO_NUM_BUF][AO_BUFSIZ];
+static int16_t         ao_seqno;
+static uint8_t         ao_bufio_mutex;
+
+#if 0
+#define DBG(...) printf(__VA_ARGS__)
+#else
+#define DBG(...)
+#endif
+
+static inline void
+ao_bufio_lock(void)
+{
+       ao_mutex_get(&ao_bufio_mutex);
+}
+
+static inline void
+ao_bufio_unlock(void)
+{
+       ao_mutex_put(&ao_bufio_mutex);
+}
+
+static inline int16_t
+ao_seqno_age(int16_t s)
+{
+       return ao_seqno - s;
+}
+
+static inline int16_t
+ao_seqno_next(void)
+{
+       return ++ao_seqno;
+}
+
+static inline int
+ao_seqno_older(int16_t a, int16_t b)
+{
+       return ao_seqno_age(a) > ao_seqno_age(b);
+}
+
+static inline void
+ao_validate_bufno(int b)
+{
+       if (b < 0 || AO_NUM_BUF <= b)
+               ao_panic(AO_PANIC_BUFIO);
+}
+
+static inline int
+ao_buf_to_num(uint8_t *buf)
+{
+       int b = (buf - &ao_buffer[0][0]) / AO_BUFSIZ;
+
+       ao_validate_bufno(b);
+       return b;
+}
+
+static inline int
+ao_bufio_to_num(struct ao_bufio *bufio)
+{
+       int b = (bufio - ao_bufio);
+
+       ao_validate_bufno(b);
+       return b;
+}
+
+static inline struct ao_bufio *
+ao_buf_to_bufio(uint8_t *buf)
+{
+       int b = ao_buf_to_num(buf);
+       struct ao_bufio *bufio;
+
+       bufio = &ao_bufio[b];
+       DBG ("buf %08x is %d bufio %08x\n", buf, b, bufio);
+       return bufio;
+}
+
+static inline uint8_t *
+ao_bufio_to_buf(struct ao_bufio *bufio)
+{
+       int b = ao_bufio_to_num(bufio);
+       uint8_t *buf;
+
+       buf = &ao_buffer[b][0];
+       DBG ("bufio %08x is %d buf %08x\n", bufio, b, buf);
+       return buf;
+}
+
+/*
+ * Write a buffer to storage if it is dirty
+ */
+static void
+ao_bufio_write(struct ao_bufio *bufio)
+{
+       if (bufio->dirty) {
+               ao_sdcard_write_block(bufio->block, ao_bufio_to_buf(bufio));
+               bufio->dirty = 0;
+       }
+}
+
+/*
+ * Read a buffer from storage
+ */
+static uint8_t
+ao_bufio_read(struct ao_bufio *bufio)
+{
+       uint8_t *buf = ao_bufio_to_buf(bufio);
+
+       return ao_sdcard_read_block(bufio->block, buf);
+}
+
+/*
+ * Find a buffer containing the specified block
+ */
+static struct ao_bufio *
+ao_bufio_find_block(uint32_t block)
+{
+       int b;
+
+       for (b = 0; b < AO_NUM_BUF; b++) {
+               struct ao_bufio *bufio = &ao_bufio[b];
+               if (bufio->block == block) {
+                       DBG ("Found existing buffer %d (seqno %d)\n",
+                               ao_bufio_to_num(bufio), bufio->seqno);
+                       return bufio;
+               }
+       }
+       return NULL;
+}
+
+/*
+ * Find the least recently used idle buffer
+ */
+static struct ao_bufio *
+ao_bufio_find_idle(void)
+{
+       int b;
+       struct ao_bufio *oldest = NULL;
+
+       for (b = 0; b < AO_NUM_BUF; b++) {
+               struct ao_bufio *bufio = &ao_bufio[b];
+               if (!bufio->busy)
+                       if (!oldest || ao_seqno_older(bufio->seqno, oldest->seqno))
+                               oldest = bufio;
+       }
+       if (oldest)
+               DBG ("Using idle buffer %d (seqno %d)\n",
+                       ao_bufio_to_num(oldest), oldest->seqno);
+       return oldest;
+}
+
+/*
+ * Return a pointer to a buffer containing
+ * the contents of the specified block
+ */
+uint8_t *
+ao_bufio_get(uint32_t block)
+{
+       struct ao_bufio *bufio;
+       uint8_t *buf = NULL;
+
+       ao_bufio_lock();
+       bufio = ao_bufio_find_block(block);
+       if (!bufio) {
+               bufio = ao_bufio_find_idle();
+               if (bufio) {
+                       ao_bufio_write(bufio);
+                       bufio->block = block;
+                       DBG ("read buffer\n");
+                       if (!ao_bufio_read(bufio)) {
+                               bufio->block = 0xffffffff;
+                               bufio = NULL;
+                       }
+               } else
+                       ao_panic(AO_PANIC_BUFIO);
+       }
+       if (bufio) {
+               bufio->busy++;
+               if (!bufio->busy)
+                       ao_panic(AO_PANIC_BUFIO);
+               buf = ao_bufio_to_buf(bufio);
+       }
+       ao_bufio_unlock();
+       return buf;
+}
+
+/*
+ * Release a buffer, marking it dirty
+ * if it has been written to
+ */
+void
+ao_bufio_put(uint8_t *buf, uint8_t write)
+{
+       struct ao_bufio *bufio;
+
+       ao_bufio_lock();
+       bufio = ao_buf_to_bufio(buf);
+       
+       if (!bufio->busy)
+               ao_panic(AO_PANIC_BUFIO);
+
+       DBG ("idle buffer %d write %d\n", ao_bufio_to_num(bufio), write);
+       bufio->dirty |= write;
+       if (!--bufio->busy) {
+               bufio->seqno = ao_seqno_next();
+               DBG ("not busy, seqno %d\n", bufio->seqno);
+       }
+       ao_bufio_unlock();
+}
+
+/*
+ * Flush a single buffer immediately. Useful
+ * if write order is important
+ */
+void
+ao_bufio_flush_one(uint8_t *buf)
+{
+       ao_bufio_lock();
+       ao_bufio_write(ao_buf_to_bufio(buf));
+       ao_bufio_unlock();
+}
+
+/*
+ * Flush all buffers to storage
+ */
+void
+ao_bufio_flush(void)
+{
+       int     b;
+
+       ao_bufio_lock();
+       for (b = 0; b < AO_NUM_BUF; b++)
+               ao_bufio_write(&ao_bufio[b]);
+       ao_bufio_unlock();
+}
+
+#if BUFIO_COMMANDS
+static void
+ao_bufio_test_read(void)
+{
+       uint8_t *buf;
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       if ((buf = ao_bufio_get(ao_cmd_lex_u32))) {
+               int i;
+               for (i = 0; i < 512; i++) {
+                       printf (" %02x", buf[i]);
+                       if ((i & 0xf) == 0xf)
+                               printf("\n");
+               }
+               ao_bufio_put(buf, 0);
+       }
+}
+
+static const struct ao_cmds ao_bufio_cmds[] = {
+       { ao_bufio_test_read,   "q\0Test bufio read" },
+       { 0, NULL },
+};
+#endif
+
+void
+ao_bufio_setup(void)
+{
+       int b;
+
+       for (b = 0; b < AO_NUM_BUF; b++) {
+               ao_bufio[b].dirty = 0;
+               ao_bufio[b].busy = 0;
+               ao_bufio[b].block = 0xffffffff;
+       }
+}
+
+void
+ao_bufio_init(void)
+{
+       ao_bufio_setup();
+       ao_sdcard_init();
+#if BUFIO_COMMANDS
+       ao_cmd_register(&ao_bufio_cmds[0]);
+#endif
+}
diff --git a/src/drivers/ao_bufio.h b/src/drivers/ao_bufio.h
new file mode 100644 (file)
index 0000000..6629f14
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_BUFIO_H_
+#define _AO_BUFIO_H_
+
+uint8_t *
+ao_bufio_get(uint32_t block);
+
+void
+ao_bufio_put(uint8_t *buf, uint8_t write);
+
+void
+ao_bufio_flush_one(uint8_t *buf);
+
+void
+ao_bufio_flush(void);
+
+void
+ao_bufio_setup(void);
+
+void
+ao_bufio_init(void);
+
+#endif /* _AO_BUFIO_H_ */
index 53bb5a62f5621bedb4c94d48d76b660b5213ea46..a26eccbc26d10a16b1719a12a612aeb0fa049dad 100644 (file)
@@ -747,7 +747,7 @@ ao_radio_send(const void *d, uint8_t size)
 #define AO_RADIO_LOTS  64
 
 void
-ao_radio_send_lots(ao_radio_fill_func fill)
+ao_radio_send_aprs(ao_radio_fill_func fill)
 {
        uint8_t buf[AO_RADIO_LOTS], *b;
        int     cnt;
diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c
new file mode 100644 (file)
index 0000000..30c5644
--- /dev/null
@@ -0,0 +1,958 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_cc115l.h>
+#include <ao_exti.h>
+#include <ao_telemetry.h>
+#include <ao_fec.h>
+
+#define AO_RADIO_MAX_SEND      sizeof (struct ao_telemetry_generic)
+
+uint8_t ao_radio_mutex;
+
+static uint8_t ao_radio_fifo;          /* fifo drained interrupt received */
+static uint8_t ao_radio_done;          /* tx done interrupt received */
+static uint8_t ao_radio_wake;          /* sleep address for radio interrupts */
+static uint8_t ao_radio_abort;         /* radio operation should abort */
+static uint8_t ao_radio_mcu_wake;      /* MARC status change */
+static uint8_t ao_radio_marcstate;     /* Last read MARC state value */
+
+/* Debugging commands */
+#define CC115L_DEBUG   0
+
+/* Runtime tracing */
+#define CC115L_TRACE   0
+
+#define FOSC   26000000
+
+#define ao_radio_select()      ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_SPI_SPEED_1MHz)
+#define ao_radio_deselect()    ao_spi_put_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS)
+#define ao_radio_spi_send(d,l) ao_spi_send((d), (l), AO_CC115L_SPI_BUS)
+#define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC115L_SPI_BUS)
+#define ao_radio_spi_recv(d,l) ao_spi_recv((d), (l), AO_CC115L_SPI_BUS)
+#define ao_radio_duplex(o,i,l) ao_spi_duplex((o), (i), (l), AO_CC115L_SPI_BUS)
+
+struct ao_cc115l_reg {
+       uint16_t        addr;
+       char            *name;
+};
+
+#if CC115L_TRACE
+
+const static struct ao_cc115l_reg ao_cc115l_reg[];
+const static char *cc115l_state_name[];
+
+enum ao_cc115l_trace_type {
+       trace_strobe,
+       trace_read,
+       trace_write,
+       trace_dma,
+       trace_line,
+};
+
+struct ao_cc115l_trace {
+       enum ao_cc115l_trace_type       type;
+       int16_t                         addr;
+       int16_t                         value;
+       const char                      *comment;
+};
+
+#define NUM_TRACE      256
+
+static struct ao_cc115l_trace  trace[NUM_TRACE];
+static int                     trace_i;
+static int                     trace_disable;
+
+static void trace_add(enum ao_cc115l_trace_type type, int16_t addr, int16_t value, const char *comment)
+{
+       if (trace_disable)
+               return;
+       switch (type) {
+       case trace_read:
+       case trace_write:
+               comment = ao_cc115l_reg[addr].name;
+               break;
+       case trace_strobe:
+               comment = cc115l_state_name[(value >> 4) & 0x7];
+               break;
+       }
+       trace[trace_i].type = type;
+       trace[trace_i].addr = addr;
+       trace[trace_i].value = value;
+       trace[trace_i].comment = comment;
+       if (++trace_i == NUM_TRACE)
+               trace_i = 0;
+}
+#else
+#define trace_add(t,a,v,c)
+#endif
+
+static uint8_t
+ao_radio_reg_read(uint8_t addr)
+{
+       uint8_t data[1];
+       uint8_t d;
+
+       data[0] = ((1 << CC115L_READ)  |
+                  (0 << CC115L_BURST) |
+                  addr);
+       ao_radio_select();
+       ao_radio_spi_send(data, 1);
+       ao_radio_spi_recv(data, 1);
+       ao_radio_deselect();
+       trace_add(trace_read, addr, data[0], NULL);
+       return data[0];
+}
+
+static void
+ao_radio_reg_write(uint8_t addr, uint8_t value)
+{
+       uint8_t data[2];
+       uint8_t d;
+
+       trace_add(trace_write, addr, value, NULL);
+       data[0] = ((0 << CC115L_READ)  |
+                  (0 << CC115L_BURST) |
+                  addr);
+       data[1] = value;
+       ao_radio_select();
+       ao_radio_spi_send(data, 2);
+       ao_radio_deselect();
+}
+
+static void
+ao_radio_burst_read_start (uint16_t addr)
+{
+       uint8_t data[1];
+       uint8_t d;
+
+       data[0] = ((1 << CC115L_READ)  |
+                  (1 << CC115L_BURST) |
+                  addr);
+       ao_radio_select();
+       ao_radio_spi_send(data, 1);
+}
+
+static void
+ao_radio_burst_read_stop (void)
+{
+       ao_radio_deselect();
+}
+
+
+static uint8_t
+ao_radio_strobe(uint8_t addr)
+{
+       uint8_t in;
+
+       ao_radio_select();
+       ao_radio_duplex(&addr, &in, 1);
+       ao_radio_deselect();
+       trace_add(trace_strobe, addr, in, NULL);
+       return in;
+}
+
+static uint8_t
+ao_radio_fifo_write_start(void)
+{
+       uint8_t addr = ((0 << CC115L_READ)  |
+                       (1 << CC115L_BURST) |
+                       CC115L_FIFO);
+       uint8_t status;
+
+       ao_radio_select();
+       ao_radio_duplex(&addr, &status, 1);
+       return status;
+}
+
+static inline uint8_t ao_radio_fifo_write_stop(uint8_t status) {
+       ao_radio_deselect();
+       return status;
+}
+
+static uint8_t
+ao_radio_fifo_write(uint8_t *data, uint8_t len)
+{
+       uint8_t status = ao_radio_fifo_write_start();
+       trace_add(trace_dma, CC115L_FIFO, len, NULL);
+       ao_radio_spi_send(data, len);
+       return ao_radio_fifo_write_stop(status);
+}
+
+static uint8_t
+ao_radio_tx_fifo_space(void)
+{
+       return CC115L_FIFO_SIZE - (ao_radio_reg_read(CC115L_TXBYTES) & CC115L_TXBYTES_NUM_TX_BYTES_MASK);
+}
+
+static uint8_t
+ao_radio_status(void)
+{
+       return ao_radio_strobe (CC115L_SNOP);
+}
+
+#define ao_radio_rdf_value 0x55
+
+static uint8_t
+ao_radio_get_marcstate(void)
+{
+       return ao_radio_reg_read(CC115L_MARCSTATE) & CC115L_MARCSTATE_MASK;
+}
+         
+static void
+ao_radio_done_isr(void)
+{
+       ao_exti_disable(AO_CC115L_DONE_INT_PORT, AO_CC115L_DONE_INT_PIN);
+       trace_add(trace_line, __LINE__, 0, "done_isr");
+       ao_radio_done = 1;
+       ao_wakeup(&ao_radio_wake);
+}
+
+static void
+ao_radio_fifo_isr(void)
+{
+       ao_exti_disable(AO_CC115L_FIFO_INT_PORT, AO_CC115L_FIFO_INT_PIN);
+       trace_add(trace_line, __LINE__, 0, "fifo_isr");
+       ao_radio_fifo = 1;
+       ao_wakeup(&ao_radio_wake);
+}
+
+static void
+ao_radio_start_tx(void)
+{
+}
+
+static void
+ao_radio_idle(void)
+{
+       ao_radio_pa_off();
+       for (;;) {
+               uint8_t state = ao_radio_strobe(CC115L_SIDLE);
+               if ((state >> CC115L_STATUS_STATE) == CC115L_STATUS_STATE_IDLE)
+                       break;
+       }
+       /* Flush any pending TX bytes */
+       ao_radio_strobe(CC115L_SFTX);
+}
+
+/*
+ * Packet deviation is 20.5kHz
+ *
+ *     fdev = fosc >> 17 * (8 + dev_m) << dev_e
+ *
+ *             26e6 / (2 ** 17) * (8 + 5) * (2 ** 3) = 20630Hz
+ */
+
+#define PACKET_DEV_E   3
+#define PACKET_DEV_M   5
+
+/*
+ * For our packet data, set the symbol rate to 38400 Baud
+ *
+ *              (256 + DATARATE_M) * 2 ** DATARATE_E
+ *     Rdata = -------------------------------------- * fosc
+ *                          2 ** 28
+ *
+ *             (256 + 131) * (2 ** 10) / (2**28) * 26e6 = 38383
+ *
+ *     DATARATE_M = 131
+ *     DATARATE_E = 10
+ */
+#define PACKET_DRATE_E 10
+#define PACKET_DRATE_M 131
+
+static const uint16_t packet_setup[] = {
+       CC115L_DEVIATN,         ((PACKET_DEV_E << CC115L_DEVIATN_DEVIATION_E) |
+                                (PACKET_DEV_M << CC115L_DEVIATN_DEVIATION_M)),
+       CC115L_MDMCFG4,         ((0xf << 4) |
+                                (PACKET_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
+       CC115L_MDMCFG3,         (PACKET_DRATE_M),
+       CC115L_MDMCFG2,         (0x00 |
+                                (CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) |
+                                (0 << CC115L_MDMCFG2_MANCHESTER_EN) |
+                                (CC115L_MDMCFG2_SYNC_MODE_16BITS << CC115L_MDMCFG2_SYNC_MODE)),
+};
+
+
+/*
+ * RDF deviation is 5kHz
+ *
+ *     fdev = fosc >> 17 * (8 + dev_m) << dev_e
+ *
+ *             26e6 / (2 ** 17) * (8 + 4) * (2 ** 1) = 4761Hz
+ */
+
+#define RDF_DEV_E      1
+#define RDF_DEV_M      4
+
+/*
+ * For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone)
+ *
+ *              (256 + DATARATE_M) * 2 ** DATARATE_E
+ *     Rdata = -------------------------------------- * fosc
+ *                          2 ** 28
+ *
+ *             (256 + 67) * (2 ** 6) / (2**28) * 26e6 = 2002
+ *
+ *     DATARATE_M = 67
+ *     DATARATE_E = 6
+ */
+#define RDF_DRATE_E    6
+#define RDF_DRATE_M    67
+
+static const uint16_t rdf_setup[] = {
+       CC115L_DEVIATN,         ((RDF_DEV_E << CC115L_DEVIATN_DEVIATION_E) |
+                                (RDF_DEV_M << CC115L_DEVIATN_DEVIATION_M)),
+       CC115L_MDMCFG4,         ((0xf << 4) |
+                                (RDF_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
+       CC115L_MDMCFG3,         (RDF_DRATE_M),
+       CC115L_MDMCFG2,         (0x00 |
+                                (CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) |
+                                (0 << CC115L_MDMCFG2_MANCHESTER_EN) |
+                                (CC115L_MDMCFG2_SYNC_MODE_NONE << CC115L_MDMCFG2_SYNC_MODE)),
+};
+
+/*
+ * APRS deviation is the same as RDF
+ */
+
+#define APRS_DEV_E     RDF_DEV_E
+#define APRS_DEV_M     RDF_DEV_M
+
+/*
+ * For our APRS beacon, set the symbol rate to 9.6kBaud (8x oversampling for 1200 baud data rate)
+ *
+ *              (256 + DATARATE_M) * 2 ** DATARATE_E
+ *     Rdata = -------------------------------------- * fosc
+ *                          2 ** 28
+ *
+ *             (256 + 131) * (2 ** 8) / (2**28) * 26e6 = 9596
+ *
+ *     DATARATE_M = 131
+ *     DATARATE_E = 8
+ *
+ */
+#define APRS_DRATE_E   8
+#define APRS_DRATE_M   131
+
+static const uint16_t aprs_setup[] = {
+       CC115L_DEVIATN,         ((APRS_DEV_E << CC115L_DEVIATN_DEVIATION_E) |
+                                (APRS_DEV_M << CC115L_DEVIATN_DEVIATION_M)),
+       CC115L_MDMCFG4,         ((0xf << 4) |
+                                (APRS_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
+       CC115L_MDMCFG3,         (APRS_DRATE_M),
+       CC115L_MDMCFG2,         (0x00 |
+                                (CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) |
+                                (0 << CC115L_MDMCFG2_MANCHESTER_EN) |
+                                (CC115L_MDMCFG2_SYNC_MODE_NONE << CC115L_MDMCFG2_SYNC_MODE)),
+};
+
+#define AO_PKTCTRL0_INFINITE   ((CC115L_PKTCTRL0_PKT_FORMAT_NORMAL << CC115L_PKTCTRL0_PKT_FORMAT) | \
+                                (0 << CC115L_PKTCTRL0_PKT_CRC_EN) |                                    \
+                                (CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_INFINITE << CC115L_PKTCTRL0_PKT_LENGTH_CONFIG))
+#define AO_PKTCTRL0_FIXED      ((CC115L_PKTCTRL0_PKT_FORMAT_NORMAL << CC115L_PKTCTRL0_PKT_FORMAT) | \
+                                (0 << CC115L_PKTCTRL0_PKT_CRC_EN) |                                    \
+                                (CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_FIXED << CC115L_PKTCTRL0_PKT_LENGTH_CONFIG))
+
+static uint16_t ao_radio_mode;
+
+
+/*
+ * These set the data rate and modulation parameters
+ */
+#define AO_RADIO_MODE_BITS_PACKET_TX   1
+#define AO_RADIO_MODE_BITS_RDF         2
+#define AO_RADIO_MODE_BITS_APRS                4
+
+/*
+ * Flips between infinite packet mode and fixed packet mode;
+ * we use infinite mode until the sender gives us the
+ * last chunk of data
+ */
+#define AO_RADIO_MODE_BITS_INFINITE    40
+#define AO_RADIO_MODE_BITS_FIXED       80
+
+#define AO_RADIO_MODE_NONE             0
+
+#define AO_RADIO_MODE_RDF              AO_RADIO_MODE_BITS_RDF
+#define AO_RADIO_MODE_PACKET_TX                AO_RADIO_MODE_BITS_PACKET_TX
+#define AO_RADIO_MODE_APRS             AO_RADIO_MODE_BITS_APRS
+
+static void
+ao_radio_set_mode(uint16_t new_mode)
+{
+       uint16_t changes;
+       int i;
+
+       if (new_mode == ao_radio_mode)
+               return;
+
+       changes = new_mode & (~ao_radio_mode);
+       if (changes & AO_RADIO_MODE_BITS_PACKET_TX)
+               for (i = 0; i < sizeof (packet_setup) / sizeof (packet_setup[0]); i += 2)
+                       ao_radio_reg_write(packet_setup[i], packet_setup[i+1]);
+
+       if (changes & AO_RADIO_MODE_BITS_RDF)
+               for (i = 0; i < sizeof (rdf_setup) / sizeof (rdf_setup[0]); i += 2)
+                       ao_radio_reg_write(rdf_setup[i], rdf_setup[i+1]);
+
+       if (changes & AO_RADIO_MODE_BITS_APRS)
+               for (i = 0; i < sizeof (aprs_setup) / sizeof (aprs_setup[0]); i += 2)
+                       ao_radio_reg_write(aprs_setup[i], aprs_setup[i+1]);
+
+       if (changes & AO_RADIO_MODE_BITS_INFINITE)
+               ao_radio_reg_write(CC115L_PKTCTRL0, AO_PKTCTRL0_INFINITE);
+
+       if (changes & AO_RADIO_MODE_BITS_FIXED)
+               ao_radio_reg_write(CC115L_PKTCTRL0, AO_PKTCTRL0_FIXED);
+
+       ao_radio_mode = new_mode;
+}
+
+/***************************************************************
+ *  SmartRF Studio(tm) Export
+ *
+ *  Radio register settings specifed with address, value
+ *
+ *  RF device: CC115L
+ *
+ ***************************************************************/
+
+static const uint16_t radio_setup[] = {
+
+       /* High when FIFO is above threshold, low when fifo is below threshold */
+       AO_CC115L_FIFO_INT_GPIO_IOCFG,      CC115L_IOCFG_GPIO_CFG_TXFIFO_THR,
+
+       /* High when transmitter is running, low when off */
+       AO_CC115L_DONE_INT_GPIO_IOCFG,      CC115L_IOCFG_GPIO_CFG_PA_PD | (1 << CC115L_IOCFG_GPIO_INV),
+
+        CC115L_FIFOTHR,                     0x47,       /* TX FIFO Thresholds */
+       CC115L_MDMCFG1,                     (0x00 |
+                                            (CC115L_MDMCFG1_NUM_PREAMBLE_4 << CC115L_MDMCFG1_NUM_PREAMBLE) |
+                                            (1 << CC115L_MDMCFG1_CHANSPC_E)),
+       CC115L_MDMCFG0,                     248,        /* Channel spacing M value (100kHz channels) */
+        CC115L_MCSM0,                       0x38,       /* Main Radio Control State Machine Configuration */
+        CC115L_RESERVED_0X20,               0xfb,       /* Use setting from SmartRF Studio */
+        CC115L_FSCAL3,                      0xe9,       /* Frequency Synthesizer Calibration */
+        CC115L_FSCAL2,                      0x2a,       /* Frequency Synthesizer Calibration */
+        CC115L_FSCAL1,                      0x00,       /* Frequency Synthesizer Calibration */
+        CC115L_FSCAL0,                      0x1f,       /* Frequency Synthesizer Calibration */
+        CC115L_TEST2,                       0x81,       /* Various Test Settings */
+        CC115L_TEST1,                       0x35,       /* Various Test Settings */
+        CC115L_TEST0,                       0x09,       /* Various Test Settings */
+};
+
+static uint8_t ao_radio_configured = 0;
+
+static void
+ao_radio_setup(void)
+{
+       int     i;
+
+       ao_radio_strobe(CC115L_SRES);
+       ao_delay(AO_MS_TO_TICKS(10));
+
+       for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2)
+               ao_radio_reg_write(radio_setup[i], radio_setup[i+1]);
+
+       ao_radio_mode = 0;
+
+       ao_config_get();
+
+       ao_radio_configured = 1;
+}
+
+static void
+ao_radio_set_len(uint8_t len)
+{
+       static uint8_t  last_len;
+
+       if (len != last_len) {
+               ao_radio_reg_write(CC115L_PKTLEN, len);
+               last_len = len;
+       }
+}
+
+static void
+ao_radio_get(void)
+{
+       static uint32_t last_radio_setting;
+       static uint8_t  last_power_setting;
+
+       ao_mutex_get(&ao_radio_mutex);
+       if (!ao_radio_configured)
+               ao_radio_setup();
+       if (ao_config.radio_setting != last_radio_setting) {
+               ao_radio_reg_write(CC115L_FREQ2, ao_config.radio_setting >> 16);
+               ao_radio_reg_write(CC115L_FREQ1, ao_config.radio_setting >> 8);
+               ao_radio_reg_write(CC115L_FREQ0, ao_config.radio_setting);
+               last_radio_setting = ao_config.radio_setting;
+       }
+       if (ao_config.radio_power != last_power_setting) {
+               ao_radio_reg_write(CC115L_PA, ao_config.radio_power);
+               last_power_setting = ao_config.radio_power;
+       }
+}
+
+static void
+_ao_radio_send_lots(ao_radio_fill_func fill, uint8_t mode);
+
+#define ao_radio_put() ao_mutex_put(&ao_radio_mutex)
+
+struct ao_radio_tone {
+       uint8_t value;
+       uint8_t len;
+};
+
+struct ao_radio_tone *ao_radio_tone;
+uint8_t ao_radio_tone_count;
+uint8_t ao_radio_tone_current;
+uint8_t ao_radio_tone_offset;
+
+int16_t
+ao_radio_tone_fill(uint8_t *buf, int16_t len)
+{
+       int16_t ret = 0;
+
+       while (len) {
+               int16_t                 this_time;
+               struct ao_radio_tone    *t;
+
+               /* Figure out how many to send of the current value */
+               t = &ao_radio_tone[ao_radio_tone_current];
+               this_time = t->len - ao_radio_tone_offset;
+               if (this_time > len)
+                       this_time = len;
+
+               /* queue the data */
+               memset(buf, t->value, this_time);
+
+               /* mark as sent */
+               len -= this_time;
+               ao_radio_tone_offset += this_time;
+               ret += this_time;
+
+               if (ao_radio_tone_offset >= t->len) {
+                       ao_radio_tone_offset = 0;
+                       ao_radio_tone_current++;
+                       if (ao_radio_tone_current >= ao_radio_tone_count) {
+                               trace_add(trace_line, __LINE__, ret, "done with tone");
+                               return -ret;
+                       }
+               }
+       }
+       trace_add(trace_line, __LINE__, ret, "got some tone");
+       return ret;
+}
+
+static void
+ao_radio_tone_run(struct ao_radio_tone *tones, int ntones)
+{
+       ao_radio_get();
+       ao_radio_tone = tones;
+       ao_radio_tone_current = 0;
+       ao_radio_tone_offset = 0;
+       _ao_radio_send_lots(ao_radio_tone_fill, AO_RADIO_MODE_RDF);
+       ao_radio_put();
+}
+
+void
+ao_radio_rdf(void)
+{
+       struct ao_radio_tone    tone;
+
+       tone.value = ao_radio_rdf_value;
+       tone.len = AO_RADIO_RDF_LEN;
+       ao_radio_tone_run(&tone, 1);
+}
+
+void
+ao_radio_continuity(uint8_t c)
+{
+       struct ao_radio_tone    tones[7];
+       uint8_t count = 0;
+       uint8_t i;
+
+       for (i = 0; i < 3; i++) {
+               tones[count].value = 0x00;
+               tones[count].len = AO_RADIO_CONT_PAUSE_LEN;
+               count++;
+               if (i < c)
+                       tones[count].value = ao_radio_rdf_value;
+               else
+                       tones[count].value = 0x00;
+               tones[count].len = AO_RADIO_CONT_TONE_LEN;
+               count++;
+       }
+       tones[count].value = 0x00;
+       tones[count].len = AO_RADIO_CONT_PAUSE_LEN;
+       count++;
+       ao_radio_tone_run(tones, count);
+}
+
+void
+ao_radio_rdf_abort(void)
+{
+       ao_radio_abort = 1;
+       ao_wakeup(&ao_radio_wake);
+}
+
+static void
+ao_radio_test_cmd(void)
+{
+       uint8_t mode = 2;
+       static uint8_t radio_on;
+       ao_cmd_white();
+       if (ao_cmd_lex_c != '\n') {
+               ao_cmd_decimal();
+               mode = (uint8_t) ao_cmd_lex_u32;
+       }
+       mode++;
+       if ((mode & 2) && !radio_on) {
+#if HAS_MONITOR
+               ao_monitor_disable();
+#endif
+#if PACKET_HAS_SLAVE
+               ao_packet_slave_stop();
+#endif
+               ao_radio_get();
+               ao_radio_set_len(0xff);
+               ao_radio_set_mode(AO_RADIO_MODE_RDF|AO_RADIO_MODE_BITS_FIXED);
+               ao_radio_strobe(CC115L_SFTX);
+               ao_radio_pa_on();
+               ao_radio_strobe(CC115L_STX);
+               radio_on = 1;
+       }
+       if (mode == 3) {
+               printf ("Hit a character to stop..."); flush();
+               getchar();
+               putchar('\n');
+       }
+       if ((mode & 1) && radio_on) {
+               ao_radio_idle();
+               ao_radio_put();
+               radio_on = 0;
+#if HAS_MONITOR
+               ao_monitor_enable();
+#endif
+       }
+}
+
+static inline int16_t
+ao_radio_gpio_bits(void)
+{
+       return AO_CC115L_DONE_INT_PORT->idr & ((1 << AO_CC115L_FIFO_INT_PIN) |
+                                              (1 << AO_CC115L_DONE_INT_PIN));
+}
+
+static void
+ao_radio_wait_fifo(void)
+{
+       ao_arch_block_interrupts();
+       while (!ao_radio_fifo && !ao_radio_done && !ao_radio_abort) {
+               trace_add(trace_line, __LINE__, ao_radio_gpio_bits(), "wait_fifo");
+               ao_sleep(&ao_radio_wake);
+       }
+       ao_arch_release_interrupts();
+       trace_add(trace_line, __LINE__, ao_radio_gpio_bits(), "wake bits");
+       trace_add(trace_line, __LINE__, ao_radio_fifo, "wake fifo");
+       trace_add(trace_line, __LINE__, ao_radio_done, "wake done");
+       trace_add(trace_line, __LINE__, ao_radio_abort, "wake abort");
+}
+
+static void
+ao_radio_wait_done(void)
+{
+       ao_arch_block_interrupts();
+       while (!ao_radio_done && !ao_radio_abort) {
+               trace_add(trace_line, __LINE__, ao_radio_gpio_bits(), "wait_done");
+               ao_sleep(&ao_radio_wake);
+       }
+       ao_arch_release_interrupts();
+       trace_add(trace_line, __LINE__, ao_radio_gpio_bits(), "wake bits");
+       trace_add(trace_line, __LINE__, ao_radio_fifo, "wake fifo");
+       trace_add(trace_line, __LINE__, ao_radio_done, "wake done");
+       trace_add(trace_line, __LINE__, ao_radio_abort, "wake abort");
+}
+
+static uint8_t tx_data[(AO_RADIO_MAX_SEND + 4) * 2];
+
+static uint8_t *ao_radio_send_buf;
+static int16_t ao_radio_send_len;
+
+static int16_t
+ao_radio_send_fill(uint8_t *buf, int16_t len)
+{
+       int16_t this_time;
+
+       this_time = ao_radio_send_len;
+       if (this_time > len)
+               this_time = len;
+       memcpy(buf, ao_radio_send_buf, this_time);
+       ao_radio_send_buf += this_time;
+       ao_radio_send_len -= this_time;
+       if (ao_radio_send_len == 0)
+               return -this_time;
+       return this_time;
+}
+
+void
+ao_radio_send(const void *d, uint8_t size)
+{
+       int i;
+
+       ao_radio_get();
+       ao_radio_send_len = ao_fec_encode(d, size, tx_data);
+       ao_radio_send_buf = tx_data;
+       _ao_radio_send_lots(ao_radio_send_fill, AO_RADIO_MODE_PACKET_TX);
+       ao_radio_put();
+}
+
+#define AO_RADIO_LOTS  64
+
+static void
+_ao_radio_send_lots(ao_radio_fill_func fill, uint8_t mode)
+{
+       uint8_t buf[AO_RADIO_LOTS], *b;
+       int     cnt;
+       int     total = 0;
+       uint8_t done = 0;
+       uint8_t started = 0;
+       uint8_t fifo_space;
+
+       fifo_space = CC115L_FIFO_SIZE;
+       ao_radio_done = 0;
+       ao_radio_fifo = 0;
+       while (!done) {
+               cnt = (*fill)(buf, sizeof(buf));
+               trace_add(trace_line, __LINE__, cnt, "send data count");
+               if (cnt < 0) {
+                       done = 1;
+                       cnt = -cnt;
+               }
+               total += cnt;
+
+               /* At the last buffer, set the total length */
+               if (done) {
+                       ao_radio_set_len(total & 0xff);
+                       ao_radio_set_mode(mode | AO_RADIO_MODE_BITS_FIXED);
+               } else {
+                       ao_radio_set_len(0xff);
+                       ao_radio_set_mode(mode | AO_RADIO_MODE_BITS_INFINITE);
+               }
+
+               b = buf;
+               while (cnt) {
+                       uint8_t this_len = cnt;
+
+                       /* Wait for some space in the fifo */
+                       while (!ao_radio_abort && (fifo_space = ao_radio_tx_fifo_space()) == 0) {
+                               trace_add(trace_line, __LINE__, this_len, "wait for space");
+                               ao_radio_wait_fifo();
+                       }
+                       if (ao_radio_abort || ao_radio_done)
+                               break;
+                       trace_add(trace_line, __LINE__, fifo_space, "got space");
+                       if (this_len > fifo_space)
+                               this_len = fifo_space;
+
+                       cnt -= this_len;
+
+                       ao_radio_done = 0;
+                       ao_radio_fifo = 0;
+                       ao_radio_fifo_write(b, this_len);
+                       b += this_len;
+
+                       ao_exti_enable(AO_CC115L_FIFO_INT_PORT, AO_CC115L_FIFO_INT_PIN);
+                       ao_exti_enable(AO_CC115L_DONE_INT_PORT, AO_CC115L_DONE_INT_PIN);
+
+                       if (!started) {
+                               ao_radio_pa_on();
+                               ao_radio_strobe(CC115L_STX);
+                               started = 1;
+                       }
+               }
+               if (ao_radio_abort || ao_radio_done)
+                       break;
+       }
+       if (ao_radio_abort)
+               ao_radio_idle();
+       ao_radio_wait_done();
+       ao_radio_pa_off();
+}
+
+void
+ao_radio_send_aprs(ao_radio_fill_func fill)
+{
+       ao_radio_get();
+       _ao_radio_send_lots(fill, AO_RADIO_MODE_APRS);
+       ao_radio_put();
+}
+
+#if CC115L_DEBUG
+const static char *cc115l_state_name[] = {
+       [CC115L_STATUS_STATE_IDLE] = "IDLE",
+       [CC115L_STATUS_STATE_TX] = "TX",
+       [CC115L_STATUS_STATE_FSTXON] = "FSTXON",
+       [CC115L_STATUS_STATE_CALIBRATE] = "CALIBRATE",
+       [CC115L_STATUS_STATE_SETTLING] = "SETTLING",
+       [CC115L_STATUS_STATE_TX_FIFO_UNDERFLOW] = "TX_FIFO_UNDERFLOW",
+};
+
+const static struct ao_cc115l_reg ao_cc115l_reg[] = {
+       { .addr = CC115L_IOCFG2, .name = "IOCFG2" },
+       { .addr = CC115L_IOCFG1, .name = "IOCFG1" },
+       { .addr = CC115L_IOCFG0, .name = "IOCFG0" },
+       { .addr = CC115L_FIFOTHR, .name = "FIFOTHR" },
+       { .addr = CC115L_SYNC1, .name = "SYNC1" },
+       { .addr = CC115L_SYNC0, .name = "SYNC0" },
+       { .addr = CC115L_PKTLEN, .name = "PKTLEN" },
+       { .addr = CC115L_PKTCTRL0, .name = "PKTCTRL0" },
+       { .addr = CC115L_CHANNR, .name = "CHANNR" },
+       { .addr = CC115L_FSCTRL0, .name = "FSCTRL0" },
+       { .addr = CC115L_FREQ2, .name = "FREQ2" },
+       { .addr = CC115L_FREQ1, .name = "FREQ1" },
+       { .addr = CC115L_FREQ0, .name = "FREQ0" },
+       { .addr = CC115L_MDMCFG4, .name = "MDMCFG4" },
+       { .addr = CC115L_MDMCFG3, .name = "MDMCFG3" },
+       { .addr = CC115L_MDMCFG2, .name = "MDMCFG2" },
+       { .addr = CC115L_MDMCFG1, .name = "MDMCFG1" },
+       { .addr = CC115L_MDMCFG0, .name = "MDMCFG0" },
+       { .addr = CC115L_DEVIATN, .name = "DEVIATN" },
+       { .addr = CC115L_MCSM1, .name = "MCSM1" },
+       { .addr = CC115L_MCSM0, .name = "MCSM0" },
+       { .addr = CC115L_RESERVED_0X20, .name = "RESERVED_0X20" },
+       { .addr = CC115L_FREND0, .name = "FREND0" },
+       { .addr = CC115L_FSCAL3, .name = "FSCAL3" },
+       { .addr = CC115L_FSCAL2, .name = "FSCAL2" },
+       { .addr = CC115L_FSCAL1, .name = "FSCAL1" },
+       { .addr = CC115L_FSCAL0, .name = "FSCAL0" },
+       { .addr = CC115L_RESERVED_0X29, .name = "RESERVED_0X29" },
+       { .addr = CC115L_RESERVED_0X2A, .name = "RESERVED_0X2A" },
+       { .addr = CC115L_RESERVED_0X2B, .name = "RESERVED_0X2B" },
+       { .addr = CC115L_TEST2, .name = "TEST2" },
+       { .addr = CC115L_TEST1, .name = "TEST1" },
+       { .addr = CC115L_TEST0, .name = "TEST0" },
+       { .addr = CC115L_PARTNUM, .name = "PARTNUM" },
+       { .addr = CC115L_VERSION, .name = "VERSION" },
+       { .addr = CC115L_MARCSTATE, .name = "MARCSTATE" },
+       { .addr = CC115L_PKTSTATUS, .name = "PKTSTATUS" },
+       { .addr = CC115L_TXBYTES, .name = "TXBYTES" },
+       { .addr = CC115L_PA, .name = "PA" },
+};
+
+#define AO_NUM_CC115L_REG      (sizeof ao_cc115l_reg / sizeof ao_cc115l_reg[0])
+
+static void ao_radio_show(void) {
+       uint8_t status = ao_radio_status();
+       int     i;
+
+       ao_radio_get();
+       status = ao_radio_status();
+       printf ("Status:   %02x\n", status);
+       printf ("CHIP_RDY: %d\n", (status >> CC115L_STATUS_CHIP_RDY) & 1);
+       printf ("STATE:    %s\n", cc115l_state_name[(status >> CC115L_STATUS_STATE) & CC115L_STATUS_STATE_MASK]);
+       printf ("MARC:     %02x\n", ao_radio_get_marcstate());
+
+       for (i = 0; i < AO_NUM_CC115L_REG; i++)
+               printf ("\t%02x %-20.20s\n", ao_radio_reg_read(ao_cc115l_reg[i].addr), ao_cc115l_reg[i].name);
+       ao_radio_put();
+}
+
+static void ao_radio_beep(void) {
+       ao_radio_rdf();
+}
+
+static void ao_radio_packet(void) {
+       static const uint8_t packet[] = {
+#if 1
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+#else
+               3, 1, 2, 3
+#endif
+       };
+
+       ao_radio_send(packet, sizeof (packet));
+}
+
+#endif /* CC115L_DEBUG */
+
+#if HAS_APRS
+#include <ao_aprs.h>
+
+static void
+ao_radio_aprs()
+{
+#if PACKET_HAS_SLAVE
+       ao_packet_slave_stop();
+#endif
+       ao_aprs_send();
+}
+#endif
+
+static const struct ao_cmds ao_radio_cmds[] = {
+       { ao_radio_test_cmd,    "C <1 start, 0 stop, none both>\0Radio carrier test" },
+#if CC115L_DEBUG
+#if HAS_APRS
+       { ao_radio_aprs,        "G\0Send APRS packet" },
+#endif
+       { ao_radio_show,        "R\0Show CC115L status" },
+       { ao_radio_beep,        "b\0Emit an RDF beacon" },
+       { ao_radio_packet,      "p\0Send a test packet" },
+#endif
+       { 0, NULL }
+};
+
+void
+ao_radio_init(void)
+{
+       int     i;
+
+       ao_radio_configured = 0;
+       ao_spi_init_cs (AO_CC115L_SPI_CS_PORT, (1 << AO_CC115L_SPI_CS_PIN));
+
+#if 0
+       AO_CC115L_SPI_CS_PORT->bsrr = ((uint32_t) (1 << AO_CC115L_SPI_CS_PIN));
+       for (i = 0; i < 10000; i++) {
+               if ((SPI_2_PORT->idr & (1 << SPI_2_MISO_PIN)) == 0)
+                       break;
+       }
+       AO_CC115L_SPI_CS_PORT->bsrr = (1 << AO_CC115L_SPI_CS_PIN);
+       if (i == 10000)
+               ao_panic(AO_PANIC_SELF_TEST_CC115L);
+#endif
+
+       /* Enable the fifo threhold interrupt pin */
+       ao_enable_port(AO_CC115L_FIFO_INT_PORT);
+       ao_exti_setup(AO_CC115L_FIFO_INT_PORT, AO_CC115L_FIFO_INT_PIN,
+                     AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH,
+                     ao_radio_fifo_isr);
+
+       /* Enable the tx done interrupt pin */
+       ao_enable_port(AO_CC115L_DONE_INT_PORT);
+       ao_exti_setup(AO_CC115L_DONE_INT_PORT, AO_CC115L_DONE_INT_PIN,
+                     AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED,
+                     ao_radio_done_isr);
+
+       ao_radio_pa_init();
+
+       ao_cmd_register(&ao_radio_cmds[0]);
+}
diff --git a/src/drivers/ao_cc115l.h b/src/drivers/ao_cc115l.h
new file mode 100644 (file)
index 0000000..811c14a
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_CC115L_H_
+#define _AO_CC115L_H_
+
+#define CC115L_BURST           6
+#define CC115L_READ            7
+
+/* Register space */
+#define CC115L_IOCFG2          0x00    /* GDO2 Output Pin Configuration */
+#define CC115L_IOCFG1          0x01    /* GDO1 Output Pin Configuration */
+#define CC115L_IOCFG0          0x02    /* GDO0 Output Pin Configuration */
+
+#define  CC115L_IOCFG_GPIO1_DS         7
+#define  CC115L_IOCFG_GPIO_INV         6
+
+#define  CC115L_IOCFG_GPIO_CFG         0
+#define  CC115L_IOCFG_GPIO_CFG_TXFIFO_THR      2
+#define  CC115L_IOCFG_GPIO_CFG_TXFIFO_THR_PKT  3
+#define  CC115L_IOCFG_GPIO_CFG_TXFIFO_UNDERFLOW        5
+#define  CC115L_IOCFG_GPIO_CFG_PKT_SYNC_TX     6
+#define  CC115L_IOCFG_GPIO_CFG_PLL_LOCKED      10
+#define  CC115L_IOCFG_GPIO_CFG_SERIAL_CLK      11
+#define  CC115L_IOCFG_GPIO_CFG_SYNC_DATA       12
+#define  CC115L_IOCFG_GPIO_CFG_ASYNC_DATA      13
+#define  CC115L_IOCFG_GPIO_CFG_PA_PD           27
+#define  CC115L_IOCFG_GPIO_CFG_CHIP_RDYn       41
+#define  CC115L_IOCFG_GPIO_CFG_XOSC_STABLE     43
+#define  CC115L_IOCFG_GPIO_CFG_HIGHZ           46
+#define  CC115L_IOCFG_GPIO_CFG_HW_0            47
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_1      48
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_1_5    49
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_2      50
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_3      51
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_4      52
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_6      53
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_8      54
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_12     55
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_16     56
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_24     57
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_32     58
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_48     59
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_64     60
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_96     61
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_128    62
+#define  CC115L_IOCFG_GPIO_CFG_CLK_XOSC_192    63
+#define  CC115L_IOCFG_GPIO_CFG_MASK    0x3f
+
+#define CC115L_FIFOTHR                 0x03    /* TX FIFO Thresholds */
+#define  CC115L_FIFOTHR_THR_MASK       0x0f
+#define  CC115L_FIFOTHR_THR_61         0
+#define  CC115L_FIFOTHR_THR_57         1
+#define  CC115L_FIFOTHR_THR_53         2
+#define  CC115L_FIFOTHR_THR_49         3
+#define  CC115L_FIFOTHR_THR_45         4
+#define  CC115L_FIFOTHR_THR_41         5
+#define  CC115L_FIFOTHR_THR_37         6
+#define  CC115L_FIFOTHR_THR_33         7
+#define  CC115L_FIFOTHR_THR_29         8
+#define  CC115L_FIFOTHR_THR_25         9
+#define  CC115L_FIFOTHR_THR_21         10
+#define  CC115L_FIFOTHR_THR_17         11
+#define  CC115L_FIFOTHR_THR_13         12
+#define  CC115L_FIFOTHR_THR_9          13
+#define  CC115L_FIFOTHR_THR_5          14
+#define  CC115L_FIFOTHR_THR_1          15
+
+#define CC115L_SYNC1           0x04    /* Sync Word, High Byte */
+#define CC115L_SYNC0           0x05    /* Sync Word, Low Byte */
+#define CC115L_PKTLEN          0x06    /* Packet Length */
+#define CC115L_PKTCTRL0                0x08    /* Packet Automation Control */
+#define  CC115L_PKTCTRL0_PKT_FORMAT            4
+#define  CC115L_PKTCTRL0_PKT_FORMAT_NORMAL             0
+#define  CC115L_PKTCTRL0_PKT_FORMAT_SYNC_SERIAL                1
+#define  CC115L_PKTCTRL0_PKT_FORMAT_RANDOM             2
+#define  CC115L_PKTCTRL0_PKT_FORMAT_ASYNC_SERIAL       3
+#define  CC115L_PKTCTRL0_PKT_FORMAT_MASK               3
+#define  CC115L_PKTCTRL0_PKT_CRC_EN            2
+#define  CC115L_PKTCTRL0_PKT_LENGTH_CONFIG     0
+#define  CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_FIXED       0
+#define  CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_VARIABLE    1
+#define  CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_INFINITE    2
+#define  CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_MASK                3
+#define CC115L_CHANNR          0x0a    /* Channel Number */
+#define CC115L_FSCTRL0         0x0c    /* Frequency Synthesizer Control */
+#define CC115L_FREQ2           0x0d    /* Frequency Control Word, High Byte */
+#define CC115L_FREQ1           0x0e    /* Frequency Control Word, Middle Byte */
+#define CC115L_FREQ0           0x0f    /* Frequency Control Word, Low Byte */
+#define CC115L_MDMCFG4                 0x10    /* Modem Configuration */
+#define  CC115L_MDMCFG4_DRATE_E                        0
+#define CC115L_MDMCFG3         0x11    /* Modem Configuration */
+#define CC115L_MDMCFG2         0x12    /* Modem Configuration */
+#define  CC115L_MDMCFG2_MOD_FORMAT             4
+#define  CC115L_MDMCFG2_MOD_FORMAT_2FSK                        0
+#define  CC115L_MDMCFG2_MOD_FORMAT_GFSK                        1
+#define  CC115L_MDMCFG2_MOD_FORMAT_OOK                 3
+#define  CC115L_MDMCFG2_MOD_FORMAT_4FSK                        4
+#define  CC115L_MDMCFG2_MOD_FORMAT_MASK                        7
+#define  CC115L_MDMCFG2_MANCHESTER_EN          3
+#define  CC115L_MDMCFG2_SYNC_MODE              0
+#define  CC115L_MDMCFG2_SYNC_MODE_NONE                 0
+#define  CC115L_MDMCFG2_SYNC_MODE_16BITS               1
+#define  CC115L_MDMCFG2_SYNC_MODE_32BITS               3
+#define  CC115L_MDMCFG2_SYNC_MODE_MASK                 3
+#define CC115L_MDMCFG1         0x13    /* Modem Configuration */
+#define  CC115L_MDMCFG1_NUM_PREAMBLE           4
+#define  CC115L_MDMCFG1_NUM_PREAMBLE_2                 0
+#define  CC115L_MDMCFG1_NUM_PREAMBLE_3                 1
+#define  CC115L_MDMCFG1_NUM_PREAMBLE_4                 2
+#define  CC115L_MDMCFG1_NUM_PREAMBLE_6                 3
+#define  CC115L_MDMCFG1_NUM_PREAMBLE_8                 4
+#define  CC115L_MDMCFG1_NUM_PREAMBLE_12                        5
+#define  CC115L_MDMCFG1_NUM_PREAMBLE_16                        6
+#define  CC115L_MDMCFG1_NUM_PREAMBLE_24                        7
+#define  CC115L_MDMCFG1_NUM_PREAMBLE_MASK              7
+#define  CC115L_MDMCFG1_CHANSPC_E              0
+#define CC115L_MDMCFG0         0x14    /* Modem Configuration */
+#define CC115L_DEVIATN         0x15    /* Modem Deviation Setting */
+#define  CC115L_DEVIATN_DEVIATION_E            4
+#define  CC115L_DEVIATN_DEVIATION_E_MASK       7
+#define  CC115L_DEVIATN_DEVIATION_M            0
+#define  CC115L_DEVIATN_DEVIATION_M_MASK       7
+#define CC115L_MCSM1           0x17    /* Main Radio Control State Machine Configuration */
+#define  CC115L_MCSM1_TXOFF_MODE               0
+#define  CC115L_MCSM1_TXOFF_MODE_IDLE                  0
+#define  CC115L_MCSM1_TXOFF_MODE_FSTXON                        1
+#define  CC115L_MCSM1_TXOFF_MODE_TX                    2
+#define  CC115L_MCSM1_TXOFF_MODE_MASK                  3
+#define CC115L_MCSM0           0x18    /* Main Radio Control State Machine Configuration */
+#define  CC115L_MCSM0_FS_AUTOCAL               4
+#define  CC115L_MCSM0_FS_AUTOCAL_NEVER                 0
+#define  CC115L_MCSM0_FS_AUTOCAL_IDLE_TO_TX            1
+#define  CC115L_MCSM0_FS_AUTOCAL_TX_TO_IDLE            2
+#define  CC115L_MCSM0_FS_AUTOCAL_4TH_TX_TO_IDLE                3
+#define  CC115L_MCSM0_FS_AUTOCAL_MASK                  3
+#define  CC115L_MCSM0_PO_TIMEOUT               2
+#define  CC115L_MCSM0_PO_TIMEOUT_1                     0
+#define  CC115L_MCSM0_PO_TIMEOUT_16                    1
+#define  CC115L_MCSM0_PO_TIMEOUT_64                    2
+#define  CC115L_MCSM0_PO_TIMEOUT_256                   3
+#define  CC115L_MCSM0_PO_TIMEOUT_MASK                  3
+#define  CC115L_MCSM0_XOSC_FORCE_ON            0
+#define CC115L_RESERVED_0X20   0x20    /* Use setting from SmartRF Studio */
+#define CC115L_FREND0          0x22    /* Front End TX Configuration */
+#define CC115L_FSCAL3          0x23    /* Frequency Synthesizer Calibration */
+#define CC115L_FSCAL2          0x24    /* Frequency Synthesizer Calibration */
+#define CC115L_FSCAL1          0x25    /* Frequency Synthesizer Calibration */
+#define CC115L_FSCAL0          0x26    /* Frequency Synthesizer Calibration */
+#define CC115L_RESERVED_0X29   0x29    /* Use setting from SmartRF Studio */
+#define CC115L_RESERVED_0X2A    0x2a           /* Use setting from SmartRF Studio */
+#define CC115L_RESERVED_0X2B    0x2b    /* Use setting from SmartRF Studio */
+#define CC115L_TEST2           0x2c    /* Various Test Settings */
+#define CC115L_TEST1           0x2d    /* Various Test Settings */
+#define CC115L_TEST0           0x2e    /* Various Test Settings */
+
+/* Status registers (use BURST bit to select these) */
+#define CC115L_PARTNUM         (0x30|(1<<CC115L_BURST))        /* Part number for CC115L */
+#define CC115L_VERSION         (0x31|(1<<CC115L_BURST))        /* Current version number */
+#define CC115L_MARCSTATE       (0x35|(1<<CC115L_BURST))        /* Control state machine state */
+#define  CC115L_MARCSTATE_MASK         0x1f
+#define  CC115L_MARCSTATE_SLEEP                0x00
+#define  CC115L_MARCSTATE_IDLE         0x01
+#define  CC115L_MARCSTATE_XOFF         0x02
+#define  CC115L_MARCSTATE_VCOON_MC     0x03
+#define  CC115L_MARCSTATE_REGON_MC     0x04
+#define  CC115L_MARCSTATE_MANCAL       0x05
+#define  CC115L_MARCSTATE_VCOON                0x06
+#define  CC115L_MARCSTATE_REGON                0x07
+#define  CC115L_MARCSTATE_STARTCAL     0x08
+#define  CC115L_MARCSTATE_BWBOOST      0x09
+#define  CC115L_MARCSTATE_FS_LOCK      0x0a
+#define  CC115L_MARCSTATE_ENDCAL       0x0c
+#define  CC115L_MARCSTATE_FSTXON       0x12
+#define  CC115L_MARCSTATE_TX           0x13
+#define  CC115L_MARCSTATE_TX_END       0x14
+#define  CC115L_MARCSTATE_TX_UNDERFLOW 0x16
+#define CC115L_PKTSTATUS       (0x38|(1<<CC115L_BURST))        /* Current GDOx status and packet status */
+#define CC115L_TXBYTES         (0x3a|(1<<CC115L_BURST))        /* Underflow and number of bytes in the TX FIFO */
+#define  CC115L_TXBYTES_TXFIFO_UNDERFLOW       7
+#define  CC115L_TXBYTES_NUM_TX_BYTES           0
+#define  CC115L_TXBYTES_NUM_TX_BYTES_MASK              0x7f
+
+/* Command strobes (no BURST bit for these) */
+#define CC115L_SRES            0x30
+#define CC115L_SFSTXON         0x31
+#define CC115L_SXOFF           0x32
+#define CC115L_SCAL            0x33
+#define CC115L_STX             0x35
+#define CC115L_SIDLE           0x36
+#define CC115L_SPWD            0x39
+#define CC115L_SFTX            0x3b
+#define CC115L_SNOP            0x3d
+
+#define CC115L_PA              0x3e
+#define CC115L_FIFO            0x3f
+
+#define CC115L_FIFO_SIZE       64
+
+/* Status byte */
+#define CC115L_STATUS_CHIP_RDY 7
+#define CC115L_STATUS_STATE    4
+#define  CC115L_STATUS_STATE_IDLE              0
+#define  CC115L_STATUS_STATE_TX                        2
+#define  CC115L_STATUS_STATE_FSTXON            3
+#define  CC115L_STATUS_STATE_CALIBRATE         4
+#define  CC115L_STATUS_STATE_SETTLING          5
+#define  CC115L_STATUS_STATE_TX_FIFO_UNDERFLOW 7
+#define  CC115L_STATUS_STATE_MASK              7
+
+#define CC115L_STATUS_FIFO_BYTES_AVAILABLE     0
+#endif /* _AO_CC115L_H_ */
index 0ebe8429124966790244e0a2fa5083782a0525a6..0f405253c2991207a021281788e9d50788cec074 100644 (file)
@@ -18,7 +18,7 @@
 #include <ao.h>
 #include <ao_companion.h>
 
-#ifdef MEGAMETRUM
+#ifdef TELEMEGA
 #define ao_spi_slow(b)
 #define ao_spi_fast(b)
 #endif
diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c
new file mode 100644 (file)
index 0000000..afd645c
--- /dev/null
@@ -0,0 +1,1654 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef AO_FAT_TEST
+#include "ao.h"
+#endif
+
+#include "ao_fat.h"
+#include "ao_bufio.h"
+
+/* Include FAT commands */
+#ifndef AO_FAT_TEST
+#define FAT_COMMANDS   1
+#endif
+
+/* Spew FAT tracing */
+#define FAT_TRACE      0
+#ifdef DBG
+#undef DBG
+#endif
+
+#if FAT_TRACE
+#define DBG(...) printf(__VA_ARGS__)
+#else
+#define DBG(...)
+#endif
+
+/*
+ * Basic file system types
+ */
+
+typedef ao_fat_offset_t                offset_t;
+typedef ao_fat_sector_t                sector_t;
+typedef ao_fat_cluster_t       cluster_t;
+typedef ao_fat_dirent_t                dirent_t;
+typedef ao_fat_cluster_offset_t        cluster_offset_t;
+
+/* Global FAT lock */
+static uint8_t ao_fat_mutex;
+
+/* Partition information, sector numbers */
+
+static uint8_t partition_type;
+static sector_t        partition_start, partition_end;
+
+#define AO_FAT_BAD_CLUSTER             0xffffff7
+#define AO_FAT_LAST_CLUSTER            0xfffffff
+#define AO_FAT_IS_LAST_CLUSTER(c)              (((c) & 0xffffff8) == 0xffffff8)
+#define AO_FAT_IS_LAST_CLUSTER16(c)    (((c) & 0xfff8) == 0xfff8)
+
+
+#define SECTOR_SIZE    512
+#define SECTOR_MASK    (SECTOR_SIZE - 1)
+#define SECTOR_SHIFT   9
+
+#define DIRENT_SIZE    32
+
+/* File system parameters */
+static uint8_t         sectors_per_cluster;
+static uint32_t                bytes_per_cluster;
+static sector_t                reserved_sector_count;
+static uint8_t         number_fat;
+static dirent_t                root_entries;
+static sector_t        sectors_per_fat;
+static cluster_t       number_cluster;
+static sector_t                fat_start;
+static sector_t        root_start;
+static sector_t        data_start;
+static cluster_t       next_free;
+static uint8_t         filesystem_full;
+
+/* FAT32 extra data */
+static uint8_t         fat32;
+static uint8_t         fsinfo_dirty;
+static cluster_t       root_cluster;
+static sector_t                fsinfo_sector;
+static cluster_t       free_count;
+
+/*
+ * Deal with LSB FAT data structures
+ */
+static uint32_t
+get_u32(uint8_t *base)
+{
+       return ((uint32_t) base[0] |
+               ((uint32_t) base[1] << 8) |
+               ((uint32_t) base[2] << 16) |
+               ((uint32_t) base[3] << 24));
+}
+
+static void
+put_u32(uint8_t *base, uint32_t value)
+{
+       base[0] = value;
+       base[1] = value >> 8;
+       base[2] = value >> 16;
+       base[3] = value >> 24;
+}
+
+static uint16_t
+get_u16(uint8_t *base)
+{
+       return ((uint16_t) base[0] | ((uint16_t) base[1] << 8));
+}
+
+static void
+put_u16(uint8_t *base, uint16_t value)
+{
+       base[0] = value;
+       base[1] = value >> 8;
+}
+
+static uint8_t
+_ao_fat_cluster_valid(cluster_t cluster)
+{
+       return (2 <= cluster && cluster < number_cluster);
+}
+
+/* Start using a sector */
+static uint8_t *
+_ao_fat_sector_get(sector_t sector)
+{
+       sector += partition_start;
+       if (sector >= partition_end)
+               return NULL;
+       return ao_bufio_get(sector);
+}
+
+/* Finish using a sector, 'w' is 1 if modified */
+#define _ao_fat_sector_put(b,w) ao_bufio_put(b,w)
+
+/* Get the next cluster entry in the chain */
+static cluster_t
+_ao_fat_entry_read(cluster_t cluster)
+{
+       sector_t        sector;
+       cluster_t       offset;
+       uint8_t         *buf;
+       cluster_t       ret;
+
+       if (!_ao_fat_cluster_valid(cluster))
+               return 0xfffffff7;
+
+       if (fat32)
+               cluster <<= 2;
+       else
+               cluster <<= 1;
+       sector = cluster >> (SECTOR_SHIFT);
+       offset = cluster & SECTOR_MASK;
+       buf = _ao_fat_sector_get(fat_start + sector);
+       if (!buf)
+               return 0;
+
+       if (fat32) {
+               ret = get_u32(buf + offset);
+               ret &= 0xfffffff;
+       } else {
+               ret = get_u16(buf + offset);
+               if (AO_FAT_IS_LAST_CLUSTER16(ret))
+                       ret |= 0xfff0000;
+       }
+       _ao_fat_sector_put(buf, 0);
+       return ret;
+}
+
+/* Replace the referenced cluster entry in the chain with
+ * 'new_value'. Return the previous value.
+ */
+static cluster_t
+_ao_fat_entry_replace(cluster_t  cluster, cluster_t new_value)
+{
+       sector_t                sector;
+       cluster_offset_t        offset;
+       uint8_t                 *buf;
+       cluster_t               ret;
+       cluster_t               old_value;
+       uint8_t                 fat;
+
+       if (!_ao_fat_cluster_valid(cluster))
+               return 0xfffffff7;
+
+       /* Convert from cluster index to byte index */
+       if (fat32)
+               cluster <<= 2;
+       else
+               cluster <<= 1;
+       sector = cluster >> SECTOR_SHIFT;
+       offset = cluster & SECTOR_MASK;
+
+       new_value &= 0xfffffff;
+       for (fat = 0; fat < number_fat; fat++) {
+               buf = _ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector);
+               if (!buf)
+                       return 0;
+               if (fat32) {
+                       old_value = get_u32(buf + offset);
+                       put_u32(buf + offset, new_value | (old_value & 0xf0000000));
+                       if (fat == 0) {
+                               ret = old_value & 0xfffffff;
+
+                               /* Track the free count if it wasn't marked
+                                * invalid when we mounted the file system
+                                */
+                               if (free_count != 0xffffffff) {
+                                       if (new_value && !ret) {
+                                               --free_count;
+                                               fsinfo_dirty = 1;
+                                       } else if (!new_value && ret) {
+                                               ++free_count;
+                                               fsinfo_dirty = 1;
+                                       }
+                               }
+                       }
+               } else {
+                       if (fat == 0) {
+                               ret = get_u16(buf + offset);
+                               if (AO_FAT_IS_LAST_CLUSTER16(ret))
+                                       ret |= 0xfff0000;
+                       }
+                       put_u16(buf + offset, new_value);
+               }
+               _ao_fat_sector_put(buf, 1);
+       }
+       return ret;
+       
+}
+
+/*
+ * Walk a cluster chain and mark
+ * all of them as free
+ */
+static void
+_ao_fat_free_cluster_chain(cluster_t cluster)
+{
+       while (_ao_fat_cluster_valid(cluster)) {
+               if (cluster < next_free) {
+                       next_free = cluster;
+                       fsinfo_dirty = 1;
+               }
+               cluster = _ao_fat_entry_replace(cluster, 0x00000000);
+       }
+}
+
+/*
+ * _ao_fat_cluster_seek
+ * 
+ * The basic file system operation -- map a file cluster index to a
+ * partition cluster number. Done by computing the cluster number and
+ * then walking that many clusters from the first cluster. Returns
+ * 0xffff if we walk off the end of the file or the cluster chain
+ * is damaged somehow
+ */
+static cluster_t
+_ao_fat_cluster_seek(cluster_t cluster, cluster_t distance)
+{
+       while (distance) {
+               cluster = _ao_fat_entry_read(cluster);
+               if (!_ao_fat_cluster_valid(cluster))
+                       break;
+               distance--;
+       }
+       return cluster;
+}
+
+/*
+ * _ao_fat_cluster_set_size
+ *
+ * Set the number of clusters in the specified chain,
+ * freeing extra ones or alocating new ones as needed
+ *
+ * Returns AO_FAT_BAD_CLUSTER on allocation failure
+ */
+
+static cluster_t
+_ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size)
+{
+       cluster_t       have;
+       cluster_t       last_cluster;
+       cluster_t       next_cluster;
+
+       /* Walk the cluster chain to the
+        * spot where it needs to change. That
+        * will either be the end of the chain (in case it needs to grow),
+        * or after the desired number of clusters, in which case it needs to shrink
+        */
+       next_cluster = first_cluster;
+       last_cluster = 0;
+       DBG("\tclusters:");
+       for (have = 0; have < size; have++) {
+               DBG(" %08x", next_cluster);
+               if (!_ao_fat_cluster_valid(next_cluster))
+                       break;
+               last_cluster = next_cluster;
+               next_cluster = _ao_fat_entry_read(next_cluster);
+       }
+       DBG("\n");
+
+       /* At this point, last_cluster points to the last valid
+        * cluster in the file, if any. That's the spot in the FAT
+        * that needs to be rewritten, either to truncate the file by
+        * writing an END marker, or to extend the file by writing
+        * more clusters. next_cluster will contain the value of the
+        * FAT at last_cluster.
+        *
+        * If this is at the head of the cluster chain, then
+        * last_cluster will be zero and next_cluster will
+        * be the first cluster in the chain.
+        */
+       if (have == size) {
+               /* The file is large enough, truncate as needed */
+               if (_ao_fat_cluster_valid(next_cluster)) {
+                       DBG("truncate between %08x and %08x\n", last_cluster, next_cluster);
+                       if (last_cluster)
+                               /*
+                                * Otherwise, rewrite the last cluster
+                                * in the chain with a LAST marker
+                                */
+                               (void) _ao_fat_entry_replace(last_cluster,
+                                                           AO_FAT_LAST_CLUSTER);
+                       else
+                               /*
+                                * If the file is getting erased, then
+                                * rewrite the directory entry cluster
+                                * value
+                                */
+                               first_cluster = 0;
+
+                       /* Clear the remaining clusters in the chain */
+                       _ao_fat_free_cluster_chain(next_cluster);
+
+                       /* The file system is no longer full (if it was) */
+                       filesystem_full = 0;
+               } else {
+                       DBG("unchanged FAT chain\n");
+                       /* The chain is already the right length, don't mess with it */
+                       ;
+               }
+       } else {
+               cluster_t       need;
+               cluster_t       free;
+
+               if (filesystem_full)
+                       return AO_FAT_BAD_CLUSTER;
+
+               /* Set next_free if it has wrapped or wasn't set before */
+               if (next_free < 2 || number_cluster <= next_free) {
+                       next_free = 2;
+                       fsinfo_dirty = 1;
+               }
+
+               /* See if there are enough free clusters in the file system */
+               need = size - have;
+
+#define loop_cluster   for (free = next_free; need > 0;)
+#define next_cluster                           \
+               if (++free == number_cluster)   \
+                       free = 2;               \
+               if (free == next_free)          \
+                       break;                  \
+
+               loop_cluster {
+                       if (!_ao_fat_entry_read(free))
+                               need--;
+                       next_cluster;
+               }
+
+               /* Still need some, tell the user that we've failed */
+               if (need) {
+                       filesystem_full = 1;
+                       return AO_FAT_BAD_CLUSTER;
+               }
+
+               /* Now go allocate those clusters and
+                * thread them onto the chain
+                */
+               need = size - have;
+               loop_cluster {
+                       if (_ao_fat_entry_read(free) == 0) {
+                               next_free = free + 1;
+                               if (next_free >= number_cluster)
+                                       next_free = 2;
+                               fsinfo_dirty = 1;
+                               DBG("\tadd cluster. old %08x new %08x\n", last_cluster, free);
+                               if (last_cluster)
+                                       _ao_fat_entry_replace(last_cluster, free);
+                               else
+                                       first_cluster = free;
+                               last_cluster = free;
+                               need--;
+                       }
+                       next_cluster;
+               }
+#undef loop_cluster
+#undef next_cluster
+               DBG("\tlast cluster %08x\n", last_cluster);
+               /* Mark the new end of the chain */
+               _ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER);
+       }
+
+       DBG("\tfirst cluster %08x\n", first_cluster);
+       return first_cluster;
+}
+
+/* Start using a root directory entry */
+static uint8_t *
+_ao_fat_root_get(dirent_t e)
+{
+       offset_t                byte = e * DIRENT_SIZE;
+       sector_t                sector = byte >> SECTOR_SHIFT;
+       cluster_offset_t        offset = byte & SECTOR_MASK;
+       uint8_t                 *buf;
+
+       if (fat32) {
+               cluster_t       cluster_distance = sector / sectors_per_cluster;
+               sector_t        sector_index = sector % sectors_per_cluster;
+               cluster_t       cluster = _ao_fat_cluster_seek(root_cluster, cluster_distance);
+
+               if (_ao_fat_cluster_valid(cluster))
+                       sector = data_start + (cluster-2) * sectors_per_cluster + sector_index;
+               else
+                       return NULL;
+       } else {
+               if (e >= root_entries)
+                       return NULL;
+               sector = root_start + sector;
+       }
+
+       buf = _ao_fat_sector_get(sector);
+       if (!buf)
+               return NULL;
+       return buf + offset;
+}
+
+/* Finish using a root directory entry, 'w' is 1 if modified */
+static void
+_ao_fat_root_put(uint8_t *root, dirent_t e, uint8_t write)
+{
+       cluster_offset_t        offset = ((e * DIRENT_SIZE) & SECTOR_MASK);
+       uint8_t                 *buf = root - offset;
+
+       _ao_fat_sector_put(buf, write);
+}
+
+/*
+ * _ao_fat_root_extend
+ *
+ * On FAT32, make the root directory at least 'ents' entries long
+ */
+static int8_t
+_ao_fat_root_extend(dirent_t ents)
+{
+       offset_t        byte_size;
+       cluster_t       cluster_size;
+       if (!fat32)
+               return 0;
+       
+       byte_size = (ents + 1) * 0x20;
+       cluster_size = (byte_size + bytes_per_cluster - 1) / bytes_per_cluster;
+       if (_ao_fat_cluster_set_size(root_cluster, cluster_size) != AO_FAT_BAD_CLUSTER)
+               return 1;
+       return 0;
+}
+               
+/*
+ * _ao_fat_setup_partition
+ * 
+ * Load the boot block and find the first partition
+ */
+static uint8_t
+_ao_fat_setup_partition(void)
+{
+       uint8_t *mbr;
+       uint8_t *partition;
+       uint32_t partition_size;
+
+       mbr = ao_bufio_get(0);
+       if (!mbr)
+               return AO_FAT_FILESYSTEM_MBR_READ_FAILURE;
+
+       /* Check the signature */
+       if (mbr[0x1fe] != 0x55 || mbr[0x1ff] != 0xaa) {
+               DBG ("Invalid MBR signature %02x %02x\n",
+                       mbr[0x1fe], mbr[0x1ff]);
+               ao_bufio_put(mbr, 0);
+               return AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE;
+       }
+
+       /* Check to see if it's actually a boot block, in which
+        * case it's presumably not a paritioned device
+        */
+
+       if (mbr[0] == 0xeb) {
+               partition_start = 0;
+               partition_size = get_u16(mbr + 0x13);
+               if (partition_size == 0)
+                       partition_size = get_u32(mbr + 0x20);
+       } else {
+               /* Just use the first partition */
+               partition = &mbr[0x1be];
+       
+               partition_type = partition[4];
+               switch (partition_type) {
+               case 4:         /* FAT16 up to 32M */
+               case 6:         /* FAT16 over 32M */
+                       break;
+               case 0x0b:      /* FAT32 up to 2047GB */
+               case 0x0c:      /* FAT32 LBA */
+                       break;
+               default:
+                       DBG ("Invalid partition type %02x\n", partition_type);
+                       ao_bufio_put(mbr, 0);
+                       return AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE;
+               }
+
+               partition_start = get_u32(partition+8);
+               partition_size = get_u32(partition+12);
+               if (partition_size == 0) {
+                       DBG ("Zero-sized partition\n");
+                       ao_bufio_put(mbr, 0);
+                       return AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION;
+               }
+       }
+       partition_end = partition_start + partition_size;
+       ao_bufio_put(mbr, 0);
+       return AO_FAT_FILESYSTEM_SUCCESS;
+}
+       
+static uint8_t
+_ao_fat_setup_fs(void)
+{
+       uint8_t         *boot = _ao_fat_sector_get(0);
+       uint32_t        data_sectors;
+
+       if (!boot)
+               return AO_FAT_FILESYSTEM_BOOT_READ_FAILURE;
+
+       /* Check the signature */
+       if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) {
+               DBG ("Invalid BOOT signature %02x %02x\n",
+                       boot[0x1fe], boot[0x1ff]);
+               _ao_fat_sector_put(boot, 0);
+               return AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE;
+       }
+
+       /* Check the sector size */
+       if (get_u16(boot + 0xb) != SECTOR_SIZE) {
+               DBG ("Invalid sector size %d\n",
+                       get_u16(boot + 0xb));
+               _ao_fat_sector_put(boot, 0);
+               return AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE;
+       }
+
+       sectors_per_cluster = boot[0xd];
+       bytes_per_cluster = sectors_per_cluster << SECTOR_SHIFT;
+       reserved_sector_count = get_u16(boot+0xe);
+       number_fat = boot[0x10];
+       root_entries = get_u16(boot + 0x11);
+       sectors_per_fat = get_u16(boot+0x16);
+       fat32 = 0;
+       if (sectors_per_fat == 0) {
+               fat32 = 1;
+               sectors_per_fat = get_u32(boot+0x24);
+               root_cluster = get_u32(boot+0x2c);
+               fsinfo_sector = get_u16(boot + 0x30);
+       }
+       _ao_fat_sector_put(boot, 0);
+
+       free_count = 0xffffffff;
+       next_free = 0;
+       if (fat32 && fsinfo_sector) {
+               uint8_t *fsinfo = _ao_fat_sector_get(fsinfo_sector);
+
+               if (fsinfo) {
+                       free_count = get_u32(fsinfo + 0x1e8);
+                       next_free = get_u32(fsinfo + 0x1ec);
+                       _ao_fat_sector_put(fsinfo, 0);
+               }
+       }
+
+       fat_start = reserved_sector_count;;
+       root_start = fat_start + number_fat * sectors_per_fat;
+       data_start = root_start + ((root_entries * DIRENT_SIZE + SECTOR_MASK) >> SECTOR_SHIFT);
+
+       data_sectors = (partition_end - partition_start) - data_start;
+
+       number_cluster = data_sectors / sectors_per_cluster;
+
+       return AO_FAT_FILESYSTEM_SUCCESS;
+}
+
+/*
+ * State for an open file
+ */
+
+struct ao_file {
+       struct ao_fat_dirent    *dirent;
+       offset_t                offset;
+       offset_t                cluster_offset;
+       cluster_t               cluster;
+       uint8_t                 busy;
+};
+
+#define AO_FAT_NFILE   8
+
+static struct ao_fat_dirent    ao_file_dirent[AO_FAT_NFILE];
+
+static struct ao_fat_dirent *
+_ao_fat_file_dirent_alloc(struct ao_fat_dirent *want)
+{
+       int8_t  d;
+       struct ao_fat_dirent *free = NULL, *dirent;
+
+       for (d = 0; d < AO_FAT_NFILE; d++) {
+
+               dirent = &ao_file_dirent[d];
+               /* See if there's another user of this file already */
+               if (want && dirent->name[0] != 0) {
+                       if (dirent->entry == want->entry)
+                               return dirent;
+               } else {
+                       if (!free) {
+                               free = dirent;
+                               if (!want)
+                                       break;
+                       }
+               }
+       }
+       if (free && want)
+               *free = *want;
+       return free;
+}
+
+static struct ao_file          ao_file_table[AO_FAT_NFILE];
+
+static int8_t
+_ao_fat_fd_alloc(struct ao_fat_dirent *dirent)
+{
+       int8_t  fd;
+
+       for (fd = 0; fd < AO_FAT_NFILE; fd++)
+               if (!ao_file_table[fd].busy) {
+                       ao_file_table[fd].dirent = _ao_fat_file_dirent_alloc(dirent);
+                       ao_file_table[fd].busy = 1;
+                       ao_file_table[fd].offset = 0;
+                       ao_file_table[fd].cluster_offset = 0;
+                       ao_file_table[fd].cluster = ao_file_table[fd].dirent->cluster;
+                               
+                       return fd;
+               }
+       return -AO_FAT_EMFILE;
+}
+
+static void
+_ao_fat_fd_free(int8_t fd)
+{
+       struct ao_file *file = &ao_file_table[fd];
+       struct ao_fat_dirent *dirent = file->dirent;
+       memset(&ao_file_table[fd], '\0', sizeof (struct ao_file));
+
+       /* Check and see if another ao_file references the same dirent */
+       for (fd = 0; fd < AO_FAT_NFILE; fd++)
+               if (ao_file_table[fd].dirent == dirent)
+                       return;
+       memset(dirent, '\0', sizeof (struct ao_fat_dirent));
+}
+
+static struct ao_file *
+_ao_fat_fd_to_file(int8_t fd)
+{
+       struct ao_file *file;
+       if (fd < 0 || AO_FAT_NFILE <= fd)
+               return NULL;
+
+       file = &ao_file_table[fd];
+       if (!file->busy)
+               return NULL;
+       return file;
+}
+
+static uint8_t                 ao_filesystem_setup;
+static uint8_t                 ao_filesystem_status;
+
+static uint8_t
+_ao_fat_setup(void)
+{
+       if (!ao_filesystem_setup) {
+
+               ao_filesystem_setup = 1;
+               ao_bufio_setup();
+       
+               /* Re-initialize all global state; this will help to allow the
+                * file system to get swapped someday
+                */
+               partition_type = partition_start = partition_end = 0;
+               sectors_per_cluster = bytes_per_cluster = reserved_sector_count = 0;
+               number_fat = root_entries = sectors_per_fat = 0;
+               number_cluster = fat_start = root_start = data_start = 0;
+               next_free = filesystem_full = 0;
+               fat32 = fsinfo_dirty = root_cluster = fsinfo_sector = free_count = 0;
+
+               /* Reset open file table */
+               memset(&ao_file_table, '\0', sizeof (ao_file_table));
+
+               ao_filesystem_status = _ao_fat_setup_partition();
+               if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS)
+                       return ao_filesystem_status;
+               ao_filesystem_status = _ao_fat_setup_fs();
+               if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS)
+                       return ao_filesystem_status;
+       }
+       return ao_filesystem_status;
+}
+
+void
+ao_fat_unmount(void)
+{
+       ao_filesystem_setup = 0;
+}
+
+/*
+ * Basic file operations
+ */
+
+static uint32_t
+_ao_fat_current_sector(struct ao_file *file)
+{
+       cluster_t       cluster_offset;
+       uint32_t        sector_offset;
+       uint16_t        sector_index;
+       cluster_t       cluster;
+
+       DBG("current sector offset %d size %d\n",
+           file->offset, file->dirent->size);
+
+       if (file->offset > file->dirent->size) {
+               printf ("file offset %d larger than size %d\n",
+                       file->offset, file->dirent->size);
+               return 0xffffffff;
+       }
+
+       sector_offset = file->offset >> SECTOR_SHIFT;
+
+       if (!file->cluster || file->offset < file->cluster_offset) {
+               file->cluster = file->dirent->cluster;
+               file->cluster_offset = 0;
+               DBG("\treset to start of file %08x\n", file->cluster);
+       }
+
+       if (file->cluster_offset + bytes_per_cluster <= file->offset) {
+               cluster_t       cluster_distance;
+
+               cluster_offset = sector_offset / sectors_per_cluster;
+
+               cluster_distance = cluster_offset - file->cluster_offset / bytes_per_cluster;
+
+               DBG("\tseek forward %d clusters\n", cluster_distance);
+               cluster = _ao_fat_cluster_seek(file->cluster, cluster_distance);
+
+               if (!_ao_fat_cluster_valid(cluster)) {
+                       printf ("invalid cluster %08x\n", cluster);
+                       return 0xffffffff;
+               }
+               file->cluster = cluster;
+               file->cluster_offset = cluster_offset * bytes_per_cluster;
+       }
+
+       sector_index = sector_offset % sectors_per_cluster;
+       DBG("current cluster %08x sector_index %d sector %d\n",
+           file->cluster, sector_index,
+           data_start + (uint32_t) (file->cluster-2) * sectors_per_cluster + sector_index);
+       return data_start + (uint32_t) (file->cluster-2) * sectors_per_cluster + sector_index;
+}
+
+/*
+ * _ao_fat_invaldate_cluster_offset
+ *
+ * When the file size gets shrunk, invalidate
+ * any file structures referencing clusters beyond that point
+ */
+
+static void
+_ao_fat_invalidate_cluster_offset(struct ao_fat_dirent *dirent)
+{
+       int8_t          fd;
+       struct ao_file  *file;
+
+       for (fd = 0; fd < AO_FAT_NFILE; fd++) {
+               file = &ao_file_table[fd];
+               if (!file->busy)
+                       continue;
+               if (file->dirent == dirent) {
+                       if (file->cluster_offset >= dirent->size) {
+                               file->cluster_offset = 0;
+                               file->cluster = dirent->cluster;
+                       }
+               }
+       }
+}
+
+/*
+ * _ao_fat_set_size
+ *
+ * Set the size of the current file, truncating or extending
+ * the cluster chain as needed
+ */
+static int8_t
+_ao_fat_set_size(struct ao_file *file, uint32_t size)
+{
+       uint8_t         *dent;
+       cluster_t       first_cluster;
+       cluster_t       have_clusters, need_clusters;
+
+       DBG ("Set size %d\n", size);
+       if (size == file->dirent->size) {
+               DBG("\tsize match\n");
+               return AO_FAT_SUCCESS;
+       }
+
+       first_cluster = file->dirent->cluster;
+       have_clusters = (file->dirent->size + bytes_per_cluster - 1) / bytes_per_cluster;
+       need_clusters = (size + bytes_per_cluster - 1) / bytes_per_cluster;
+
+       DBG ("\tfirst cluster %08x have %d need %d\n", first_cluster, have_clusters, need_clusters);
+       if (have_clusters != need_clusters) {
+               if (file->cluster && size > file->cluster_offset) {
+                       cluster_t       offset_clusters = (file->cluster_offset + bytes_per_cluster) / bytes_per_cluster;
+                       cluster_t       extra_clusters = need_clusters - offset_clusters;
+                       cluster_t       next_cluster;
+
+                       DBG ("\tset size relative offset_clusters %d extra_clusters %d\n",
+                            offset_clusters, extra_clusters);
+
+                       /* Need one more to account for file->cluster, which we already have */
+                       next_cluster = _ao_fat_cluster_set_size(file->cluster, extra_clusters + 1);
+                       if (next_cluster == AO_FAT_BAD_CLUSTER)
+                               return -AO_FAT_ENOSPC;
+               } else {
+                       DBG ("\tset size absolute need_clusters %d\n", need_clusters);
+                       first_cluster = _ao_fat_cluster_set_size(first_cluster, need_clusters);
+
+                       if (first_cluster == AO_FAT_BAD_CLUSTER)
+                               return -AO_FAT_ENOSPC;
+               }
+       }
+
+       DBG ("\tupdate directory size\n");
+       /* Update the directory entry */
+       dent = _ao_fat_root_get(file->dirent->entry);
+       if (!dent) {
+               printf ("dent update failed\n");
+               return -AO_FAT_EIO;
+       }
+       put_u32(dent + 0x1c, size);
+       put_u16(dent + 0x1a, first_cluster);
+       if (fat32)
+               put_u16(dent + 0x14, first_cluster >> 16);
+       _ao_fat_root_put(dent, file->dirent->entry, 1);
+
+       file->dirent->size = size;
+       file->dirent->cluster = first_cluster;
+       if (have_clusters > need_clusters)
+               _ao_fat_invalidate_cluster_offset(file->dirent);
+       DBG ("set size done\n");
+       return AO_FAT_SUCCESS;
+}
+
+/*
+ * _ao_fat_root_init
+ *
+ * Initialize a root directory entry
+ */
+static void
+_ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr)
+{
+       memset(dent, '\0', 0x20);
+       memmove(dent, name, 11);
+
+       dent[0x0b] = 0x00;
+       dent[0x0c] = 0x00;
+       dent[0x0d] = 0x00;
+
+       /* XXX fix time */
+       put_u16(dent + 0x0e, 0);
+       /* XXX fix date */
+       put_u16(dent + 0x10, 0);
+       /* XXX fix date */
+       put_u16(dent + 0x12, 0);
+
+       /* XXX fix time */
+       put_u16(dent + 0x16, 0);
+       /* XXX fix date */
+       put_u16(dent + 0x18, 0);
+
+       /* cluster number */
+       /* Low cluster bytes */
+       put_u16(dent + 0x1a, 0);
+       /* FAT32 high cluster bytes */
+       put_u16(dent + 0x14, 0);
+
+       /* size */
+       put_u32(dent + 0x1c, 0);
+}
+
+
+static void
+_ao_fat_dirent_init(struct ao_fat_dirent *dirent, uint8_t *dent, uint16_t entry)
+{
+       memcpy(dirent->name, dent + 0x00, 11);
+       dirent->attr = dent[0x0b];
+       dirent->size = get_u32(dent+0x1c);
+       dirent->cluster = get_u16(dent+0x1a);
+       if (fat32)
+               dirent->cluster |= (cluster_t) get_u16(dent + 0x14) << 16;
+       dirent->entry = entry;
+}
+
+/*
+ * _ao_fat_flush_fsinfo
+ *
+ * Write out any fsinfo changes to disk
+ */
+
+static void
+_ao_fat_flush_fsinfo(void)
+{
+       uint8_t *fsinfo;
+
+       if (!fat32)
+               return;
+
+       if (!fsinfo_dirty)
+               return;
+       fsinfo_dirty = 0;
+       if (!fsinfo_sector)
+               return;
+
+       fsinfo = _ao_fat_sector_get(fsinfo_sector);
+       if (fsinfo) {
+               put_u32(fsinfo + 0x1e8, free_count);
+               put_u32(fsinfo + 0x1ec, next_free);
+               _ao_fat_sector_put(fsinfo, 1);
+       }
+}
+
+/*
+ * Public API
+ */
+
+/*
+ * ao_fat_sync
+ *
+ * Flush any pending I/O to storage
+ */
+
+static void
+_ao_fat_sync(void)
+{
+       if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
+               return;
+       _ao_fat_flush_fsinfo();
+       ao_bufio_flush();
+}
+
+void
+ao_fat_sync(void)
+{
+       ao_mutex_get(&ao_fat_mutex);
+       _ao_fat_sync();
+       ao_mutex_put(&ao_fat_mutex);
+}
+
+/*
+ * ao_fat_full
+ *
+ * Returns TRUE if the filesystem cannot take
+ * more data
+ */
+
+int8_t
+ao_fat_full(void)
+{
+       ao_mutex_get(&ao_fat_mutex);
+       if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) {
+               ao_mutex_put(&ao_fat_mutex);
+               return 1;
+       }
+       ao_mutex_put(&ao_fat_mutex);
+       return filesystem_full;
+}
+
+static int8_t
+_ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
+{
+       uint8_t *dent;
+
+       if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
+               return -AO_FAT_EIO;
+
+       for (;;) {
+               dent = _ao_fat_root_get(*entry);
+               if (!dent)
+                       return -AO_FAT_EDIREOF;
+
+               if (dent[0] == AO_FAT_DENT_END) {
+                       _ao_fat_root_put(dent, *entry, 0);
+                       return -AO_FAT_EDIREOF;
+               }
+               if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) {
+                       _ao_fat_dirent_init(dirent, dent, *entry);
+                       _ao_fat_root_put(dent, *entry, 0);
+                       (*entry)++;
+                       return AO_FAT_SUCCESS;
+               }
+               _ao_fat_root_put(dent, *entry, 0);
+               (*entry)++;
+       }
+}
+
+int8_t
+ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
+{
+       int8_t status;
+
+       ao_mutex_get(&ao_fat_mutex);
+       status = _ao_fat_readdir(entry, dirent);
+       ao_mutex_put(&ao_fat_mutex);
+       return status;
+}
+
+/*
+ * ao_fat_open
+ *
+ * Open an existing file.
+ */
+static int8_t
+_ao_fat_open(char name[11], uint8_t mode)
+{
+       uint16_t                entry = 0;
+       struct ao_fat_dirent    dirent;
+       int8_t                  status;
+
+       if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
+               return -AO_FAT_EIO;
+
+       for (;;) {
+               status = _ao_fat_readdir(&entry, &dirent);
+               if (status < 0) {
+                       if (status == -AO_FAT_EDIREOF)
+                               return -AO_FAT_ENOENT;
+                       return status;
+               }
+               if (!memcmp(name, dirent.name, 11)) {
+                       if (AO_FAT_IS_DIR(dirent.attr))
+                               return -AO_FAT_EISDIR;
+                       if (!AO_FAT_IS_FILE(dirent.attr))
+                               return -AO_FAT_EPERM;
+                       if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY))
+                               return -AO_FAT_EACCESS;
+                       return _ao_fat_fd_alloc(&dirent);
+               }
+       }
+       return -AO_FAT_ENOENT;
+}
+
+int8_t
+ao_fat_open(char name[11], uint8_t mode)
+{
+       int8_t status;
+
+       ao_mutex_get(&ao_fat_mutex);
+       status = _ao_fat_open(name, mode);
+       ao_mutex_put(&ao_fat_mutex);
+       return status;
+}
+
+/*
+ * ao_fat_close
+ *
+ * Close the currently open file
+ */
+static int8_t
+_ao_fat_close(int8_t fd)
+{
+       struct ao_file *file;
+
+       file = _ao_fat_fd_to_file(fd);
+       if (!file)
+               return -AO_FAT_EBADF;
+
+       _ao_fat_fd_free(fd);
+       _ao_fat_sync();
+       return AO_FAT_SUCCESS;
+}
+
+int8_t
+ao_fat_close(int8_t fd)
+{
+       int8_t status;
+
+       ao_mutex_get(&ao_fat_mutex);
+       status = _ao_fat_close(fd);
+       ao_mutex_put(&ao_fat_mutex);
+       return status;
+}
+
+/*
+ * ao_fat_creat
+ *
+ * Open and truncate an existing file or
+ * create a new file
+ */
+
+static int8_t
+_ao_fat_creat(char name[11])
+{
+       uint16_t                entry;
+       int8_t                  fd;
+       int8_t                  status;
+       uint8_t                 *dent;
+       struct ao_file          *file;
+
+       if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
+               return -AO_FAT_EIO;
+
+       fd = _ao_fat_open(name, AO_FAT_OPEN_WRITE);
+       if (fd >= 0) {
+               file = &ao_file_table[fd];
+               status = _ao_fat_set_size(file, 0);
+               if (status < 0) {
+                       _ao_fat_close(fd);
+                       fd = status;
+               }
+       } else {
+               if (fd == -AO_FAT_ENOENT) {
+                       entry = 0;
+                       for (;;) {
+                               dent = _ao_fat_root_get(entry);
+                               if (!dent) {
+                               
+                                       if (_ao_fat_root_extend(entry))
+                                               continue;
+                                       fd = -AO_FAT_ENOSPC;
+                                       break;
+                               }
+                               if (dent[0] == AO_FAT_DENT_EMPTY || dent[0] == AO_FAT_DENT_END) {
+                                       fd = _ao_fat_fd_alloc(NULL);
+                                       if (fd < 0) {
+                                               _ao_fat_root_put(dent, entry, 0);
+                                               break;
+                                       }
+
+                                       file = &ao_file_table[fd];
+                                       /* Initialize the dent */
+                                       _ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR);
+
+                                       /* Now initialize the dirent from the dent */
+                                       _ao_fat_dirent_init(file->dirent, dent, entry);
+
+                                       /* And write the dent to storage */
+                                       _ao_fat_root_put(dent, entry, 1);
+
+                                       status = -AO_FAT_SUCCESS;
+                                       break;
+                               } else {
+                                       _ao_fat_root_put(dent, entry, 0);
+                               }
+                               entry++;
+                       }
+               }
+       }
+       return fd;
+}
+
+int8_t
+ao_fat_creat(char name[11])
+{
+       int8_t  status;
+
+       ao_mutex_get(&ao_fat_mutex);
+       status = _ao_fat_creat(name);
+       ao_mutex_put(&ao_fat_mutex);
+       return status;
+}
+
+/*
+ * ao_fat_map_current
+ *
+ * Map the sector pointed at by the current file offset
+ */
+
+static void *
+ao_fat_map_current(struct ao_file *file, int len, cluster_offset_t *offsetp, cluster_offset_t *this_time)
+{
+       cluster_offset_t        offset;
+       sector_t                sector;
+       void                    *buf;
+
+       offset = file->offset & SECTOR_MASK;
+       sector = _ao_fat_current_sector(file);
+       if (sector == 0xffffffff) {
+               printf ("invalid sector at offset %d\n", file->offset);
+               return NULL;
+       }
+       buf = _ao_fat_sector_get(sector);
+       if (!buf)
+               printf ("sector get failed. Sector %d. Partition end %d\n", sector, partition_end);
+       if (offset + len < SECTOR_SIZE)
+               *this_time = len;
+       else
+               *this_time = SECTOR_SIZE - offset;
+       *offsetp = offset;
+       return buf;
+}
+
+/*
+ * ao_fat_read
+ *
+ * Read from the file
+ */
+int
+ao_fat_read(int8_t fd, void *dst, int len)
+{
+       uint8_t                 *dst_b = dst;
+       cluster_offset_t        this_time;
+       cluster_offset_t        offset;
+       uint8_t                 *buf;
+       int                     ret = 0;
+       struct ao_file          *file;
+
+       ao_mutex_get(&ao_fat_mutex);
+       file = _ao_fat_fd_to_file(fd);
+       if (!file) {
+               ret = -AO_FAT_EBADF;
+               goto done;
+       }
+
+       if (file->offset + len > file->dirent->size)
+               len = file->dirent->size - file->offset;
+
+       if (len < 0)
+               len = 0;
+
+       while (len) {
+               buf = ao_fat_map_current(file, len, &offset, &this_time);
+               if (!buf) {
+                       printf ("map_current failed\n");
+                       ret = -AO_FAT_EIO;
+                       break;
+               }
+               memcpy(dst_b, buf + offset, this_time);
+               _ao_fat_sector_put(buf, 0);
+
+               ret += this_time;
+               len -= this_time;
+               dst_b += this_time;
+               file->offset = file->offset + this_time;
+       }
+done:
+       ao_mutex_put(&ao_fat_mutex);
+       return ret;
+}
+
+/*
+ * ao_fat_write
+ *
+ * Write to the file, extended as necessary
+ */
+int
+ao_fat_write(int8_t fd, void *src, int len)
+{
+       uint8_t                 *src_b = src;
+       cluster_offset_t        this_time;
+       cluster_offset_t        offset;
+       uint8_t                 *buf;
+       int                     ret = 0;
+       struct ao_file          *file;
+
+       ao_mutex_get(&ao_fat_mutex);
+       file = _ao_fat_fd_to_file(fd);
+       if (!file) {
+               ret = -AO_FAT_EBADF;
+               goto done;
+       }
+
+       if (file->offset + len > file->dirent->size) {
+               ret = _ao_fat_set_size(file, file->offset + len);
+               if (ret < 0)
+                       goto done;
+       }
+
+       while (len) {
+               buf = ao_fat_map_current(file, len, &offset, &this_time);
+               if (!buf) {
+                       printf ("map_current failed\n");
+                       ret = -AO_FAT_EIO;
+                       break;
+               }
+               memcpy(buf + offset, src_b, this_time);
+               _ao_fat_sector_put(buf, 1);
+
+               ret += this_time;
+               len -= this_time;
+               src_b += this_time;
+               file->offset = file->offset + this_time;
+       }
+done:
+       ao_mutex_put(&ao_fat_mutex);
+       return ret;
+}
+
+/*
+ * ao_fat_seek
+ *
+ * Set the position for the next I/O operation
+ * Note that this doesn't actually change the size
+ * of the file if the requested position is beyond
+ * the current file length, that would take a future
+ * write
+ */
+int32_t
+ao_fat_seek(int8_t fd, int32_t pos, uint8_t whence)
+{
+       offset_t        new_offset;
+       struct ao_file  *file;
+       int32_t         ret;
+
+       ao_mutex_get(&ao_fat_mutex);
+       file = _ao_fat_fd_to_file(fd);
+       if (!file) {
+               ret = -AO_FAT_EBADF;
+               goto done;
+       }
+
+       new_offset = file->offset;
+       switch (whence) {
+       case AO_FAT_SEEK_SET:
+               new_offset = pos;
+               break;
+       case AO_FAT_SEEK_CUR:
+               new_offset += pos;
+               break;
+       case AO_FAT_SEEK_END:
+               new_offset = file->dirent->size + pos;
+               break;
+       }
+       ret = file->offset = new_offset;
+done:
+       ao_mutex_put(&ao_fat_mutex);
+       return ret;
+}
+
+/*
+ * ao_fat_unlink
+ *
+ * Remove a file from the directory, marking
+ * all clusters as free
+ */
+int8_t
+ao_fat_unlink(char name[11])
+{
+       uint16_t                entry = 0;
+       struct ao_fat_dirent    dirent;
+       int8_t                  ret;
+
+       ao_mutex_get(&ao_fat_mutex);
+       if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) {
+               ret = -AO_FAT_EIO;
+               goto done;
+       }
+
+       while (ao_fat_readdir(&entry, &dirent)) {
+               if (memcmp(name, dirent.name, 11) == 0) {
+                       uint8_t *next;
+                       uint8_t *ent;
+                       uint8_t delete;
+
+                       if (AO_FAT_IS_DIR(dirent.attr)) {
+                               ret = -AO_FAT_EISDIR;
+                               goto done;
+                       }
+                       if (!AO_FAT_IS_FILE(dirent.attr)) {
+                               ret = -AO_FAT_EPERM;
+                               goto done;
+                       }
+
+                       _ao_fat_free_cluster_chain(dirent.cluster);
+                       next = _ao_fat_root_get(dirent.entry + 1);
+                       if (next && next[0] != AO_FAT_DENT_END)
+                               delete = AO_FAT_DENT_EMPTY;
+                       else
+                               delete = AO_FAT_DENT_END;
+                       if (next)
+                               _ao_fat_root_put(next, dirent.entry + 1, 0);
+                       ent = _ao_fat_root_get(dirent.entry);
+                       if (ent) {
+                               memset(ent, '\0', DIRENT_SIZE);
+                               *ent = delete;
+                               _ao_fat_root_put(ent, dirent.entry, 1);
+                       }
+                       ao_bufio_flush();
+                       ret = AO_FAT_SUCCESS;
+                       goto done;
+               }
+       }
+       ret = -AO_FAT_ENOENT;
+done:
+       ao_mutex_put(&ao_fat_mutex);
+       return ret;
+}
+
+int8_t
+ao_fat_rename(char old[11], char new[11])
+{
+       return -AO_FAT_EIO;
+}
+
+#if FAT_COMMANDS
+
+static const char *filesystem_errors[] = {
+       [AO_FAT_FILESYSTEM_SUCCESS] = "FAT file system operating normally",
+       [AO_FAT_FILESYSTEM_MBR_READ_FAILURE] = "MBR media read error",
+       [AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE] = "MBR signature invalid",
+       [AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE] = "Unsupported paritition type",
+       [AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION] = "Partition has zero sectors",
+       [AO_FAT_FILESYSTEM_BOOT_READ_FAILURE] = "Boot block media read error",
+       [AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE] = "Boot block signature invalid",
+       [AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE] = "Sector size not 512",
+};
+       
+static void
+ao_fat_mbr_cmd(void)
+{
+       uint8_t status;
+
+       ao_mutex_get(&ao_fat_mutex);
+       status = _ao_fat_setup();
+       if (status == AO_FAT_FILESYSTEM_SUCCESS) {
+               printf ("partition type: %02x\n", partition_type);
+               printf ("partition start: %08x\n", partition_start);
+
+               printf ("partition end:   %08x\n", partition_end);
+
+               printf ("fat32: %d\n", fat32);
+               printf ("sectors per cluster %d\n", sectors_per_cluster);
+               printf ("reserved sectors %d\n", reserved_sector_count);
+               printf ("number of FATs %d\n", number_fat);
+               printf ("root entries %d\n", root_entries);
+               printf ("sectors per fat %d\n", sectors_per_fat);
+
+               printf ("fat  start %d\n", fat_start);
+               printf ("root start %d\n", root_start);
+               printf ("data start %d\n", data_start);
+       } else {
+               printf ("FAT filesystem not available: %s\n", filesystem_errors[status]);
+       }
+       ao_mutex_put(&ao_fat_mutex);
+}
+
+struct ao_fat_attr {
+       uint8_t bit;
+       char    label;
+};
+
+static const struct ao_fat_attr ao_fat_attr[] = {
+       { .bit = AO_FAT_FILE_READ_ONLY, .label = 'R' },
+       { .bit = AO_FAT_FILE_HIDDEN, .label = 'H' },
+       { .bit = AO_FAT_FILE_SYSTEM, .label = 'S' },
+       { .bit = AO_FAT_FILE_VOLUME_LABEL, .label = 'V' },
+       { .bit = AO_FAT_FILE_DIRECTORY, .label = 'D' },
+       { .bit = AO_FAT_FILE_ARCHIVE, .label = 'A' },
+};
+
+#define NUM_FAT_ATTR   (sizeof (ao_fat_attr) / sizeof (ao_fat_attr[0]))
+
+static void
+ao_fat_list_cmd(void)
+{
+       uint16_t                entry = 0;
+       struct ao_fat_dirent    dirent;
+       int                     i;
+       int8_t                  status;
+
+       while ((status = ao_fat_readdir(&entry, &dirent)) == AO_FAT_SUCCESS) {
+               for (i = 0; i < 8; i++)
+                       putchar(dirent.name[i]);
+               putchar('.');
+               for (; i < 11; i++)
+                       putchar(dirent.name[i]);
+               for (i = 0; i < NUM_FAT_ATTR; i++)
+                       putchar (dirent.attr & ao_fat_attr[i].bit ? ao_fat_attr[i].label : ' ');
+               printf (" @%08x %d\n", dirent.cluster, dirent.size);
+       }
+       if (status != -AO_FAT_EDIREOF)
+               printf ("readdir failed: %d\n", status);
+}
+
+static uint8_t
+ao_fat_parse_name(char name[11])
+{
+       uint8_t c;
+
+       name[0] = '\0';
+       ao_cmd_white();
+       c = 0;
+       while (ao_cmd_lex_c != '\n') {
+               if (ao_cmd_lex_c == '.') {
+                       for (; c < 8; c++)
+                               name[c] = ' ';
+               } else {
+                       if (c < 11)
+                               name[c++] = ao_cmd_lex_c;
+               }
+               ao_cmd_lex();
+       }
+       while (c < 11)
+               name[c++] = ' ';
+}
+
+static void
+ao_fat_dump_cmd(void)
+{
+       char            name[11];
+       int8_t          fd;
+       int             cnt, i;
+       char            buf[32];
+
+       ao_fat_parse_name(name);
+       if (name[0] == '\0') {
+               ao_cmd_status = ao_cmd_syntax_error;
+               return;
+       }
+               
+       fd = ao_fat_open(name, AO_FAT_OPEN_READ);
+       if (fd < 0) {
+               printf ("Open failed: %d\n", fd);
+               return;
+       }
+       while ((cnt = ao_fat_read(fd, buf, sizeof(buf))) > 0) {
+               for (i = 0; i < cnt; i++)
+                       putchar(buf[i]);
+       }
+       ao_fat_close(fd);
+}
+
+static void
+ao_fat_write_cmd(void)
+{
+       char            name[11];
+       int8_t          fd;
+       int             cnt, i;
+       char            buf[64];
+       char            c;
+       int             status;
+
+       ao_fat_parse_name(name);
+       if (name[0] == '\0') {
+               ao_cmd_status = ao_cmd_syntax_error;
+               return;
+       }
+               
+       fd = ao_fat_creat(name);
+       if (fd < 0) {
+               printf ("Open failed: %d\n", fd);
+               return;
+       }
+       flush();
+       while ((c = getchar()) != 4) {
+               if (c == '\r') c = '\n';
+               if (ao_echo()) {
+                       if (c == '\n') putchar ('\r');
+                       putchar(c); flush();
+               }
+               status = ao_fat_write(fd, &c, 1);
+               if (status != 1) {
+                       printf ("Write failure %d\n", status);
+                       break;
+               }
+       }
+       ao_fat_close(fd);
+}
+
+static void
+put32(uint32_t a)
+{
+       ao_cmd_put16(a >> 16);
+       ao_cmd_put16(a);
+}
+
+static void
+ao_fat_hexdump_cmd(void)
+{
+       char            name[11];
+       int8_t          fd;
+       int             cnt, i;
+       char            buf[8];
+       uint32_t        addr;
+
+       ao_fat_parse_name(name);
+       if (name[0] == '\0') {
+               ao_cmd_status = ao_cmd_syntax_error;
+               return;
+       }
+               
+       fd = ao_fat_open(name, AO_FAT_OPEN_READ);
+       if (fd < 0) {
+               printf ("Open failed: %d\n", fd);
+               return;
+       }
+       addr = 0;
+       while ((cnt = ao_fat_read(fd, buf, sizeof(buf))) > 0) {
+               put32(addr);
+               for (i = 0; i < cnt; i++) {
+                       putchar(' ');
+                       ao_cmd_put8(buf[i]);
+               }
+               putchar('\n');
+               addr += cnt;
+       }
+       ao_fat_close(fd);
+}
+
+static const struct ao_cmds ao_fat_cmds[] = {
+       { ao_fat_mbr_cmd,       "M\0Show FAT MBR and other info" },
+       { ao_fat_list_cmd,      "F\0List FAT directory" },
+       { ao_fat_dump_cmd,      "D <name>\0Dump FAT file" },
+       { ao_fat_write_cmd,     "W <name>\0Write FAT file (end with ^D)" },
+       { ao_fat_hexdump_cmd,   "H <name>\0HEX dump FAT file" },
+       { 0, NULL },
+};
+
+#endif
+
+void
+ao_fat_init(void)
+{
+       ao_bufio_init();
+       ao_cmd_register(&ao_fat_cmds[0]);
+}
diff --git a/src/drivers/ao_fat.h b/src/drivers/ao_fat.h
new file mode 100644 (file)
index 0000000..0143536
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_FAT_H_
+#define _AO_FAT_H_
+
+void
+ao_fat_init(void);
+
+#define AO_FAT_FILE_REGULAR            0x00
+#define AO_FAT_FILE_READ_ONLY          0x01
+#define AO_FAT_FILE_HIDDEN             0x02
+#define AO_FAT_FILE_SYSTEM             0x04
+#define AO_FAT_FILE_VOLUME_LABEL       0x08
+#define AO_FAT_FILE_DIRECTORY          0x10
+#define AO_FAT_FILE_ARCHIVE            0x20
+
+#define AO_FAT_DENT_EMPTY              0xe5
+#define AO_FAT_DENT_END                        0x00
+
+#define AO_FAT_IS_FILE(attr)   (((attr) & (AO_FAT_FILE_VOLUME_LABEL|AO_FAT_FILE_DIRECTORY)) == 0)
+#define AO_FAT_IS_DIR(attr)    (((attr) & (AO_FAT_FILE_DIRECTORY|AO_FAT_FILE_VOLUME_LABEL)) == AO_FAT_FILE_DIRECTORY)
+
+/* API error codes */
+#define AO_FAT_SUCCESS                 0
+#define AO_FAT_EPERM                   1
+#define AO_FAT_ENOENT                  2
+#define AO_FAT_EIO                     4
+#define AO_FAT_EBADF                   9
+#define AO_FAT_EACCESS                 13
+#define AO_FAT_EEXIST                  17
+#define AO_FAT_ENOTDIR                 20
+#define AO_FAT_EISDIR                  21
+#define AO_FAT_EMFILE                  24
+#define AO_FAT_EFBIG                   27
+#define AO_FAT_ENOSPC                  28
+#define AO_FAT_EDIREOF                 29
+
+/* ao_fat_setup return values */
+#define AO_FAT_FILESYSTEM_SUCCESS                      0
+#define AO_FAT_FILESYSTEM_MBR_READ_FAILURE             1
+#define AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE                2
+#define AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE       3
+#define AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION         4
+
+#define AO_FAT_FILESYSTEM_BOOT_READ_FAILURE            5
+#define AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE       6
+#define AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE          7
+
+void
+ao_fat_sync(void);
+
+void
+ao_fat_unmount(void);
+
+int8_t
+ao_fat_full(void);
+
+int8_t
+ao_fat_open(char name[11], uint8_t mode);
+
+#define AO_FAT_OPEN_READ               0
+#define AO_FAT_OPEN_WRITE              1
+#define AO_FAT_OPEN_RW                 2
+
+int8_t
+ao_fat_creat(char name[11]);
+
+int8_t
+ao_fat_close(int8_t fd);
+
+int
+ao_fat_read(int8_t fd, void *dest, int len);
+
+int
+ao_fat_write(int8_t fd, void *src, int len);
+
+#define AO_FAT_SEEK_SET        0
+#define AO_FAT_SEEK_CUR        1
+#define AO_FAT_SEEK_END        2
+
+int32_t
+ao_fat_seek(int8_t fd, int32_t pos, uint8_t whence);
+
+int8_t
+ao_fat_unlink(char name[11]);
+
+int8_t
+ao_fat_rename(char old[11], char new[11]);
+
+/*
+ * Byte offset within a file. Supports files up to 2GB in size
+ */
+typedef int32_t                ao_fat_offset_t;
+
+/*
+ * Cluster index in partition data space
+ */
+typedef uint32_t       ao_fat_cluster_t;
+
+/*
+ * Sector offset within partition
+ */
+typedef uint32_t       ao_fat_sector_t;
+
+/*
+ * Index within the root directory
+ */
+typedef uint16_t       ao_fat_dirent_t;
+
+/*
+ * Offset within a cluster (or sector)
+ */
+typedef uint16_t       ao_fat_cluster_offset_t;
+
+struct ao_fat_dirent {
+       char                    name[11];
+       uint8_t                 attr;
+       uint32_t                size;
+       ao_fat_cluster_t        cluster;
+       uint16_t                entry;
+};
+
+int8_t
+ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent);
+
+#endif /* _AO_FAT_H_ */
diff --git a/src/drivers/ao_log_fat.c b/src/drivers/ao_log_fat.c
new file mode 100644 (file)
index 0000000..af77401
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_log.h"
+#include "ao_fat.h"
+
+static uint8_t log_year, log_month, log_day;
+static uint8_t log_open;
+static int8_t  log_fd;
+static uint8_t log_mutex;
+
+static void
+ao_log_open(void)
+{
+       char    name[12];
+       int8_t  status;
+
+       sprintf(name,"%04d%02d%02dLOG", 2000 + log_year, log_month, log_day);
+       status = ao_fat_open(name, AO_FAT_OPEN_WRITE);
+       if (status >= 0) {
+               log_fd = status;
+               ao_fat_seek(log_fd, 0, AO_FAT_SEEK_END);
+               log_open = 1;
+       } else if (status == -AO_FAT_ENOENT) {
+               status = ao_fat_creat(name);
+               if (status == AO_FAT_SUCCESS) {
+                       log_fd = status;
+                       log_open = 1;
+               }
+       } 
+}
+
+static void
+ao_log_close(void)
+{
+       if (log_open) {
+               log_open = 0;
+               ao_fat_close(log_fd);
+               log_fd = -1;
+       }
+}
+
+uint8_t
+ao_log_full(void)
+{
+       return ao_fat_full();
+}
+
+uint8_t
+ao_log_mega(struct ao_log_mega *log)
+{
+       uint8_t wrote = 0;
+       ao_mutex_get(&log_mutex);
+       if (log->type == AO_LOG_GPS_TIME) {
+               if (log_open &&
+                   (log_year != log->u.gps.year ||
+                    log_month != log->u.gps.month ||
+                    log_day != log->u.gps.day)) {
+                       ao_log_close();
+               }
+               if (!log_open) {
+                       log_year = log->u.gps.year;
+                       log_month = log->u.gps.month;
+                       log_day = log->u.gps.day;
+                       ao_log_open();
+               }
+       }
+       if (log_open) {
+               wrote = ao_fat_write(log_fd, log, sizeof (*log)) == AO_FAT_SUCCESS;
+               ao_fat_sync();
+       }
+       ao_mutex_put(&log_mutex);
+       return wrote;
+}
+
+void
+ao_log_flush(void)
+{
+       ao_fat_sync();
+}
diff --git a/src/drivers/ao_rfpa0133.c b/src/drivers/ao_rfpa0133.c
new file mode 100644 (file)
index 0000000..a98e261
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+static void
+ao_rfpa0133_set_power(uint8_t power)
+{
+       ao_gpio_set(AO_PA_GAIN_8_GPIO, AO_PA_GAIN_8_PIN, AO_PA_GAIN_8, power & 1);
+       ao_gpio_set(AO_PA_GAIN_16_GPIO, AO_PA_GAIN_16_PIN, AO_PA_GAIN_16, (power >> 1) & 1);
+}
+
+void
+ao_radio_pa_on(void)
+{
+       ao_rfpa0133_set_power(ao_config.radio_amp);
+       ao_gpio_set(AO_PA_POWER_GPIO, AO_PA_POWER_PIN, AO_PA_POWER, 1);
+}
+
+void
+ao_radio_pa_off(void)
+{
+       ao_gpio_set(AO_PA_POWER_GPIO, AO_PA_POWER_PIN, AO_PA_POWER, 0);
+       ao_rfpa0133_set_power(0);
+}
+
+void
+ao_radio_pa_init(void)
+{
+       ao_enable_output(AO_PA_POWER_GPIO, AO_PA_POWER_PIN, AO_PA_POWER, 0);
+       ao_enable_output(AO_PA_GAIN_8_GPIO, AO_PA_GAIN_8_PIN, AO_PA_GAIN_8, 0);
+       ao_enable_output(AO_PA_GAIN_16_GPIO, AO_PA_GAIN_16_PIN, AO_PA_GAIN_16, 0);
+}
diff --git a/src/drivers/ao_rfpa0133.h b/src/drivers/ao_rfpa0133.h
new file mode 100644 (file)
index 0000000..2ba7f69
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_RFPA0133_H_
+#define _AO_RFPA0133_H_
+
+void
+ao_rfpa0133_on(void);
+
+void
+ao_rfpa0133_off(void);
+
+void
+ao_rfpa0133_init(void);
+
+#endif /* _AO_RFPA0133_H_ */
diff --git a/src/drivers/ao_sdcard.c b/src/drivers/ao_sdcard.c
new file mode 100644 (file)
index 0000000..c13017f
--- /dev/null
@@ -0,0 +1,700 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_sdcard.h"
+
+extern uint8_t ao_radio_mutex;
+
+#define ao_sdcard_get_slow() do { ao_mutex_get(&ao_radio_mutex); ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_250kHz); } while (0)
+#define ao_sdcard_get()        do { ao_mutex_get(&ao_radio_mutex); ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_FAST); } while (0)
+#define ao_sdcard_put() do { ao_spi_put(AO_SDCARD_SPI_BUS); ao_mutex_put(&ao_radio_mutex); } while (0)
+#define ao_sdcard_send_fixed(d,l)      ao_spi_send_fixed((d), (l), AO_SDCARD_SPI_BUS)
+#define ao_sdcard_send(d,l)            ao_spi_send((d), (l), AO_SDCARD_SPI_BUS)
+#define ao_sdcard_recv(d,l)            ao_spi_recv((d), (l), AO_SDCARD_SPI_BUS)
+#define ao_sdcard_select()             ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,0)
+#define ao_sdcard_deselect()           ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,1)
+
+/* Include SD card commands */
+#define SDCARD_DEBUG   0
+
+/* Spew SD tracing */
+#define SDCARD_TRACE   0
+
+/* Emit error and warning messages */
+#define SDCARD_WARN    0
+
+static uint8_t initialized;
+static uint8_t present;
+static uint8_t mutex;
+static enum ao_sdtype sdtype;
+
+#define ao_sdcard_lock()       ao_mutex_get(&mutex)
+#define ao_sdcard_unlock()     ao_mutex_put(&mutex)
+
+#if SDCARD_TRACE
+#define DBG(...) printf(__VA_ARGS__)
+#else
+#define DBG(...)
+#endif
+
+#if SDCARD_WARN
+#define WARN(...) printf(__VA_ARGS__)
+#else
+#define WARN(...)
+#endif
+
+#define later(x,y)     ((int16_t) ((x) - (y)) >= 0)
+
+/*
+ * Wait while the card is busy. The card will return a stream of 0xff
+ * when it is ready to accept a command
+ */
+
+static uint8_t
+ao_sdcard_wait_busy(void)
+{
+       uint16_t        timeout = ao_time() + SDCARD_BUSY_TIMEOUT;
+       uint8_t         reply;
+       for (;;) {
+               ao_sdcard_recv(&reply, 1);
+               DBG("\t\twait busy %02x\n", reply);
+               if (reply == 0xff)
+                       break;
+               if (later(ao_time(), timeout)) {
+                       WARN("wait busy timeout\n");
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+
+/*
+ * Send an SD command and await the status reply
+ */
+
+static uint8_t
+ao_sdcard_send_cmd(uint8_t cmd, uint32_t arg)
+{
+       uint8_t data[6];
+       uint8_t reply;
+       int i;
+       uint16_t timeout;
+
+       DBG ("\tsend_cmd %d arg %08x\n", cmd, arg);
+
+       /* Wait for the card to not be busy */
+       if (cmd != SDCARD_GO_IDLE_STATE) {
+               if (!ao_sdcard_wait_busy())
+                       return SDCARD_STATUS_TIMEOUT;
+       }
+       
+       data[0] = cmd & 0x3f | 0x40;
+       data[1] = arg >> 24;
+       data[2] = arg >> 16;
+       data[3] = arg >> 8;
+       data[4] = arg;
+       if (cmd == SDCARD_GO_IDLE_STATE)
+               data[5] = 0x95; /* Valid for 0 arg */
+       else if (cmd == SDCARD_SEND_IF_COND)
+               data[5] = 0x87; /* Valid for 0x1aa arg */
+       else
+               data[5] = 0xff; /* no CRC */
+       ao_sdcard_send(data, 6);
+
+       /* The first reply byte will be the status,
+        * which must have the high bit clear
+        */
+       timeout = ao_time() + SDCARD_CMD_TIMEOUT;
+       for (;;) {
+               ao_sdcard_recv(&reply, 1);
+               DBG ("\t\tgot byte %02x\n", reply);
+               if ((reply & 0x80) == 0)
+                       break;
+               if (later(ao_time(), timeout)) {
+                       WARN("send_cmd %02x timeout\n", cmd);
+                       return SDCARD_STATUS_TIMEOUT;
+               }
+       }
+#if SDCARD_WARN
+       if (reply != SDCARD_STATUS_READY_STATE && reply != SDCARD_STATUS_IDLE_STATE)
+               WARN("send_cmd %d failed %02x\n", cmd, reply);
+#endif
+       return reply;
+}
+
+/*
+ * Retrieve any reply, discarding the trailing CRC byte
+ */
+static void
+ao_sdcard_recv_reply(uint8_t *reply, int len)
+{
+       uint8_t discard;
+
+       if (len)
+               ao_sdcard_recv(reply, len);
+       /* trailing byte */
+       ao_sdcard_recv(&discard, 1);
+}
+
+/*
+ * Switch to 'idle' state. This is used to get the card into SPI mode
+ */
+static uint8_t
+ao_sdcard_go_idle_state(void)
+{
+       uint8_t ret;
+
+       DBG ("go_idle_state\n");
+       ao_sdcard_select();
+       ret = ao_sdcard_send_cmd(SDCARD_GO_IDLE_STATE, 0);
+       ao_sdcard_recv_reply(NULL, 0);
+       ao_sdcard_deselect();
+       DBG ("\tgo_idle_state status %02x\n", ret);
+       return ret;
+}
+
+static uint8_t
+ao_sdcard_send_op_cond(void)
+{
+       uint8_t ret;
+
+       DBG ("send_op_cond\n");
+       ao_sdcard_select();
+       ret = ao_sdcard_send_cmd(SDCARD_SEND_OP_COND, 0);
+       ao_sdcard_recv_reply(NULL, 0);
+       ao_sdcard_deselect();
+       DBG ("\tsend_op_cond %02x\n", ret);
+       return ret;
+}
+
+static uint8_t
+ao_sdcard_send_if_cond(uint32_t arg, uint8_t send_if_cond_response[4])
+{
+       uint8_t ret;
+
+       DBG ("send_if_cond\n");
+       ao_sdcard_select();
+       ret = ao_sdcard_send_cmd(SDCARD_SEND_IF_COND, arg);
+       if (ret != SDCARD_STATUS_IDLE_STATE) {
+               DBG ("\tsend_if_cond failed %02x\n", ret);
+               return ret;
+       }
+       ao_sdcard_recv_reply(send_if_cond_response, 4);
+       DBG ("send_if_cond status %02x response %02x %02x %02x %02x\n",
+               ret,
+               send_if_cond_response[0],
+               send_if_cond_response[1],
+               send_if_cond_response[2],
+               send_if_cond_response[3]);
+       ao_sdcard_deselect();
+       return ret;
+}
+
+/*
+ * _ao_sdcard_send_status
+ *
+ * Get the 2-byte status value.
+ *
+ * Called from other functions with CS held low already,
+ * hence prefixing the name with '_'
+ */
+static uint16_t
+_ao_sdcard_send_status(void)
+{
+       uint8_t ret;
+       uint8_t extra;
+
+       DBG ("send_status\n");
+       ret = ao_sdcard_send_cmd(SDCARD_SEND_STATUS, 0);
+       ao_sdcard_recv_reply(&extra, 1);
+       if (ret != SDCARD_STATUS_READY_STATE)
+               DBG ("\tsend_if_cond failed %02x\n", ret);
+       return ret | (extra << 8);
+}
+
+/*
+ * ao_sdcard_set_blocklen
+ *
+ * Set the block length for future read and write commands
+ */
+static uint8_t
+ao_sdcard_set_blocklen(uint32_t blocklen)
+{
+       uint8_t ret;
+
+       DBG ("set_blocklen %d\n", blocklen);
+       ao_sdcard_select();
+       ret = ao_sdcard_send_cmd(SDCARD_SET_BLOCKLEN, blocklen);
+       ao_sdcard_recv_reply(NULL, 0);
+       ao_sdcard_deselect();
+       if (ret != SDCARD_STATUS_READY_STATE)
+               DBG ("\tsend_if_cond failed %02x\n", ret);
+       return ret;
+}
+
+/*
+ * _ao_sdcard_app_cmd
+ *
+ * Send the app command prefix
+ *
+ * Called with the CS held low, hence
+ * the '_' prefix
+ */
+static uint8_t
+_ao_sdcard_app_cmd(void)
+{
+       uint8_t ret;
+
+       DBG ("app_cmd\n");
+       ret = ao_sdcard_send_cmd(SDCARD_APP_CMD, 0);
+       ao_sdcard_recv_reply(NULL, 0);
+       DBG ("\tapp_cmd status %02x\n");
+       return ret;
+}
+
+static uint8_t
+ao_sdcard_app_send_op_cond(uint32_t arg)
+{
+       uint8_t ret;
+
+       DBG("send_op_comd\n");
+       ao_sdcard_select();
+       ret = _ao_sdcard_app_cmd();
+       if (ret != SDCARD_STATUS_IDLE_STATE)
+               goto bail;
+       ret = ao_sdcard_send_cmd(SDCARD_APP_SEND_OP_COMD, arg);
+       ao_sdcard_recv_reply(NULL, 0);
+bail:
+       ao_sdcard_deselect();
+       DBG ("\tapp_send_op_cond status %02x\n", ret);
+       return ret;
+}
+
+static uint8_t
+ao_sdcard_read_ocr(uint8_t read_ocr_response[4])
+{
+       uint8_t ret;
+
+       DBG ("read_ocr\n");
+       ao_sdcard_select();
+       ret = ao_sdcard_send_cmd(SDCARD_READ_OCR, 0);
+       if (ret != SDCARD_STATUS_READY_STATE)
+               DBG ("\tread_ocr failed %02x\n", ret);
+       else {
+               ao_sdcard_recv_reply(read_ocr_response, 4);
+               DBG ("\tread_ocr status %02x response %02x %02x %02x %02x\n", ret,
+                       read_ocr_response[0], read_ocr_response[1],
+                       read_ocr_response[2], read_ocr_response[3]);
+       }
+       ao_sdcard_deselect();
+       return ret;
+}
+
+/*
+ * Follow the flow-chart defined by the SD group to
+ * initialize the card and figure out what kind it is
+ */
+static void
+ao_sdcard_setup(void)
+{
+       int     i;
+       uint8_t ret;
+       uint8_t response[10];
+
+       DBG ("Testing sdcard\n");
+
+       ao_sdcard_get_slow();
+       /*
+        * min 74 clocks with CS high
+        */
+       ao_sdcard_send_fixed(0xff, 10);
+
+       /* Reset the card and get it into SPI mode */
+       for (i = 0; i < SDCARD_IDLE_RETRY; i++) {
+               if (ao_sdcard_go_idle_state() == SDCARD_STATUS_IDLE_STATE)
+                       break;
+       }
+       if (i == SDCARD_IDLE_RETRY)
+               goto bail;
+
+       /* Figure out what kind of card we have */
+       sdtype = ao_sdtype_unknown;
+
+       if (ao_sdcard_send_if_cond(0x1aa, response) == SDCARD_STATUS_IDLE_STATE) {
+               uint32_t        arg = 0;
+               uint8_t         sdver2 = 0;
+
+               /* Check for SD version 2 */
+               if ((response[2] & 0xf) == 1 && response[3] == 0xaa) {
+                       arg = 0x40000000;
+                       sdver2 = 1;
+               }
+
+               for (i = 0; i < SDCARD_OP_COND_RETRY; i++) {
+                       ao_delay(AO_MS_TO_TICKS(10));
+                       ret = ao_sdcard_app_send_op_cond(arg);
+                       if (ret != SDCARD_STATUS_IDLE_STATE)
+                               break;
+               }
+               if (ret != SDCARD_STATUS_READY_STATE) {
+                       /* MMC */
+                       for (i = 0; i < SDCARD_OP_COND_RETRY; i++) {
+                               ao_delay(AO_MS_TO_TICKS(10));
+                               ret = ao_sdcard_send_op_cond();
+                               if (ret != SDCARD_STATUS_IDLE_STATE)
+                                       break;
+                       }
+                       if (ret != SDCARD_STATUS_READY_STATE)
+                               goto bail;
+                       sdtype = ao_sdtype_mmc3;
+               } else {
+                       /* SD */
+                       if (sdver2 != 0) {
+                               ret = ao_sdcard_read_ocr(response);
+                               if (ret != SDCARD_STATUS_READY_STATE)
+                                       goto bail;
+                               if ((response[0] & 0xc0) == 0xc0)
+                                       sdtype = ao_sdtype_sd2block;
+                               else
+                                       sdtype = ao_sdtype_sd2byte;
+                       } else {
+                               sdtype = ao_sdtype_sd1;
+                       }
+               }
+
+               /* For everything but SDHC cards, set the block length */
+               if (sdtype != ao_sdtype_sd2block) {
+                       ret = ao_sdcard_set_blocklen(512);
+                       if (ret != SDCARD_STATUS_READY_STATE)
+                               DBG ("set_blocklen failed, ignoring\n");
+               }
+       }
+
+       DBG ("SD card detected, type %d\n", sdtype);
+bail:
+       ao_sdcard_put();
+}
+
+static uint8_t
+_ao_sdcard_reset(void)
+{
+       int i;
+       uint8_t ret;
+       uint8_t response[10];
+
+       for (i = 0; i < SDCARD_IDLE_RETRY; i++) {
+               if (ao_sdcard_go_idle_state() == SDCARD_STATUS_IDLE_STATE)
+                       break;
+       }
+       if (i == SDCARD_IDLE_RETRY) {
+               ret = 0x3f;
+               goto bail;
+       }
+
+       /* Follow the setup path to get the card out of idle state and
+        * up and running again
+        */
+       if (ao_sdcard_send_if_cond(0x1aa, response) == SDCARD_STATUS_IDLE_STATE) {
+               uint32_t        arg = 0;
+               uint8_t         sdver2 = 0;
+
+               /* Check for SD version 2 */
+               if ((response[2] & 0xf) == 1 && response[3] == 0xaa) {
+                       arg = 0x40000000;
+                       sdver2 = 1;
+               }
+
+               for (i = 0; i < SDCARD_IDLE_RETRY; i++) {
+                       ret = ao_sdcard_app_send_op_cond(arg);
+                       if (ret != SDCARD_STATUS_IDLE_STATE)
+                               break;
+               }
+
+               if (ret != SDCARD_STATUS_READY_STATE) {
+                       /* MMC */
+                       for (i = 0; i < SDCARD_IDLE_RETRY; i++) {
+                               ret = ao_sdcard_send_op_cond();
+                               if (ret != SDCARD_STATUS_IDLE_STATE)
+                                       break;
+                       }
+                       if (ret != SDCARD_STATUS_READY_STATE)
+                               goto bail;
+               }
+
+               /* For everything but SDHC cards, set the block length */
+               if (sdtype != ao_sdtype_sd2block) {
+                       ret = ao_sdcard_set_blocklen(512);
+                       if (ret != SDCARD_STATUS_READY_STATE)
+                               DBG ("set_blocklen failed, ignoring\n");
+               }
+       }
+bail:
+       return ret;
+}
+
+/*
+ * The card will send 0xff until it is ready to send
+ * the data block at which point it will send the START_BLOCK
+ * marker followed by the data. This function waits while
+ * the card is sending 0xff
+ */
+static uint8_t
+ao_sdcard_wait_block_start(void)
+{
+       uint8_t         v;
+       uint16_t        timeout = ao_time() + SDCARD_BLOCK_TIMEOUT;
+
+       DBG ("\twait_block_start\n");
+       for (;;) {
+               ao_sdcard_recv(&v, 1);
+               DBG("\t\trecv %02x\n", v);
+               if (v != 0xff)
+                       break;
+               if (later(ao_time(), timeout)) {
+                       printf ("wait block start timeout\n");
+                       return 0xff;
+               }
+       }
+       return v;
+}
+
+/*
+ * Read a block of 512 bytes from the card
+ */
+uint8_t
+ao_sdcard_read_block(uint32_t block, uint8_t *data)
+{
+       uint8_t ret;
+       uint8_t start_block;
+       uint8_t crc[2];
+       int tries;
+
+       ao_sdcard_lock();
+       if (!initialized) {
+               ao_sdcard_setup();
+               initialized = 1;
+               if (sdtype != ao_sdtype_unknown)
+                       present = 1;
+       }
+       if (!present) {
+               ao_sdcard_unlock();
+               return 0;
+       }
+       DBG("read block %d\n", block);
+       if (sdtype != ao_sdtype_sd2block)
+               block <<= 9;
+
+       ao_sdcard_get();
+       for (tries = 0; tries < 10; tries++) {
+               ao_sdcard_select();
+
+               ret = ao_sdcard_send_cmd(SDCARD_READ_BLOCK, block);
+               ao_sdcard_recv_reply(NULL, 0);
+               if (ret != SDCARD_STATUS_READY_STATE) {
+                       uint16_t        status;
+                       WARN ("read block command failed %d status %02x\n", block, ret);
+                       status = _ao_sdcard_send_status();
+                       WARN ("\tstatus now %04x\n", status);
+                       goto bail;
+               }
+
+               ao_sdcard_send_fixed(0xff, 1);
+
+               /* Wait for the data start block marker */
+               start_block = ao_sdcard_wait_block_start();
+               if (start_block != SDCARD_DATA_START_BLOCK) {
+                       WARN ("wait block start failed %02x\n", start_block);
+                       ret = 0x3f;
+                       goto bail;
+               }
+
+               ao_sdcard_recv(data, 512);
+               ao_sdcard_recv(crc, 2);
+       bail:
+               ao_sdcard_deselect();
+               if (ret == SDCARD_STATUS_READY_STATE)
+                       break;
+               if (ret == SDCARD_STATUS_IDLE_STATE) {
+                       ret = _ao_sdcard_reset();
+                       if (ret != SDCARD_STATUS_READY_STATE)
+                               break;
+               }
+       }
+       ao_sdcard_put();
+       ao_sdcard_unlock();
+
+#if SDCARD_WARN
+       if (ret != SDCARD_STATUS_READY_STATE)
+               WARN("read failed\n");
+       else if (tries)
+               WARN("took %d tries to read %d\n", tries + 1, block);
+#endif
+
+       DBG("read %s\n", ret == SDCARD_STATUS_READY_STATE ? "success" : "failure");
+       return ret == SDCARD_STATUS_READY_STATE;
+}
+
+/*
+ * Write a block of 512 bytes to the card
+ */
+uint8_t
+ao_sdcard_write_block(uint32_t block, uint8_t *data)
+{
+       uint8_t ret;
+       uint8_t response[1];
+       uint8_t start_block[8];
+       uint16_t status;
+       static uint8_t  check_data[512];
+       int     i;
+       int     tries;
+
+       ao_sdcard_lock();
+       if (!initialized) {
+               ao_sdcard_setup();
+               initialized = 1;
+               if (sdtype != ao_sdtype_unknown)
+                       present = 1;
+       }
+       if (!present) {
+               ao_sdcard_unlock();
+               return 0;
+       }
+       DBG("write block %d\n", block);
+       if (sdtype != ao_sdtype_sd2block)
+               block <<= 9;
+
+       ao_sdcard_get();
+
+       for (tries = 0; tries < 10; tries++) {
+               ao_sdcard_select();
+
+               ret = ao_sdcard_send_cmd(SDCARD_WRITE_BLOCK, block);
+               ao_sdcard_recv_reply(NULL, 0);
+               if (ret != SDCARD_STATUS_READY_STATE)
+                       goto bail;
+
+               /* Write a pad byte followed by the data start block marker */
+               start_block[0] = 0xff;
+               start_block[1] = SDCARD_DATA_START_BLOCK;
+               ao_sdcard_send(start_block, 2);
+
+               /* Send the data */
+               ao_sdcard_send(data, 512);
+
+               /* Fake the CRC */
+               ao_sdcard_send_fixed(0xff, 2);
+
+               /* See if the card liked the data */
+               ao_sdcard_recv(response, sizeof (response));
+               if ((response[0] & SDCARD_DATA_RES_MASK) != SDCARD_DATA_RES_ACCEPTED) {
+                       int i;
+                       WARN("Data not accepted, response");
+                       for (i = 0; i < sizeof (response); i++)
+                               WARN(" %02x", response[i]);
+                       WARN("\n");
+                       ret = 0x3f;
+                       goto bail;
+               }
+               
+               /* Wait for the bus to go idle (should be done with an interrupt?) */
+               if (!ao_sdcard_wait_busy()) {
+                       ret = 0x3f;
+                       goto bail;
+               }
+
+               /* Check the current status after the write completes */
+               status = _ao_sdcard_send_status();
+               if ((status & 0xff) != SDCARD_STATUS_READY_STATE) {
+                       WARN ("send status after write %04x\n", status);
+                       ret = status & 0xff;
+                       goto bail;
+               }
+       bail:
+               ao_sdcard_deselect();
+               DBG("write %s\n", ret == SDCARD_STATUS_READY_STATE ? "success" : "failure");
+               if (ret == SDCARD_STATUS_READY_STATE)
+                       break;
+       }
+       ao_sdcard_put();
+       ao_sdcard_unlock();
+       if (tries)
+               WARN("took %d tries to write %d\n", tries + 1, block);
+
+       return ret == SDCARD_STATUS_READY_STATE;
+}
+
+#if SDCARD_DEBUG
+static uint8_t test_data[512];
+
+static void
+ao_sdcard_test_read(void)
+{
+       int i;
+
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       
+       for (i = 0; i < 100; i++) {
+               printf ("."); flush();
+               if (!ao_sdcard_read_block(ao_cmd_lex_u32+i, test_data)) {
+                       printf ("read error %d\n", i);
+                       return;
+               }
+       }
+       printf ("data:");
+       for (i = 0; i < 18; i++)
+               printf (" %02x", test_data[i]);
+       printf ("\n");
+}
+
+static void
+ao_sdcard_test_write(void)
+{
+       int     i;
+       printf ("data:");
+       for (i = 0; i < 16; i++) {
+               test_data[i]++;
+               printf (" %02x", test_data[i]);
+       }
+       printf ("\n");
+       if (!ao_sdcard_write_block(1, test_data)) {
+               printf ("write error\n");
+               return;
+       }
+}
+
+static const struct ao_cmds ao_sdcard_cmds[] = {
+       { ao_sdcard_test_read,  "x\0Test read" },
+       { ao_sdcard_test_write, "y\0Test read" },
+       { 0, NULL },
+};
+#endif
+
+void
+ao_sdcard_init(void)
+{
+       stm_pupdr_set(AO_SDCARD_SPI_PORT, AO_SDCARD_SPI_SCK_PIN, STM_PUPDR_PULL_UP);
+       stm_pupdr_set(AO_SDCARD_SPI_PORT, AO_SDCARD_SPI_MISO_PIN, STM_PUPDR_PULL_UP);
+       stm_pupdr_set(AO_SDCARD_SPI_PORT, AO_SDCARD_SPI_MOSI_PIN, STM_PUPDR_PULL_UP);
+       ao_spi_init_cs(AO_SDCARD_SPI_CS_PORT, (1 << AO_SDCARD_SPI_CS_PIN));
+#if SDCARD_DEBUG
+       ao_cmd_register(&ao_sdcard_cmds[0]);
+#endif
+}
diff --git a/src/drivers/ao_sdcard.h b/src/drivers/ao_sdcard.h
new file mode 100644 (file)
index 0000000..50b70c7
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_SDCARD_H_
+#define _AO_SDCARD_H_
+
+uint8_t
+ao_sdcard_read_block(uint32_t block, uint8_t *data);
+
+uint8_t
+ao_sdcard_write_block(uint32_t block, uint8_t *data);
+
+void
+ao_sdcard_init(void);
+
+/* Commands */
+#define SDCARD_GO_IDLE_STATE           0
+#define SDCARD_SEND_OP_COND            1
+#define SDCARD_SEND_IF_COND            8
+#define SDCARD_SEND_CSD                        9
+#define SDCARD_SEND_CID                        10
+#define SDCARD_SEND_STATUS             13
+#define SDCARD_SET_BLOCKLEN            16
+#define SDCARD_READ_BLOCK              17
+#define SDCARD_WRITE_BLOCK             24
+#define SDCARD_WRITE_MULTIPLE_BLOCK    25
+#define SDCARD_ERASE_WR_BLK_START      32
+#define SDCARD_ERASE_WR_BLK_END                33
+#define SDCARD_ERASE                   38
+#define SDCARD_APP_CMD                 55
+#define SDCARD_READ_OCR                        58
+
+/* App commands */
+#define SDCARD_APP_SET_WR_BLK_ERASE_COUNT      23
+#define SDCARD_APP_SEND_OP_COMD                        41
+
+/* Status */
+#define SDCARD_STATUS_READY_STATE      0x00
+#define SDCARD_STATUS_IDLE_STATE       0x01
+#define SDCARD_STATUS_ERASE_RESET      0x02
+#define SDCARD_STATUS_ILLEGAL_COMMAND  0x04
+#define SDCARD_STATUS_COM_CRC_ERROR    0x08
+#define SDCARD_STATUS_ERASE_SEQ_ERROR  0x10
+#define SDCARD_STATUS_ADDRESS_ERROR    0x20
+#define SDCARD_STATUS_PARAMETER_ERROR  0x40
+#define SDCARD_STATUS_TIMEOUT          0xff
+
+#define SDCARD_DATA_START_BLOCK                0xfe
+#define SDCARD_STOP_TRAN_TOKEN         0xfd
+#define SDCARD_WRITE_MULTIPLE_TOKEN    0xfc
+#define SDCARD_DATA_RES_MASK           0x1f
+#define SDCARD_DATA_RES_ACCEPTED       0x05
+
+#define SDCARD_CMD_TIMEOUT             AO_MS_TO_TICKS(20)
+#define SDCARD_BUSY_TIMEOUT            AO_MS_TO_TICKS(20)
+#define SDCARD_BLOCK_TIMEOUT           AO_MS_TO_TICKS(200)
+#define SDCARD_IDLE_RETRY              10
+#define SDCARD_OP_COND_RETRY           10
+
+enum ao_sdtype {
+       ao_sdtype_unknown,
+       ao_sdtype_mmc3,
+       ao_sdtype_sd1,
+       ao_sdtype_sd2byte,
+       ao_sdtype_sd2block,
+};
+
+#endif /* _AO_SDCARD_H_ */
diff --git a/src/megametrum-v0.1/.gitignore b/src/megametrum-v0.1/.gitignore
deleted file mode 100644 (file)
index b04d395..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-ao_product.h
-megametrum-*.elf
diff --git a/src/megametrum-v0.1/Makefile b/src/megametrum-v0.1/Makefile
deleted file mode 100644 (file)
index a5fdcbb..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-#
-# AltOS build
-#
-#
-
-include ../stm/Makefile.defs
-
-INC = \
-       ao.h \
-       ao_arch.h \
-       ao_arch_funcs.h \
-       ao_companion.h \
-       ao_data.h \
-       ao_sample.h \
-       ao_pins.h \
-       altitude-pa.h \
-       ao_kalman.h \
-       ao_product.h \
-       ao_ms5607.h \
-       ao_hmc5883.h \
-       ao_mpu6000.h \
-       ao_mma655x.h \
-       ao_cc1120_CC1120.h \
-       ao_profile.h \
-       ao_task.h \
-       ao_whiten.h \
-       ao_sample_profile.h \
-       ao_mpu.h \
-       stm32l.h \
-       Makefile
-
-#
-# Common AltOS sources
-#
-#      ao_hmc5883.c
-
-#PROFILE=ao_profile.c
-#PROFILE_DEF=-DAO_PROFILE=1
-
-#SAMPLE_PROFILE=ao_sample_profile.c \
-#      ao_sample_profile_timer.c
-#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1
-
-#STACK_GUARD=ao_mpu_stm.c
-#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1
-
-ALTOS_SRC = \
-       ao_interrupt.c \
-       ao_product.c \
-       ao_romconfig.c \
-       ao_cmd.c \
-       ao_config.c \
-       ao_task.c \
-       ao_led.c \
-       ao_stdio.c \
-       ao_panic.c \
-       ao_timer.c \
-       ao_mutex.c \
-       ao_serial_stm.c \
-       ao_gps_skytraq.c \
-       ao_gps_report_mega.c \
-       ao_ignite.c \
-       ao_freq.c \
-       ao_dma_stm.c \
-       ao_spi_stm.c \
-       ao_cc1120.c \
-       ao_fec_tx.c \
-       ao_fec_rx.c \
-       ao_data.c \
-       ao_ms5607.c \
-       ao_mma655x.c \
-       ao_hmc5883.c \
-       ao_adc_stm.c \
-       ao_beep_stm.c \
-       ao_storage.c \
-       ao_m25.c \
-       ao_usb_stm.c \
-       ao_exti_stm.c \
-       ao_report.c \
-       ao_i2c_stm.c \
-       ao_mpu6000.c \
-       ao_convert_pa.c \
-       ao_log.c \
-       ao_log_mega.c \
-       ao_sample.c \
-       ao_kalman.c \
-       ao_flight.c \
-       ao_telemetry.c \
-       ao_packet_slave.c \
-       ao_packet.c \
-       ao_companion.c \
-       ao_pyro.c \
-       ao_aprs.c \
-       $(PROFILE) \
-       $(SAMPLE_PROFILE) \
-       $(STACK_GUARD)
-
-PRODUCT=MegaMetrum-v0.1
-PRODUCT_DEF=-DMEGAMETRUM
-IDPRODUCT=0x0023
-
-CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF) -Os -g
-
-PROGNAME=megametrum-v0.1
-PROG=$(PROGNAME)-$(VERSION).elf
-
-SRC=$(ALTOS_SRC) ao_megametrum.c
-OBJ=$(SRC:.c=.o)
-
-all: $(PROG)
-
-$(PROG): Makefile $(OBJ) altos.ld
-       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(SAT_CLIB) -lgcc
-
-../altitude-pa.h: make-altitude-pa
-       nickle $< > $@
-
-$(OBJ): $(INC)
-
-ao_product.h: ao-make-product.5c ../Version
-       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
-
-distclean:     clean
-
-clean:
-       rm -f *.o $(PROGNAME)-*.elf
-       rm -f ao_product.h
-
-install:
-
-uninstall:
diff --git a/src/megametrum-v0.1/ao_megametrum.c b/src/megametrum-v0.1/ao_megametrum.c
deleted file mode 100644 (file)
index fbdab64..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright Â© 2011 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include <ao.h>
-#include <ao_hmc5883.h>
-#include <ao_mpu6000.h>
-#include <ao_mma655x.h>
-#include <ao_log.h>
-#include <ao_exti.h>
-#include <ao_packet.h>
-#include <ao_companion.h>
-#include <ao_profile.h>
-#if HAS_SAMPLE_PROFILE
-#include <ao_sample_profile.h>
-#endif
-#include <ao_pyro.h>
-#if HAS_STACK_GUARD
-#include <ao_mpu.h>
-#endif
-
-int
-main(void)
-{
-       ao_clock_init();
-       
-#if HAS_STACK_GUARD
-       ao_mpu_init();
-#endif
-
-       ao_task_init();
-       ao_serial_init();
-       ao_led_init(LEDS_AVAILABLE);
-       ao_led_on(AO_LED_GREEN);
-       ao_timer_init();
-
-       ao_i2c_init();
-       ao_spi_init();
-       ao_dma_init();
-       ao_exti_init();
-
-       ao_adc_init();
-#if HAS_BEEP
-       ao_beep_init();
-#endif
-       ao_cmd_init();
-
-#if HAS_MS5607
-       ao_ms5607_init();
-#endif
-#if HAS_HMC5883
-       ao_hmc5883_init();
-#endif
-#if HAS_MPU6000
-       ao_mpu6000_init();
-#endif
-#if HAS_MMA655X
-       ao_mma655x_init();
-#endif
-
-       ao_storage_init();
-       
-       ao_flight_init();
-       ao_log_init();
-       ao_report_init();
-
-       ao_usb_init();
-       ao_gps_init();
-       ao_gps_report_mega_init();
-       ao_telemetry_init();
-       ao_radio_init();
-       ao_packet_slave_init(FALSE);
-       ao_igniter_init();
-       ao_companion_init();
-       ao_pyro_init();
-
-       ao_config_init();
-#if AO_PROFILE
-       ao_profile_init();
-#endif
-#if HAS_SAMPLE_PROFILE
-       ao_sample_profile_init();
-#endif
-       
-       ao_start_scheduler();
-       return 0;
-}
diff --git a/src/megametrum-v0.1/ao_pins.h b/src/megametrum-v0.1/ao_pins.h
deleted file mode 100644 (file)
index 4c64587..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright Â© 2012 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#ifndef _AO_PINS_H_
-#define _AO_PINS_H_
-
-#define HAS_TASK_QUEUE         1
-
-/* 8MHz High speed external crystal */
-#define AO_HSE                 8000000
-
-/* PLLVCO = 96MHz (so that USB will work) */
-#define AO_PLLMUL              12
-#define AO_RCC_CFGR_PLLMUL     (STM_RCC_CFGR_PLLMUL_12)
-
-/* SYSCLK = 32MHz (no need to go faster than CPU) */
-#define AO_PLLDIV              3
-#define AO_RCC_CFGR_PLLDIV     (STM_RCC_CFGR_PLLDIV_3)
-
-/* HCLK = 32MHz (CPU clock) */
-#define AO_AHB_PRESCALER       1
-#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
-
-/* Run APB1 at 16MHz (HCLK/2) */
-#define AO_APB1_PRESCALER      2
-#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE2_DIV_2
-
-/* Run APB2 at 16MHz (HCLK/2) */
-#define AO_APB2_PRESCALER      2
-#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_2
-
-#define HAS_SERIAL_1           1
-#define USE_SERIAL_1_STDIN     0
-#define SERIAL_1_PB6_PB7       0
-#define SERIAL_1_PA9_PA10      1
-
-#define HAS_SERIAL_2           0
-#define USE_SERIAL_2_STDIN     0
-#define SERIAL_2_PA2_PA3       0
-#define SERIAL_2_PD5_PD6       0
-
-#define HAS_SERIAL_3           1
-#define USE_SERIAL_3_STDIN     0
-#define SERIAL_3_PB10_PB11     0
-#define SERIAL_3_PC10_PC11     1
-#define SERIAL_3_PD8_PD9       0
-
-#define ao_gps_getchar         ao_serial3_getchar
-#define ao_gps_putchar         ao_serial3_putchar
-#define ao_gps_set_speed       ao_serial3_set_speed
-#define ao_gps_fifo            (ao_stm_usart3.rx_fifo)
-
-#define HAS_EEPROM             1
-#define USE_INTERNAL_FLASH     0
-#define HAS_USB                        1
-#define HAS_BEEP               1
-#define HAS_RADIO              1
-#define HAS_TELEMETRY          1
-#define HAS_APRS               1
-
-#define HAS_SPI_1              1
-#define SPI_1_PA5_PA6_PA7      1       /* Barometer */
-#define SPI_1_PB3_PB4_PB5      0
-#define SPI_1_PE13_PE14_PE15   1       /* Accelerometer */
-#define SPI_1_OSPEEDR          STM_OSPEEDR_10MHz
-
-#define HAS_SPI_2              1
-#define SPI_2_PB13_PB14_PB15   1       /* Flash, Companion */
-#define SPI_2_PD1_PD3_PD4      0
-#define SPI_2_OSPEEDR          STM_OSPEEDR_10MHz
-
-#define SPI_2_PORT             (&stm_gpiob)
-#define SPI_2_SCK_PIN          13
-#define SPI_2_MISO_PIN         14
-#define SPI_2_MOSI_PIN         15
-
-#define HAS_I2C_1              1
-#define I2C_1_PB8_PB9          1
-
-#define HAS_I2C_2              1
-#define I2C_2_PB10_PB11                1
-
-#define PACKET_HAS_SLAVE       1
-#define PACKET_HAS_MASTER      0
-
-#define LOW_LEVEL_DEBUG                0
-
-#define LED_PORT_ENABLE                STM_RCC_AHBENR_GPIOCEN
-#define LED_PORT               (&stm_gpioc)
-#define LED_PIN_RED            8
-#define LED_PIN_GREEN          9
-#define AO_LED_RED             (1 << LED_PIN_RED)
-#define AO_LED_GREEN           (1 << LED_PIN_GREEN)
-
-#define LEDS_AVAILABLE         (AO_LED_RED | AO_LED_GREEN)
-
-#define HAS_GPS                        1
-#define HAS_FLIGHT             1
-#define HAS_ADC                        1
-#define HAS_LOG                        1
-
-/*
- * Igniter
- */
-
-#define HAS_IGNITE             1
-#define HAS_IGNITE_REPORT      1
-
-#define AO_SENSE_DROGUE(p)     ((p)->adc.sense[0])
-#define AO_SENSE_MAIN(p)       ((p)->adc.sense[1])
-#define AO_IGNITER_CLOSED      400
-#define AO_IGNITER_OPEN                60
-
-#define AO_IGNITER_DROGUE_PORT (&stm_gpiod)
-#define AO_IGNITER_DROGUE_PIN  6
-
-#define AO_IGNITER_MAIN_PORT   (&stm_gpiod)
-#define AO_IGNITER_MAIN_PIN    7
-
-#define AO_PYRO_PORT_0 (&stm_gpiob)
-#define AO_PYRO_PIN_0  5
-
-#define AO_PYRO_PORT_1 (&stm_gpioe)
-#define AO_PYRO_PIN_1  4
-
-#define AO_PYRO_PORT_2 (&stm_gpioe)
-#define AO_PYRO_PIN_2  6
-
-#define AO_PYRO_PORT_3 (&stm_gpioe)
-#define AO_PYRO_PIN_3  5
-
-/* Number of general purpose pyro channels available */
-#define AO_PYRO_NUM    4
-
-#define AO_IGNITER_SET_DROGUE(v)       stm_gpio_set(AO_IGNITER_DROGUE_PORT, AO_IGNITER_DROGUE_PIN, v)
-#define AO_IGNITER_SET_MAIN(v)         stm_gpio_set(AO_IGNITER_MAIN_PORT, AO_IGNITER_MAIN_PIN, v)
-
-/*
- * ADC
- */
-#define AO_DATA_RING           32
-#define AO_ADC_NUM_SENSE       6
-
-struct ao_adc {
-       int16_t                 sense[AO_ADC_NUM_SENSE];
-       int16_t                 v_batt;
-       int16_t                 v_pbatt;
-       int16_t                 accel_ref;
-       int16_t                 accel;
-       int16_t                 temp;
-};
-
-#define AO_ADC_SENSE_A         0
-#define AO_ADC_SENSE_A_PORT    (&stm_gpioa)
-#define AO_ADC_SENSE_A_PIN     0
-
-#define AO_ADC_SENSE_B         1
-#define AO_ADC_SENSE_B_PORT    (&stm_gpioa)
-#define AO_ADC_SENSE_B_PIN     1
-
-#define AO_ADC_SENSE_C         2
-#define AO_ADC_SENSE_C_PORT    (&stm_gpioa)
-#define AO_ADC_SENSE_C_PIN     2
-
-#define AO_ADC_SENSE_D         3
-#define AO_ADC_SENSE_D_PORT    (&stm_gpioa)
-#define AO_ADC_SENSE_D_PIN     3
-
-#define AO_ADC_SENSE_E         4
-#define AO_ADC_SENSE_E_PORT    (&stm_gpioa)
-#define AO_ADC_SENSE_E_PIN     4
-
-#define AO_ADC_SENSE_F         22
-#define AO_ADC_SENSE_F_PORT    (&stm_gpioe)
-#define AO_ADC_SENSE_F_PIN     7
-
-#define AO_ADC_V_BATT          8
-#define AO_ADC_V_BATT_PORT     (&stm_gpiob)
-#define AO_ADC_V_BATT_PIN      0
-
-#define AO_ADC_V_PBATT         9
-#define AO_ADC_V_PBATT_PORT    (&stm_gpiob)
-#define AO_ADC_V_PBATT_PIN     1
-
-#define AO_ADC_ACCEL_REF       10
-#define AO_ADC_ACCEL_REF_PORT  (&stm_gpioc)
-#define AO_ADC_ACCEL_REF_PIN   0
-
-#define AO_ADC_ACCEL           11
-#define AO_ADC_ACCEL_PORT      (&stm_gpioc)
-#define AO_ADC_ACCEL_PIN       1
-
-#define AO_ADC_TEMP            16
-
-#define AO_ADC_RCC_AHBENR      ((1 << STM_RCC_AHBENR_GPIOAEN) | \
-                                (1 << STM_RCC_AHBENR_GPIOEEN) | \
-                                (1 << STM_RCC_AHBENR_GPIOBEN) | \
-                                (1 << STM_RCC_AHBENR_GPIOCEN))
-
-#define AO_NUM_ADC_PIN         (AO_ADC_NUM_SENSE + 4)
-
-#define AO_ADC_PIN0_PORT       AO_ADC_SENSE_A_PORT
-#define AO_ADC_PIN0_PIN                AO_ADC_SENSE_A_PIN
-#define AO_ADC_PIN1_PORT       AO_ADC_SENSE_B_PORT
-#define AO_ADC_PIN1_PIN                AO_ADC_SENSE_B_PIN
-#define AO_ADC_PIN2_PORT       AO_ADC_SENSE_C_PORT
-#define AO_ADC_PIN2_PIN                AO_ADC_SENSE_C_PIN
-#define AO_ADC_PIN3_PORT       AO_ADC_SENSE_D_PORT
-#define AO_ADC_PIN3_PIN                AO_ADC_SENSE_D_PIN
-#define AO_ADC_PIN4_PORT       AO_ADC_SENSE_E_PORT
-#define AO_ADC_PIN4_PIN                AO_ADC_SENSE_E_PIN
-#define AO_ADC_PIN5_PORT       AO_ADC_SENSE_F_PORT
-#define AO_ADC_PIN5_PIN                AO_ADC_SENSE_F_PIN
-#define AO_ADC_PIN6_PORT       AO_ADC_V_BATT_PORT
-#define AO_ADC_PIN6_PIN                AO_ADC_V_BATT_PIN
-#define AO_ADC_PIN7_PORT       AO_ADC_V_PBATT_PORT
-#define AO_ADC_PIN7_PIN                AO_ADC_V_PBATT_PIN
-#define AO_ADC_PIN8_PORT       AO_ADC_ACCEL_REF_PORT
-#define AO_ADC_PIN8_PIN                AO_ADC_ACCEL_REF_PIN
-#define AO_ADC_PIN9_PORT       AO_ADC_ACCEL_PORT
-#define AO_ADC_PIN9_PIN                AO_ADC_ACCEL_PIN
-
-#define AO_NUM_ADC             (AO_ADC_NUM_SENSE + 5)
-
-#define AO_ADC_SQ1             AO_ADC_SENSE_A
-#define AO_ADC_SQ2             AO_ADC_SENSE_B
-#define AO_ADC_SQ3             AO_ADC_SENSE_C
-#define AO_ADC_SQ4             AO_ADC_SENSE_D
-#define AO_ADC_SQ5             AO_ADC_SENSE_E
-#define AO_ADC_SQ6             AO_ADC_SENSE_F
-#define AO_ADC_SQ7             AO_ADC_V_BATT
-#define AO_ADC_SQ8             AO_ADC_V_PBATT
-#define AO_ADC_SQ9             AO_ADC_ACCEL_REF
-#define AO_ADC_SQ10            AO_ADC_ACCEL
-#define AO_ADC_SQ11            AO_ADC_TEMP
-
-/*
- * Pressure sensor settings
- */
-#define HAS_MS5607             1
-#define HAS_MS5611             0
-#define AO_MS5607_PRIVATE_PINS 1
-#define AO_MS5607_CS_PORT      (&stm_gpioc)
-#define AO_MS5607_CS_PIN       4
-#define AO_MS5607_CS_MASK      (1 << AO_MS5607_CS)
-#define AO_MS5607_MISO_PORT    (&stm_gpioa)
-#define AO_MS5607_MISO_PIN     6
-#define AO_MS5607_MISO_MASK    (1 << AO_MS5607_MISO)
-#define AO_MS5607_SPI_INDEX    AO_SPI_1_PA5_PA6_PA7
-
-/*
- * SPI Flash memory
- */
-
-#define M25_MAX_CHIPS          1
-#define AO_M25_SPI_CS_PORT     (&stm_gpiod)
-#define AO_M25_SPI_CS_MASK     (1 << 3)
-#define AO_M25_SPI_BUS         AO_SPI_2_PB13_PB14_PB15
-
-/*
- * Radio (cc1120)
- */
-
-/* gets pretty close to 434.550 */
-
-#define AO_RADIO_CAL_DEFAULT   0x6ca333
-
-#define AO_FEC_DEBUG           0
-#define AO_CC1120_SPI_CS_PORT  (&stm_gpioc)
-#define AO_CC1120_SPI_CS_PIN   5
-#define AO_CC1120_SPI_BUS      AO_SPI_2_PB13_PB14_PB15
-#define AO_CC1120_SPI          stm_spi2
-
-#define AO_CC1120_INT_PORT             (&stm_gpioc)
-#define AO_CC1120_INT_PIN              14
-#define AO_CC1120_MCU_WAKEUP_PORT      (&stm_gpioc)
-#define AO_CC1120_MCU_WAKEUP_PIN       (0)
-
-#define AO_CC1120_INT_GPIO     2
-#define AO_CC1120_INT_GPIO_IOCFG       CC1120_IOCFG2
-
-#define AO_CC1120_MARC_GPIO    3
-#define AO_CC1120_MARC_GPIO_IOCFG      CC1120_IOCFG3
-
-
-#define HAS_BOOT_RADIO         0
-
-/*
- * Mag sensor (hmc5883)
- */
-
-#define HAS_HMC5883            0
-#define AO_HMC5883_INT_PORT    (&stm_gpioc)
-#define AO_HMC5883_INT_PIN     12
-#define AO_HMC5883_I2C_INDEX   STM_I2C_INDEX(1)
-
-/*
- * mpu6000
- */
-
-#define HAS_MPU6000            1       
-#define AO_MPU6000_INT_PORT    (&stm_gpioc)
-#define AO_MPU6000_INT_PIN     13
-#define AO_MPU6000_I2C_INDEX   STM_I2C_INDEX(1)
-
-#define HAS_HIGHG_ACCEL                0
-
-/*
- * mma655x
- */
-
-#define HAS_MMA655X            1
-#define AO_MMA655X_SPI_INDEX   AO_SPI_1_PE13_PE14_PE15
-#define AO_MMA655X_CS_PORT     (&stm_gpiod)
-#define AO_MMA655X_CS_PIN      4
-
-#define NUM_CMDS               16
-
-/*
- * Companion
- */
-
-#define AO_COMPANION_CS_PORT   (&stm_gpiod)
-#define AO_COMPANION_CS_PIN    (0)
-#define AO_COMPANION_SPI_BUS   AO_SPI_2_PB13_PB14_PB15
-
-/*
- * Monitor
- */
-
-#define HAS_MONITOR            0
-#define LEGACY_MONITOR         0
-#define HAS_MONITOR_PUT                1
-#define AO_MONITOR_LED         0
-#define HAS_RSSI               0
-
-/*
- * Profiling Viterbi decoding
- */
-
-#ifndef AO_PROFILE
-#define AO_PROFILE             0
-#endif
-
-#endif /* _AO_PINS_H_ */
diff --git a/src/megametrum-v0.1/stlink-pins b/src/megametrum-v0.1/stlink-pins
deleted file mode 100644 (file)
index 3bd16d0..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-ST discovery card pins
-
-1      AIN-1
-2      JTCK
-3      GND
-4      JTMS
-5      NRST
-6      SWO
-
-MegaMetrum v0.1 misc connector
-
-1      GND
-2      reset_n
-3      boot0
-4      tx1
-5      rx1
-6      +3.3V
-7      GND
-8      jtms
-9      jtck
-10     jtdi
-11     jtdo
-12     jntrst
-13     sda2
-14     scl2
-15     pe1
-16     pe0
-
-For debugging:
-
-       ST      MM v0.1
-JTCK   2       9
-GND    3       7
-JTMS   4       8
-NRST   5       2
-
-Altus Metrum STM32L standard debug connector (4 pin MicoMaTch):
-
-       TL      ST
-GND    1       3
-NRST   2       5
-SWDIO  3       4
-SWCLK  4       2
-
-MegaAccel:
-
-Jumpers
-PC0 (pin15) (blue)     PE0 (pin97)     accel_ref       (debug 16)
-PC1 (pin16) (green)    PE1 (pin98)     accel           (debug 15)
diff --git a/src/product/Makefile.telebt b/src/product/Makefile.telebt
deleted file mode 100644 (file)
index fd52cec..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-#
-# TeleBT build file
-#
-# Define TELEBT_VER, TELEBT_DEF, TELEBT_INC and TELEBT_SRC
-# and include this file
-
-vpath %.c ..:../core:../cc1111:../drivers:../product
-vpath %.h ..:../core:../cc1111:../drivers:../product
-vpath ao-make-product.5c ../util
-
-ifndef VERSION
-include ../Version
-endif
-
-INC = \
-       ao.h \
-       ao_pins.h \
-       ao_arch.h \
-       ao_arch_funcs.h \
-       cc1111.h \
-       ao_product.h \
-       $(TELEBT_INC)
-
-CORE_SRC = \
-       ao_cmd.c \
-       ao_config.c \
-       ao_gps_print.c \
-       ao_monitor.c \
-       ao_mutex.c \
-       ao_panic.c \
-       ao_state.c \
-       ao_stdio.c \
-       ao_task.c \
-       ao_freq.c
-
-CC1111_SRC = \
-       ao_dbg.c \
-       ao_dma.c \
-       ao_led.c \
-       ao_packet.c \
-       ao_packet_master.c \
-       ao_radio.c \
-       ao_romconfig.c \
-       ao_serial.c \
-       ao_string.c \
-       ao_timer.c \
-       ao_usb.c \
-       _bp.c
-
-DRIVER_SRC = \
-       ao_btm.c
-
-PRODUCT_SRC = \
-       ao_telebt.c
-
-SRC = \
-       $(CORE_SRC) \
-       $(CC1111_SRC) \
-       $(DRIVER_SRC) \
-       $(PRODUCT_SRC) \
-       $(TELEBT_SRC)
-
-PROGNAME = telebt-v$(TELEBT_VER)
-PROG = $(PROGNAME)-$(VERSION).ihx
-PRODUCT=TeleBT-v$(TELEBT_VER)
-PRODUCT_DEF=-DTELEBT_V_$(TELEBT_DEF)
-IDPRODUCT=0x000e
-
-include ../cc1111/Makefile.cc1111
-
-NICKLE=nickle
-CHECK_STACK=sh ../util/check-stack
-
-V=0
-# The user has explicitly enabled quiet compilation.
-ifeq ($(V),0)
-quiet = @printf "  $1 $2 $@\n"; $($1)
-endif
-# Otherwise, print the full command line.
-quiet ?= $($1)
-
-all: $(PROG)
-
-$(PROG): $(REL) Makefile
-       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
-       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@
-
-ao_product.h: ao-make-product.5c ../Version
-       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
-
-distclean:     clean
-
-clean: clean-cc1111
-
-install:
-
-uninstall:
diff --git a/src/product/ao_telebt.c b/src/product/ao_telebt.c
deleted file mode 100644 (file)
index b147eab..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright Â© 2011 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-#if HAS_LOG
-__code uint8_t ao_log_format = AO_LOG_FORMAT_NONE;     /* until we actually log stuff */
-#endif
-
-void
-main(void)
-{
-       ao_clock_init();
-
-       /* Turn on the LED until the system is stable */
-       ao_led_init(LEDS_AVAILABLE);
-       ao_led_on(AO_LED_RED);
-
-       ao_task_init();
-
-       ao_timer_init();
-#if HAS_BEEP
-       ao_beep_init();
-#endif
-       ao_cmd_init();
-#if HAS_EEPROM
-       ao_spi_init();
-       ao_storage_init();
-#endif
-       ao_usb_init();
-       ao_monitor_init();
-#if HAS_LOG
-       ao_report_init();
-#endif
-       ao_radio_init();
-       ao_packet_master_init();
-       ao_btm_init();
-#if HAS_LOG
-       ao_log_single_init();
-#endif
-#if HAS_DBG
-       ao_dbg_init();
-#endif
-#if HAS_AES
-       ao_aes_init();
-       ao_radio_cmac_init();
-#endif
-       ao_config_init();
-       ao_start_scheduler();
-}
index 5cc94bd969f64a3a147461bbe7489ef2416115a3..1bc5aaadd789060a44f0be9b11dd22289944f6ba 100644 (file)
@@ -8,8 +8,8 @@ endif
 CC=arm-none-eabi-gcc
 OBJCOPY=arm-none-eabi-objcopy
 
-PDCLIB=/home/keithp/sat
-C_LIB=$(PDCLIB)/lib/pdclib.a
+PDCLIB=/opt/cortex
+C_LIB=$(PDCLIB)/lib/pdclib-cortex-m3.a
 C_INC=-I$(PDCLIB)/include
 
 DEF_CFLAGS=-g -std=gnu99 -Os -mlittle-endian -mthumb -ffreestanding -nostdlib -I. -I../../src/stm $(C_INC)
index 3ce6eead168b4ff0b2a18669fa5a0e60d08368a2..c8bb7d704b4573b0c306e6e03d8aaa2f8bc858b6 100644 (file)
@@ -13,8 +13,8 @@ vpath ao-make-product.5c ../util
        objcopy -O ihex $*.elf $@
 
 CC=arm-none-eabi-gcc
-SAT=/home/keithp/sat
-SAT_CLIB=$(SAT)/lib/pdclib.a
+SAT=/opt/cortex
+SAT_CLIB=$(SAT)/lib/pdclib-cortex-m3.a
 SAT_CFLAGS=-I$(SAT)/include
 
 ifndef VERSION
index 5a75a97d86d6be05f1128529493fdf1c52b9af30..58783f1a0771262f6c573abc81162d8f09c0401f 100644 (file)
@@ -55,10 +55,16 @@ ao_storage_erase(ao_pos_t pos) __reentrant
 static void
 ao_intflash_unlock(void)
 {
+       /* Disable backup write protection */
+       stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
+
        /* Unlock Data EEPROM and FLASH_PECR register */
        stm_flash.pekeyr = STM_FLASH_PEKEYR_PEKEY1;
        stm_flash.pekeyr = STM_FLASH_PEKEYR_PEKEY2;
 
+       if (stm_flash.pecr & (1 << STM_FLASH_PECR_PELOCK))
+               printf ("eeprom unlock failed\n");
+
        /* Configure the FTDW bit (FLASH_PECR[8]) to execute
         * word write, whatever the previous value of the word
         * being written to
@@ -68,8 +74,8 @@ ao_intflash_unlock(void)
                          (0 << STM_FLASH_PECR_EOPIE) |
                          (0 << STM_FLASH_PECR_FPRG) |
                          (0 << STM_FLASH_PECR_ERASE) |
-                         (0 << STM_FLASH_PECR_FTDW) |
-                         (1 << STM_FLASH_PECR_DATA) |
+                         (1 << STM_FLASH_PECR_FTDW) |
+                         (0 << STM_FLASH_PECR_DATA) |
                          (0 << STM_FLASH_PECR_PROG) |
                          (0 << STM_FLASH_PECR_OPTLOCK) |
                          (0 << STM_FLASH_PECR_PRGLOCK) |
@@ -97,15 +103,9 @@ ao_intflash_write32(uint16_t pos, uint32_t w)
 
        addr = (uint32_t *) (stm_eeprom + pos);
 
-       /* Erase previous word */
-       *addr = 0;
+       /* Write a word to a valid address in the data EEPROM */
+       *addr = w;
        ao_intflash_wait();
-
-       if (w) {
-               /* Write a word to a valid address in the data EEPROM */
-               *addr = w;
-               ao_intflash_wait();
-       }
 }
 
 static void
index 779e227503110971abcb3c8aa14744ef1594b1ed..809b5c6fcf72eac1993e96741f46b60ab955c303 100644 (file)
@@ -36,7 +36,7 @@ static uint16_t       ao_i2c_addr[STM_NUM_I2C];
 uint8_t        ao_i2c_mutex[STM_NUM_I2C];
 
 # define I2C_HIGH_SLOW 5000    /* ns, 100kHz clock */
-#ifdef MEGAMETRUM
+#ifdef TELEMEGA
 # define I2C_HIGH_FAST 2000    /* ns, 167kHz clock */
 #else
 # define I2C_HIGH_FAST 1000    /* ns, 333kHz clock */
index 7b4af96443f28af949669e2d1cf18c2a49d2db49..56329c2446c43f6188a6f29dc3ae4d596a93c0df 100644 (file)
@@ -160,6 +160,8 @@ ao_spi_recv(void *block, uint16_t len, uint8_t spi_index)
        uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
        uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
 
+       spi_dev_null = 0xff;
+
        /* Set up transmit DMA to make the SPI hardware actually run */
        ao_dma_set_transfer(mosi_dma_index,
                            &stm_spi->dr,
index e07625d8d7d1e809db29f18c5f6deaae388f0869..8b7c2327ff2bdd34f93e7f93a8351f0c3ff63a4f 100644 (file)
 #include "ao.h"
 #include <ao_task.h>
 
-volatile __data AO_TICK_TYPE ao_tick_count;
+volatile AO_TICK_TYPE ao_tick_count;
 
-uint16_t ao_time(void)
+AO_TICK_TYPE
+ao_time(void)
 {
-       uint16_t        v;
-       ao_arch_critical(
-               v = ao_tick_count;
-               );
-       return v;
+       return ao_tick_count;
 }
 
 #if AO_DATA_ALL
diff --git a/src/telebt-v0.0/.gitignore b/src/telebt-v0.0/.gitignore
deleted file mode 100644 (file)
index 1acfbfc..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-telebt-*
-ao_product.h
diff --git a/src/telebt-v0.0/.sdcdbrc b/src/telebt-v0.0/.sdcdbrc
deleted file mode 100644 (file)
index 710b4a2..0000000
+++ /dev/null
@@ -1 +0,0 @@
---directory=..
diff --git a/src/telebt-v0.0/Makefile b/src/telebt-v0.0/Makefile
deleted file mode 100644 (file)
index e89639a..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# TeleBT v0.0 build
-#
-
-TELEBT_VER=0.0
-TELEBT_DEF=0_0
-
-include ../product/Makefile.telebt
-
diff --git a/src/telebt-v0.1/.gitignore b/src/telebt-v0.1/.gitignore
deleted file mode 100644 (file)
index 1acfbfc..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-telebt-*
-ao_product.h
diff --git a/src/telebt-v0.1/.sdcdbrc b/src/telebt-v0.1/.sdcdbrc
deleted file mode 100644 (file)
index b9f6129..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
---directory=../cc1111:../product:../core:../drivers:.
-
diff --git a/src/telebt-v0.1/Makefile b/src/telebt-v0.1/Makefile
deleted file mode 100644 (file)
index 90cd3ca..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#
-# TeleBT v0.1 build
-#
-
-TELEBT_VER=0.1
-TELEBT_DEF=0_1
-
-TELEBT_INC = \
-       ao_25lc1024.h
-
-TELEBT_SRC = \
-       ao_beep.c \
-       ao_log_single.c \
-       ao_log_telem.c \
-       ao_report.c \
-       ao_spi.c \
-       ao_storage.c \
-       ao_m25.c
-
-include ../product/Makefile.telebt
-
diff --git a/src/telebt-v1.0/.gitignore b/src/telebt-v1.0/.gitignore
new file mode 100644 (file)
index 0000000..1acfbfc
--- /dev/null
@@ -0,0 +1,2 @@
+telebt-*
+ao_product.h
diff --git a/src/telebt-v1.0/.sdcdbrc b/src/telebt-v1.0/.sdcdbrc
new file mode 100644 (file)
index 0000000..b9f6129
--- /dev/null
@@ -0,0 +1,2 @@
+--directory=../cc1111:../product:../core:../drivers:.
+
diff --git a/src/telebt-v1.0/Makefile b/src/telebt-v1.0/Makefile
new file mode 100644 (file)
index 0000000..911a8b0
--- /dev/null
@@ -0,0 +1,97 @@
+#
+# TeleBT build file
+#
+
+TELEBT_VER=1.0
+TELEBT_DEF=1_0
+
+vpath %.c ..:../core:../cc1111:../drivers:../product
+vpath %.h ..:../core:../cc1111:../drivers:../product
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       cc1111.h \
+       ao_product.h
+
+CORE_SRC = \
+       ao_cmd.c \
+       ao_config.c \
+       ao_gps_print.c \
+       ao_monitor.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_state.c \
+       ao_stdio.c \
+       ao_task.c \
+       ao_freq.c
+
+CC1111_SRC = \
+       ao_dbg.c \
+       ao_adc.c \
+       ao_dma.c \
+       ao_led.c \
+       ao_packet.c \
+       ao_packet_master.c \
+       ao_radio.c \
+       ao_romconfig.c \
+       ao_serial.c \
+       ao_string.c \
+       ao_timer.c \
+       ao_usb.c \
+       _bp.c
+
+DRIVER_SRC = \
+       ao_btm.c
+
+PRODUCT_SRC = \
+       ao_telebt.c
+
+SRC = \
+       $(CORE_SRC) \
+       $(CC1111_SRC) \
+       $(DRIVER_SRC) \
+       $(PRODUCT_SRC)
+
+PROGNAME = telebt-v$(TELEBT_VER)
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=TeleBT-v$(TELEBT_VER)
+PRODUCT_DEF=-DTELEBT_V_$(TELEBT_DEF)
+IDPRODUCT=0x000e
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: $(PROG)
+
+$(PROG): $(REL) Makefile
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
diff --git a/src/telebt-v1.0/ao_pins.h b/src/telebt-v1.0/ao_pins.h
new file mode 100644 (file)
index 0000000..9e47f3b
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright Â© 2010 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_RADIO      1
+#define HAS_FLIGHT             0
+#define HAS_USB                        1
+#define HAS_BEEP               0
+#define HAS_SERIAL_1           1
+#define HAS_SERIAL_1_ALT_1     1
+#define HAS_SERIAL_1_ALT_2     0
+#define HAS_SERIAL_1_HW_FLOW   1
+#define USE_SERIAL_1_STDIN     1
+#define DELAY_SERIAL_1_STDIN   1
+#define HAS_DBG                        1
+#define HAS_EEPROM             0
+#define HAS_LOG                        0
+#define USE_INTERNAL_FLASH     0
+#define HAS_BTM                        1
+#define DBG_ON_P1              1
+#define DBG_ON_P0              0
+#define PACKET_HAS_MASTER      1
+#define PACKET_HAS_SLAVE       0
+#define AO_LED_RED             1
+#define AO_LED_BLUE            2
+#define LEDS_AVAILABLE         (AO_LED_RED|AO_LED_BLUE)
+#define AO_MONITOR_LED         AO_LED_RED
+#define AO_BT_LED              AO_LED_BLUE
+#define BT_LINK_ON_P2          0
+#define BT_LINK_ON_P1          1
+#define BT_LINK_PIN_INDEX      7
+#define BT_LINK_PIN            P1_7
+#define HAS_MONITOR            1
+#define LEGACY_MONITOR         0
+
+#define HAS_ADC                        1
+#define AO_PAD_ADC_BATT                0
+#define AO_ADC_PINS            (1 << AO_PAD_ADC_BATT)
+
+struct ao_adc {
+       int16_t         batt;
+};
+
+#define AO_ADC_DUMP(p)                                                 \
+       printf ("tick: %5u batt %5d\n",                                 \
+               (p)->tick,                                              \
+               (p)->adc.batt)
+
+#if DBG_ON_P1
+
+       #define DBG_CLOCK       (1 << 4)        /* mi0 */
+       #define DBG_DATA        (1 << 5)        /* mo0 */
+       #define DBG_RESET_N     (1 << 3)        /* c0 */
+
+       #define DBG_CLOCK_PIN   (P1_4)
+       #define DBG_DATA_PIN    (P1_5)
+       #define DBG_RESET_N_PIN (P1_3)
+
+       #define DBG_PORT_NUM    1
+       #define DBG_PORT        P1
+       #define DBG_PORT_SEL    P1SEL
+       #define DBG_PORT_INP    P1INP
+       #define DBG_PORT_DIR    P1DIR
+
+#endif /* DBG_ON_P1 */
+
+#if DBG_ON_P0
+
+       #define DBG_CLOCK       (1 << 3)
+       #define DBG_DATA        (1 << 4)
+       #define DBG_RESET_N     (1 << 5)
+
+       #define DBG_CLOCK_PIN   (P0_3)
+       #define DBG_DATA_PIN    (P0_4)
+       #define DBG_RESET_N_PIN (P0_5)
+
+       #define DBG_PORT_NUM    0
+       #define DBG_PORT        P0
+       #define DBG_PORT_SEL    P0SEL
+       #define DBG_PORT_INP    P0INP
+       #define DBG_PORT_DIR    P0DIR
+
+#endif /* DBG_ON_P0 */
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telebt-v1.0/ao_telebt.c b/src/telebt-v1.0/ao_telebt.c
new file mode 100644 (file)
index 0000000..935cde7
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright Â© 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+void
+main(void)
+{
+       ao_clock_init();
+
+       /* Turn on the LED until the system is stable */
+       ao_led_init(LEDS_AVAILABLE);
+       ao_led_on(AO_LED_RED);
+
+       ao_task_init();
+
+       ao_timer_init();
+       ao_cmd_init();
+       ao_usb_init();
+       ao_monitor_init();
+       ao_radio_init();
+       ao_packet_master_init();
+       ao_adc_init();
+       ao_btm_init();
+#if HAS_DBG
+       ao_dbg_init();
+#endif
+       ao_config_init();
+       ao_start_scheduler();
+}
diff --git a/src/telegps-v0.1/.gitignore b/src/telegps-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..b1e261f
--- /dev/null
@@ -0,0 +1,2 @@
+ao_product.h
+telegps-*.elf
diff --git a/src/telegps-v0.1/Makefile b/src/telegps-v0.1/Makefile
new file mode 100644 (file)
index 0000000..aae3766
--- /dev/null
@@ -0,0 +1,98 @@
+#
+# AltOS build
+#
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_pins.h \
+       ao_product.h \
+       ao_task.h \
+       ao_whiten.h \
+       ao_cc115l.h \
+       ao_fec.h \
+       stm32l.h \
+       ao_sdcard.h \
+       ao_bufio.h \
+       ao_fat.h \
+       Makefile
+
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+#SAMPLE_PROFILE=ao_sample_profile.c \
+#      ao_sample_profile_timer.c
+#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1
+
+#STACK_GUARD=ao_mpu_stm.c
+#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1
+
+ALTOS_SRC = \
+       ao_interrupt.c \
+       ao_product.c \
+       ao_romconfig.c \
+       ao_cmd.c \
+       ao_config.c \
+       ao_task.c \
+       ao_led.c \
+       ao_stdio.c \
+       ao_panic.c \
+       ao_timer.c \
+       ao_mutex.c \
+       ao_freq.c \
+       ao_dma_stm.c \
+       ao_spi_stm.c \
+       ao_usb_stm.c \
+       ao_exti_stm.c \
+       ao_serial_stm.c \
+       ao_gps_skytraq.c \
+       ao_cc115l.c \
+       ao_fec_tx.c \
+       ao_rfpa0133.c \
+       ao_aprs.c \
+       ao_storage.c \
+       ao_eeprom_stm.c \
+       ao_sdcard.c \
+       ao_bufio.c \
+       ao_fat.c \
+       ao_log_fat.c \
+       ao_gps_report_mega.c \
+       ao_telemetry.c \
+       $(SAMPLE_PROFILE)
+
+PRODUCT=TeleGPS-v0.1
+PRODUCT_DEF=-DTELEGPS
+IDPRODUCT=0x0025
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF) -Os -g
+
+PROGNAME=telegps-v0.1
+PROG=$(PROGNAME)-$(VERSION).elf
+
+SRC=$(ALTOS_SRC) ao_telegps.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG)
+
+$(PROG): Makefile $(OBJ) altos.ld
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(SAT_CLIB) -lgcc
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/telegps-v0.1/ao_pins.h b/src/telegps-v0.1/ao_pins.h
new file mode 100644 (file)
index 0000000..5bea268
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright Â© 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_TASK_QUEUE         1
+
+/* 8MHz High speed external crystal */
+#define AO_HSE                 8000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL              12
+#define AO_RCC_CFGR_PLLMUL     (STM_RCC_CFGR_PLLMUL_12)
+
+/* SYSCLK = 32MHz (no need to go faster than CPU) */
+#define AO_PLLDIV              3
+#define AO_RCC_CFGR_PLLDIV     (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHz (CPU clock) */
+#define AO_AHB_PRESCALER       1
+#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at 16MHz (HCLK/2) */
+#define AO_APB1_PRESCALER      2
+#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE2_DIV_2
+
+/* Run APB2 at 16MHz (HCLK/2) */
+#define AO_APB2_PRESCALER      2
+#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_2
+
+#define HAS_SERIAL_1           1
+#define USE_SERIAL_1_STDIN     0
+#define SERIAL_1_PB6_PB7       1
+#define SERIAL_1_PA9_PA10      0
+
+#define HAS_SERIAL_2           1
+#define USE_SERIAL_2_STDIN     0
+#define SERIAL_2_PA2_PA3       1
+#define SERIAL_2_PD5_PD6       0
+
+#define HAS_SERIAL_3           0
+#define USE_SERIAL_3_STDIN     0
+#define SERIAL_3_PB10_PB11     0
+#define SERIAL_3_PC10_PC11     1
+#define SERIAL_3_PD8_PD9       0
+
+#define ao_gps_getchar         ao_serial2_getchar
+#define ao_gps_putchar         ao_serial2_putchar
+#define ao_gps_set_speed       ao_serial2_set_speed
+#define ao_gps_fifo            (ao_stm_usart2.rx_fifo)
+
+#define HAS_EEPROM             1
+#define USE_INTERNAL_FLASH     1
+#define HAS_USB                        1
+#define HAS_BEEP               0
+#define HAS_RADIO              1
+#define HAS_TELEMETRY          1
+#define HAS_APRS               1
+#define HAS_RADIO_RECV         0
+
+#define HAS_SPI_1              1
+#define SPI_1_PA5_PA6_PA7      1       /* SD card */
+#define SPI_1_PB3_PB4_PB5      0
+#define SPI_1_PE13_PE14_PE15   0
+#define SPI_1_OSPEEDR          STM_OSPEEDR_10MHz
+
+#define SPI_1_PORT             (&stm_gpioa)
+#define SPI_1_SCK_PIN          5
+#define SPI_1_MISO_PIN         6
+#define SPI_1_MOSI_PIN         7
+
+#define HAS_SPI_2              1
+#define SPI_2_PB13_PB14_PB15   1       /* CC115L */
+#define SPI_2_PD1_PD3_PD4      0
+#define SPI_2_OSPEEDR          STM_OSPEEDR_10MHz
+
+#define SPI_2_PORT             (&stm_gpiob)
+#define SPI_2_SCK_PIN          13
+#define SPI_2_MISO_PIN         14
+#define SPI_2_MOSI_PIN         15
+
+#define HAS_I2C_1              0
+#define I2C_1_PB8_PB9          1
+
+#define HAS_I2C_2              0
+#define I2C_2_PB10_PB11                1
+
+#define PACKET_HAS_SLAVE       0
+#define PACKET_HAS_MASTER      0
+
+#define LOW_LEVEL_DEBUG                0
+
+#define LED_PORT_0_ENABLE      STM_RCC_AHBENR_GPIOAEN
+#define LED_PORT_0             (&stm_gpioa)
+#define LED_PORT_0_MASK                (0xff)
+#define LED_PORT_0_SHIFT       0
+#define LED_PIN_RED            0
+#define LED_PIN_GREEN          2
+#define AO_LED_RED             (1 << LED_PIN_RED)
+#define AO_LED_GREEN           (1 << LED_PIN_GREEN)
+
+#define LEDS_AVAILABLE         (AO_LED_RED | AO_LED_GREEN)
+
+#define HAS_GPS                        1
+#define HAS_FLIGHT             0
+#define HAS_ADC                        0
+#define HAS_LOG                        0
+
+/*
+ * Telemetry monitoring
+ */
+#define HAS_MONITOR            0
+#define LEGACY_MONITOR         0
+#define HAS_MONITOR_PUT                0
+#define AO_MONITOR_LED         AO_LED_GREEN
+
+/*
+ * Radio (cc115l)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT   0x10b6a5
+
+#define HAS_RADIO_POWER                1
+
+#define AO_FEC_DEBUG           0
+#define AO_CC115L_SPI_CS_PORT  (&stm_gpiob)
+#define AO_CC115L_SPI_CS_PIN   12
+#define AO_CC115L_SPI_BUS      AO_SPI_2_PB13_PB14_PB15
+#define AO_CC115L_SPI          stm_spi2
+
+#define AO_CC115L_FIFO_INT_GPIO_IOCFG  CC115L_IOCFG2
+#define AO_CC115L_FIFO_INT_PORT                (&stm_gpioa)
+#define AO_CC115L_FIFO_INT_PIN         (9)
+
+#define AO_CC115L_DONE_INT_GPIO_IOCFG  CC115L_IOCFG0
+#define AO_CC115L_DONE_INT_PORT                (&stm_gpioa)
+#define AO_CC115L_DONE_INT_PIN         (10)
+
+#define HAS_RADIO_AMP          1
+
+/*
+ * Power amplifier (RFPA0133)
+ */
+
+#define AO_PA_POWER_GPIO       (&stm_gpiob)
+#define AO_PA_POWER_PIN                1
+#define AO_PA_GAIN_8_GPIO      (&stm_gpiob)
+#define AO_PA_GAIN_8_PIN       10
+#define AO_PA_GAIN_16_GPIO     (&stm_gpiob)
+#define AO_PA_GAIN_16_PIN      11
+
+/*
+ * SD card
+ */
+
+#define AO_SDCARD_SPI_BUS      AO_SPI_1_PA5_PA6_PA7
+#define AO_SDCARD_SPI_PORT     SPI_1_PORT
+#define AO_SDCARD_SPI_SCK_PIN  SPI_1_SCK_PIN
+#define AO_SDCARD_SPI_MISO_PIN SPI_1_MISO_PIN
+#define AO_SDCARD_SPI_MOSI_PIN SPI_1_MOSI_PIN
+#define AO_SDCARD_SPI_CS_PORT  (&stm_gpioa)
+#define AO_SDCARD_SPI_CS_PIN   4
+#define AO_SDCARD_SPI          stm_spi1
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telegps-v0.1/ao_telegps.c b/src/telegps-v0.1/ao_telegps.c
new file mode 100644 (file)
index 0000000..68116bf
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+#include <ao_fat.h>
+
+uint16_t       ao_flight_number = 1;
+
+int
+main(void)
+{
+       ao_clock_init();
+       
+#if HAS_STACK_GUARD
+       ao_mpu_init();
+#endif
+
+       ao_task_init();
+//     ao_led_init(LEDS_AVAILABLE);
+//     ao_led_on(AO_LED_RED);
+       ao_timer_init();
+
+
+       ao_spi_init();
+       ao_dma_init();
+       ao_exti_init();
+
+       ao_storage_init();
+
+       ao_serial_init();
+
+       ao_cmd_init();
+
+       ao_usb_init();
+       ao_radio_init();
+
+       ao_fat_init();
+
+       ao_gps_init();
+       ao_gps_report_mega_init();
+
+       ao_telemetry_init();
+       ao_telemetry_set_interval(AO_SEC_TO_TICKS(1));
+       ao_rdf_set(1);
+
+#if HAS_SAMPLE_PROFILE
+       ao_sample_profile_init();
+#endif
+       ao_config_init();
+       
+       ao_start_scheduler();
+       return 0;
+}
index d2702dd66a877e2603bdcee03e53098cb7e3c0fa..a4a83d0224ec09b3acb221d14e36626131ac8025 100644 (file)
@@ -61,7 +61,7 @@ ALTOS_SRC = \
        ao_radio_cmac_cmd.c
 
 PRODUCT=TeleLCO-v0.1
-PRODUCT_DEF=-DMEGAMETRUM
+PRODUCT_DEF=-DTELEMEGA
 IDPRODUCT=0x0023
 
 CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) -Os -g
diff --git a/src/telemega-v0.1/.gitignore b/src/telemega-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..e67759a
--- /dev/null
@@ -0,0 +1,2 @@
+ao_product.h
+telemega-*.elf
diff --git a/src/telemega-v0.1/Makefile b/src/telemega-v0.1/Makefile
new file mode 100644 (file)
index 0000000..16393ea
--- /dev/null
@@ -0,0 +1,131 @@
+#
+# AltOS build
+#
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_companion.h \
+       ao_data.h \
+       ao_sample.h \
+       ao_pins.h \
+       altitude-pa.h \
+       ao_kalman.h \
+       ao_product.h \
+       ao_ms5607.h \
+       ao_hmc5883.h \
+       ao_mpu6000.h \
+       ao_mma655x.h \
+       ao_cc1120_CC1120.h \
+       ao_profile.h \
+       ao_task.h \
+       ao_whiten.h \
+       ao_sample_profile.h \
+       ao_mpu.h \
+       stm32l.h \
+       Makefile
+
+#
+# Common AltOS sources
+#
+#      ao_hmc5883.c
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+#SAMPLE_PROFILE=ao_sample_profile.c \
+#      ao_sample_profile_timer.c
+#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1
+
+#STACK_GUARD=ao_mpu_stm.c
+#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1
+
+ALTOS_SRC = \
+       ao_interrupt.c \
+       ao_product.c \
+       ao_romconfig.c \
+       ao_cmd.c \
+       ao_config.c \
+       ao_task.c \
+       ao_led.c \
+       ao_stdio.c \
+       ao_panic.c \
+       ao_timer.c \
+       ao_mutex.c \
+       ao_serial_stm.c \
+       ao_gps_skytraq.c \
+       ao_gps_report_mega.c \
+       ao_ignite.c \
+       ao_freq.c \
+       ao_dma_stm.c \
+       ao_spi_stm.c \
+       ao_cc1120.c \
+       ao_fec_tx.c \
+       ao_fec_rx.c \
+       ao_data.c \
+       ao_ms5607.c \
+       ao_mma655x.c \
+       ao_hmc5883.c \
+       ao_adc_stm.c \
+       ao_beep_stm.c \
+       ao_storage.c \
+       ao_m25.c \
+       ao_usb_stm.c \
+       ao_exti_stm.c \
+       ao_report.c \
+       ao_i2c_stm.c \
+       ao_mpu6000.c \
+       ao_convert_pa.c \
+       ao_log.c \
+       ao_log_mega.c \
+       ao_sample.c \
+       ao_kalman.c \
+       ao_flight.c \
+       ao_telemetry.c \
+       ao_packet_slave.c \
+       ao_packet.c \
+       ao_companion.c \
+       ao_pyro.c \
+       ao_aprs.c \
+       $(PROFILE) \
+       $(SAMPLE_PROFILE) \
+       $(STACK_GUARD)
+
+PRODUCT=TeleMega-v0.1
+PRODUCT_DEF=-DTELEMEGA
+IDPRODUCT=0x0023
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF) -Os -g
+
+PROGNAME=telemega-v0.1
+PROG=$(PROGNAME)-$(VERSION).elf
+
+SRC=$(ALTOS_SRC) ao_telemega.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG)
+
+$(PROG): Makefile $(OBJ) altos.ld
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(SAT_CLIB) -lgcc
+
+../altitude-pa.h: make-altitude-pa
+       nickle $< > $@
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/telemega-v0.1/ao_pins.h b/src/telemega-v0.1/ao_pins.h
new file mode 100644 (file)
index 0000000..4c64587
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * Copyright Â© 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_TASK_QUEUE         1
+
+/* 8MHz High speed external crystal */
+#define AO_HSE                 8000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL              12
+#define AO_RCC_CFGR_PLLMUL     (STM_RCC_CFGR_PLLMUL_12)
+
+/* SYSCLK = 32MHz (no need to go faster than CPU) */
+#define AO_PLLDIV              3
+#define AO_RCC_CFGR_PLLDIV     (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHz (CPU clock) */
+#define AO_AHB_PRESCALER       1
+#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at 16MHz (HCLK/2) */
+#define AO_APB1_PRESCALER      2
+#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE2_DIV_2
+
+/* Run APB2 at 16MHz (HCLK/2) */
+#define AO_APB2_PRESCALER      2
+#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_2
+
+#define HAS_SERIAL_1           1
+#define USE_SERIAL_1_STDIN     0
+#define SERIAL_1_PB6_PB7       0
+#define SERIAL_1_PA9_PA10      1
+
+#define HAS_SERIAL_2           0
+#define USE_SERIAL_2_STDIN     0
+#define SERIAL_2_PA2_PA3       0
+#define SERIAL_2_PD5_PD6       0
+
+#define HAS_SERIAL_3           1
+#define USE_SERIAL_3_STDIN     0
+#define SERIAL_3_PB10_PB11     0
+#define SERIAL_3_PC10_PC11     1
+#define SERIAL_3_PD8_PD9       0
+
+#define ao_gps_getchar         ao_serial3_getchar
+#define ao_gps_putchar         ao_serial3_putchar
+#define ao_gps_set_speed       ao_serial3_set_speed
+#define ao_gps_fifo            (ao_stm_usart3.rx_fifo)
+
+#define HAS_EEPROM             1
+#define USE_INTERNAL_FLASH     0
+#define HAS_USB                        1
+#define HAS_BEEP               1
+#define HAS_RADIO              1
+#define HAS_TELEMETRY          1
+#define HAS_APRS               1
+
+#define HAS_SPI_1              1
+#define SPI_1_PA5_PA6_PA7      1       /* Barometer */
+#define SPI_1_PB3_PB4_PB5      0
+#define SPI_1_PE13_PE14_PE15   1       /* Accelerometer */
+#define SPI_1_OSPEEDR          STM_OSPEEDR_10MHz
+
+#define HAS_SPI_2              1
+#define SPI_2_PB13_PB14_PB15   1       /* Flash, Companion */
+#define SPI_2_PD1_PD3_PD4      0
+#define SPI_2_OSPEEDR          STM_OSPEEDR_10MHz
+
+#define SPI_2_PORT             (&stm_gpiob)
+#define SPI_2_SCK_PIN          13
+#define SPI_2_MISO_PIN         14
+#define SPI_2_MOSI_PIN         15
+
+#define HAS_I2C_1              1
+#define I2C_1_PB8_PB9          1
+
+#define HAS_I2C_2              1
+#define I2C_2_PB10_PB11                1
+
+#define PACKET_HAS_SLAVE       1
+#define PACKET_HAS_MASTER      0
+
+#define LOW_LEVEL_DEBUG                0
+
+#define LED_PORT_ENABLE                STM_RCC_AHBENR_GPIOCEN
+#define LED_PORT               (&stm_gpioc)
+#define LED_PIN_RED            8
+#define LED_PIN_GREEN          9
+#define AO_LED_RED             (1 << LED_PIN_RED)
+#define AO_LED_GREEN           (1 << LED_PIN_GREEN)
+
+#define LEDS_AVAILABLE         (AO_LED_RED | AO_LED_GREEN)
+
+#define HAS_GPS                        1
+#define HAS_FLIGHT             1
+#define HAS_ADC                        1
+#define HAS_LOG                        1
+
+/*
+ * Igniter
+ */
+
+#define HAS_IGNITE             1
+#define HAS_IGNITE_REPORT      1
+
+#define AO_SENSE_DROGUE(p)     ((p)->adc.sense[0])
+#define AO_SENSE_MAIN(p)       ((p)->adc.sense[1])
+#define AO_IGNITER_CLOSED      400
+#define AO_IGNITER_OPEN                60
+
+#define AO_IGNITER_DROGUE_PORT (&stm_gpiod)
+#define AO_IGNITER_DROGUE_PIN  6
+
+#define AO_IGNITER_MAIN_PORT   (&stm_gpiod)
+#define AO_IGNITER_MAIN_PIN    7
+
+#define AO_PYRO_PORT_0 (&stm_gpiob)
+#define AO_PYRO_PIN_0  5
+
+#define AO_PYRO_PORT_1 (&stm_gpioe)
+#define AO_PYRO_PIN_1  4
+
+#define AO_PYRO_PORT_2 (&stm_gpioe)
+#define AO_PYRO_PIN_2  6
+
+#define AO_PYRO_PORT_3 (&stm_gpioe)
+#define AO_PYRO_PIN_3  5
+
+/* Number of general purpose pyro channels available */
+#define AO_PYRO_NUM    4
+
+#define AO_IGNITER_SET_DROGUE(v)       stm_gpio_set(AO_IGNITER_DROGUE_PORT, AO_IGNITER_DROGUE_PIN, v)
+#define AO_IGNITER_SET_MAIN(v)         stm_gpio_set(AO_IGNITER_MAIN_PORT, AO_IGNITER_MAIN_PIN, v)
+
+/*
+ * ADC
+ */
+#define AO_DATA_RING           32
+#define AO_ADC_NUM_SENSE       6
+
+struct ao_adc {
+       int16_t                 sense[AO_ADC_NUM_SENSE];
+       int16_t                 v_batt;
+       int16_t                 v_pbatt;
+       int16_t                 accel_ref;
+       int16_t                 accel;
+       int16_t                 temp;
+};
+
+#define AO_ADC_SENSE_A         0
+#define AO_ADC_SENSE_A_PORT    (&stm_gpioa)
+#define AO_ADC_SENSE_A_PIN     0
+
+#define AO_ADC_SENSE_B         1
+#define AO_ADC_SENSE_B_PORT    (&stm_gpioa)
+#define AO_ADC_SENSE_B_PIN     1
+
+#define AO_ADC_SENSE_C         2
+#define AO_ADC_SENSE_C_PORT    (&stm_gpioa)
+#define AO_ADC_SENSE_C_PIN     2
+
+#define AO_ADC_SENSE_D         3
+#define AO_ADC_SENSE_D_PORT    (&stm_gpioa)
+#define AO_ADC_SENSE_D_PIN     3
+
+#define AO_ADC_SENSE_E         4
+#define AO_ADC_SENSE_E_PORT    (&stm_gpioa)
+#define AO_ADC_SENSE_E_PIN     4
+
+#define AO_ADC_SENSE_F         22
+#define AO_ADC_SENSE_F_PORT    (&stm_gpioe)
+#define AO_ADC_SENSE_F_PIN     7
+
+#define AO_ADC_V_BATT          8
+#define AO_ADC_V_BATT_PORT     (&stm_gpiob)
+#define AO_ADC_V_BATT_PIN      0
+
+#define AO_ADC_V_PBATT         9
+#define AO_ADC_V_PBATT_PORT    (&stm_gpiob)
+#define AO_ADC_V_PBATT_PIN     1
+
+#define AO_ADC_ACCEL_REF       10
+#define AO_ADC_ACCEL_REF_PORT  (&stm_gpioc)
+#define AO_ADC_ACCEL_REF_PIN   0
+
+#define AO_ADC_ACCEL           11
+#define AO_ADC_ACCEL_PORT      (&stm_gpioc)
+#define AO_ADC_ACCEL_PIN       1
+
+#define AO_ADC_TEMP            16
+
+#define AO_ADC_RCC_AHBENR      ((1 << STM_RCC_AHBENR_GPIOAEN) | \
+                                (1 << STM_RCC_AHBENR_GPIOEEN) | \
+                                (1 << STM_RCC_AHBENR_GPIOBEN) | \
+                                (1 << STM_RCC_AHBENR_GPIOCEN))
+
+#define AO_NUM_ADC_PIN         (AO_ADC_NUM_SENSE + 4)
+
+#define AO_ADC_PIN0_PORT       AO_ADC_SENSE_A_PORT
+#define AO_ADC_PIN0_PIN                AO_ADC_SENSE_A_PIN
+#define AO_ADC_PIN1_PORT       AO_ADC_SENSE_B_PORT
+#define AO_ADC_PIN1_PIN                AO_ADC_SENSE_B_PIN
+#define AO_ADC_PIN2_PORT       AO_ADC_SENSE_C_PORT
+#define AO_ADC_PIN2_PIN                AO_ADC_SENSE_C_PIN
+#define AO_ADC_PIN3_PORT       AO_ADC_SENSE_D_PORT
+#define AO_ADC_PIN3_PIN                AO_ADC_SENSE_D_PIN
+#define AO_ADC_PIN4_PORT       AO_ADC_SENSE_E_PORT
+#define AO_ADC_PIN4_PIN                AO_ADC_SENSE_E_PIN
+#define AO_ADC_PIN5_PORT       AO_ADC_SENSE_F_PORT
+#define AO_ADC_PIN5_PIN                AO_ADC_SENSE_F_PIN
+#define AO_ADC_PIN6_PORT       AO_ADC_V_BATT_PORT
+#define AO_ADC_PIN6_PIN                AO_ADC_V_BATT_PIN
+#define AO_ADC_PIN7_PORT       AO_ADC_V_PBATT_PORT
+#define AO_ADC_PIN7_PIN                AO_ADC_V_PBATT_PIN
+#define AO_ADC_PIN8_PORT       AO_ADC_ACCEL_REF_PORT
+#define AO_ADC_PIN8_PIN                AO_ADC_ACCEL_REF_PIN
+#define AO_ADC_PIN9_PORT       AO_ADC_ACCEL_PORT
+#define AO_ADC_PIN9_PIN                AO_ADC_ACCEL_PIN
+
+#define AO_NUM_ADC             (AO_ADC_NUM_SENSE + 5)
+
+#define AO_ADC_SQ1             AO_ADC_SENSE_A
+#define AO_ADC_SQ2             AO_ADC_SENSE_B
+#define AO_ADC_SQ3             AO_ADC_SENSE_C
+#define AO_ADC_SQ4             AO_ADC_SENSE_D
+#define AO_ADC_SQ5             AO_ADC_SENSE_E
+#define AO_ADC_SQ6             AO_ADC_SENSE_F
+#define AO_ADC_SQ7             AO_ADC_V_BATT
+#define AO_ADC_SQ8             AO_ADC_V_PBATT
+#define AO_ADC_SQ9             AO_ADC_ACCEL_REF
+#define AO_ADC_SQ10            AO_ADC_ACCEL
+#define AO_ADC_SQ11            AO_ADC_TEMP
+
+/*
+ * Pressure sensor settings
+ */
+#define HAS_MS5607             1
+#define HAS_MS5611             0
+#define AO_MS5607_PRIVATE_PINS 1
+#define AO_MS5607_CS_PORT      (&stm_gpioc)
+#define AO_MS5607_CS_PIN       4
+#define AO_MS5607_CS_MASK      (1 << AO_MS5607_CS)
+#define AO_MS5607_MISO_PORT    (&stm_gpioa)
+#define AO_MS5607_MISO_PIN     6
+#define AO_MS5607_MISO_MASK    (1 << AO_MS5607_MISO)
+#define AO_MS5607_SPI_INDEX    AO_SPI_1_PA5_PA6_PA7
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS          1
+#define AO_M25_SPI_CS_PORT     (&stm_gpiod)
+#define AO_M25_SPI_CS_MASK     (1 << 3)
+#define AO_M25_SPI_BUS         AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Radio (cc1120)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT   0x6ca333
+
+#define AO_FEC_DEBUG           0
+#define AO_CC1120_SPI_CS_PORT  (&stm_gpioc)
+#define AO_CC1120_SPI_CS_PIN   5
+#define AO_CC1120_SPI_BUS      AO_SPI_2_PB13_PB14_PB15
+#define AO_CC1120_SPI          stm_spi2
+
+#define AO_CC1120_INT_PORT             (&stm_gpioc)
+#define AO_CC1120_INT_PIN              14
+#define AO_CC1120_MCU_WAKEUP_PORT      (&stm_gpioc)
+#define AO_CC1120_MCU_WAKEUP_PIN       (0)
+
+#define AO_CC1120_INT_GPIO     2
+#define AO_CC1120_INT_GPIO_IOCFG       CC1120_IOCFG2
+
+#define AO_CC1120_MARC_GPIO    3
+#define AO_CC1120_MARC_GPIO_IOCFG      CC1120_IOCFG3
+
+
+#define HAS_BOOT_RADIO         0
+
+/*
+ * Mag sensor (hmc5883)
+ */
+
+#define HAS_HMC5883            0
+#define AO_HMC5883_INT_PORT    (&stm_gpioc)
+#define AO_HMC5883_INT_PIN     12
+#define AO_HMC5883_I2C_INDEX   STM_I2C_INDEX(1)
+
+/*
+ * mpu6000
+ */
+
+#define HAS_MPU6000            1       
+#define AO_MPU6000_INT_PORT    (&stm_gpioc)
+#define AO_MPU6000_INT_PIN     13
+#define AO_MPU6000_I2C_INDEX   STM_I2C_INDEX(1)
+
+#define HAS_HIGHG_ACCEL                0
+
+/*
+ * mma655x
+ */
+
+#define HAS_MMA655X            1
+#define AO_MMA655X_SPI_INDEX   AO_SPI_1_PE13_PE14_PE15
+#define AO_MMA655X_CS_PORT     (&stm_gpiod)
+#define AO_MMA655X_CS_PIN      4
+
+#define NUM_CMDS               16
+
+/*
+ * Companion
+ */
+
+#define AO_COMPANION_CS_PORT   (&stm_gpiod)
+#define AO_COMPANION_CS_PIN    (0)
+#define AO_COMPANION_SPI_BUS   AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Monitor
+ */
+
+#define HAS_MONITOR            0
+#define LEGACY_MONITOR         0
+#define HAS_MONITOR_PUT                1
+#define AO_MONITOR_LED         0
+#define HAS_RSSI               0
+
+/*
+ * Profiling Viterbi decoding
+ */
+
+#ifndef AO_PROFILE
+#define AO_PROFILE             0
+#endif
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telemega-v0.1/ao_telemega.c b/src/telemega-v0.1/ao_telemega.c
new file mode 100644 (file)
index 0000000..fbdab64
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright Â© 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_hmc5883.h>
+#include <ao_mpu6000.h>
+#include <ao_mma655x.h>
+#include <ao_log.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_profile.h>
+#if HAS_SAMPLE_PROFILE
+#include <ao_sample_profile.h>
+#endif
+#include <ao_pyro.h>
+#if HAS_STACK_GUARD
+#include <ao_mpu.h>
+#endif
+
+int
+main(void)
+{
+       ao_clock_init();
+       
+#if HAS_STACK_GUARD
+       ao_mpu_init();
+#endif
+
+       ao_task_init();
+       ao_serial_init();
+       ao_led_init(LEDS_AVAILABLE);
+       ao_led_on(AO_LED_GREEN);
+       ao_timer_init();
+
+       ao_i2c_init();
+       ao_spi_init();
+       ao_dma_init();
+       ao_exti_init();
+
+       ao_adc_init();
+#if HAS_BEEP
+       ao_beep_init();
+#endif
+       ao_cmd_init();
+
+#if HAS_MS5607
+       ao_ms5607_init();
+#endif
+#if HAS_HMC5883
+       ao_hmc5883_init();
+#endif
+#if HAS_MPU6000
+       ao_mpu6000_init();
+#endif
+#if HAS_MMA655X
+       ao_mma655x_init();
+#endif
+
+       ao_storage_init();
+       
+       ao_flight_init();
+       ao_log_init();
+       ao_report_init();
+
+       ao_usb_init();
+       ao_gps_init();
+       ao_gps_report_mega_init();
+       ao_telemetry_init();
+       ao_radio_init();
+       ao_packet_slave_init(FALSE);
+       ao_igniter_init();
+       ao_companion_init();
+       ao_pyro_init();
+
+       ao_config_init();
+#if AO_PROFILE
+       ao_profile_init();
+#endif
+#if HAS_SAMPLE_PROFILE
+       ao_sample_profile_init();
+#endif
+       
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/telemega-v0.1/stlink-pins b/src/telemega-v0.1/stlink-pins
new file mode 100644 (file)
index 0000000..390f8e5
--- /dev/null
@@ -0,0 +1,57 @@
+ST discovery card pins
+
+1      AIN-1
+2      JTCK
+3      GND
+4      JTMS
+5      NRST
+6      SWO
+
+MegaMetrum v0.1 misc connector
+
+1      GND
+2      reset_n
+3      boot0
+4      tx1
+5      rx1
+6      +3.3V
+7      GND
+8      jtms
+9      jtck
+10     jtdi
+11     jtdo
+12     jntrst
+13     sda2
+14     scl2
+15     pe1
+16     pe0
+
+For debugging:
+
+       ST      MM v0.1
+JTCK   2       9
+GND    3       7
+JTMS   4       8
+NRST   5       2
+
+Altus Metrum STM32L standard debug connector (4 pin MicoMaTch):
+
+       TL      ST
+GND    1       3
+NRST   2       5
+SWDIO  3       4
+SWCLK  4       2
+
+Altus Metrum standard 4-pin connector to MegaMetrum v0.1 misc connector:
+
+       AMstd   MM v0.1
+gnd    1       1
+nrst   2       2
+swdio  3       8
+swclk  4       9
+
+MegaAccel:
+
+Jumpers
+PC0 (pin15) (blue)     PE0 (pin97)     accel_ref       (debug 16)
+PC1 (pin16) (green)    PE1 (pin98)     accel           (debug 15)
index 701e25fc314b3e8830b27a1f93cecad3106df4d5..30239afccf57bc71377860a837be3e7ddac8a9d8 100644 (file)
@@ -46,6 +46,7 @@
        #define AO_LED_RED              1
        #define AO_LED_GREEN            2
        #define AO_MONITOR_LED          AO_LED_RED
+       #define AO_BT_LED               AO_LED_GREEN
        #define LEDS_AVAILABLE          (AO_LED_RED|AO_LED_GREEN)
        #define SPI_CS_ON_P1            1
        #define SPI_CS_ON_P0            0
@@ -60,6 +61,7 @@
        #define LEGACY_MONITOR          0
        #define HAS_RSSI                0
        #define HAS_AES                 0
+       #define HAS_RADIO               1
 #endif
 
 #if DBG_ON_P1
index 8d79d168db0cda75551664666c2a4c8a22bc063c..8f74c348c186c7313dfd961ad8056f52e059c96b 100644 (file)
@@ -7,6 +7,7 @@ ao_gps_test
 ao_gps_test_skytraq
 ao_convert_test
 ao_convert_pa_test
+ao_fat_test
 ao_fec_test
 ao_flight_test_mm
 ao_flight_test_noisy_accel
index fccc793787da3bcfe92fb3ee1fd5aee64ede4015..a62b59c59c6aa5d7f3ea65995710fb5f7de98f7a 100644 (file)
@@ -2,7 +2,7 @@ vpath % ..:../core:../drivers:../util:../micropeak
 
 PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_flight_test_mm \
        ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test ao_fec_test \
-       ao_aprs_test ao_micropeak_test
+       ao_aprs_test ao_micropeak_test ao_fat_test
 
 INCS=ao_kalman.h ao_ms5607.h ao_log.h ao_data.h altitude-pa.h altitude.h
 
@@ -30,7 +30,7 @@ ao_flight_test_accel: ao_flight_test.c ao_host.h ao_flight.c  ao_sample.c ao_kal
        cc $(CFLAGS) -o $@ -DFORCE_ACCEL=1 ao_flight_test.c
 
 ao_flight_test_mm: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c $(INCS)
-       cc -DMEGAMETRUM=1 $(CFLAGS) -o $@ $< -lm
+       cc -DTELEMEGA=1 $(CFLAGS) -o $@ $< -lm
 
 ao_gps_test: ao_gps_test.c ao_gps_sirf.c ao_gps_print.c ao_host.h
        cc $(CFLAGS) -o $@ $<
@@ -64,3 +64,6 @@ check: ao_fec_test ao_flight_test ao_flight_test_baro run-tests
 
 ao_micropeak_test: ao_micropeak_test.c ao_microflight.c ao_kalman.h
        cc $(CFLAGS) -o $@ ao_micropeak_test.c -lm
+
+ao_fat_test: ao_fat_test.c ao_fat.c ao_bufio.c
+       cc $(CFLAGS) -o $@ ao_fat_test.c -lssl
index 3b31f2d35ae764ce16033129fbe5481470bab70b..dd5eac4dcc4f07d2781e352a9bd3ee2d4e4be15a 100644 (file)
@@ -107,7 +107,7 @@ int main(int argc, char **argv)
 }
 
 void
-ao_radio_send_lots(ao_radio_fill_func fill)
+ao_radio_send_aprs(ao_radio_fill_func fill)
 {
        int16_t len;
        uint8_t done = 0;
diff --git a/src/test/ao_fat_test.c b/src/test/ao_fat_test.c
new file mode 100644 (file)
index 0000000..d130902
--- /dev/null
@@ -0,0 +1,565 @@
+/*
+ * Copyright Â© 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <getopt.h>
+#include <math.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <openssl/md5.h>
+
+#define AO_FAT_TEST
+
+void
+ao_mutex_get(uint8_t *mutex)
+{
+}
+
+void
+ao_mutex_put(uint8_t *mutex)
+{
+}
+
+void
+ao_panic(uint8_t panic)
+{
+       printf ("panic %d\n", panic);
+       abort();
+}
+
+#define AO_PANIC_BUFIO 15
+
+#define ao_cmd_success 0
+
+uint8_t ao_cmd_status;
+uint32_t ao_cmd_lex_u32;
+
+void
+ao_cmd_decimal()
+{
+}
+
+#define ao_cmd_register(x)
+
+struct ao_cmds {
+       void            (*func)(void);
+       const char      *help;
+};
+
+int fs_fd;
+
+uint64_t       total_reads, total_writes;
+
+uint8_t
+ao_sdcard_read_block(uint32_t block, uint8_t *data)
+{
+       ++total_reads;
+       lseek(fs_fd, block * 512, 0);
+       return read(fs_fd, data, 512) == 512;
+}
+
+uint8_t
+ao_sdcard_write_block(uint32_t block, uint8_t *data)
+{
+       ++total_writes;
+       lseek(fs_fd, block * 512, 0);
+       return write(fs_fd, data, 512) == 512;
+}
+
+struct fs_param {
+       int     fat;
+       int     blocks;
+} fs_params[] = {
+       { .fat = 16, .blocks = 16384 },
+       { .fat = 32, .blocks = 16384 },
+       { .fat = 16, .blocks = 65536 },
+       { .fat = 32, .blocks = 65536 },
+       { .fat = 16, .blocks = 1048576 },
+       { .fat = 32, .blocks = 1048576 },
+       { .fat = 0, .blocks = 0 },
+};
+
+char           *fs = "fs.fat";
+struct fs_param        *param;
+
+void
+ao_sdcard_init(void)
+{
+       char    cmd[1024];
+
+       if (fs_fd) {
+               close(fs_fd);
+               fs_fd = 0;
+       }
+       snprintf(cmd, sizeof(cmd), "rm -f %s && mkfs.vfat -F %d -C %s %d",
+                fs, param->fat, fs, param->blocks);
+       if (system (cmd) != 0) {
+               fprintf(stderr, "'%s' failed\n", cmd);
+               exit(1);
+       }
+       fs_fd = open(fs, 2);
+       if (fs_fd < 0) {
+               perror (fs);
+               exit(1);
+       }
+}
+
+#include "ao_bufio.c"
+void
+check_bufio(char *where)
+{
+       int     b;
+
+       for (b = 0; b < AO_NUM_BUF; b++) {
+               if (ao_bufio[b].busy) {
+                       printf ("%s: buffer %d busy. block %d seqno %u\n",
+                               where, b, ao_bufio[b].block, ao_bufio[b].seqno & 0xffff);
+                       abort();
+               }
+       }
+}
+
+
+void
+check_fat(void);
+
+#include "ao_fat.c"
+
+/* Get the next cluster entry in the chain */
+static cluster_t
+ao_fat_entry_raw_read(cluster_t cluster, uint8_t fat)
+{
+       sector_t                sector;
+       cluster_offset_t        offset;
+       uint8_t                 *buf;
+       cluster_t               ret;
+
+       if (fat32)
+               cluster <<= 2;
+       else
+               cluster <<= 1;
+       sector = cluster >> SECTOR_SHIFT;
+       offset = cluster & SECTOR_MASK;
+       buf = _ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector);
+       if (!buf)
+               return 0;
+       if (fat32)
+               ret = get_u32(buf + offset);
+       else
+               ret = get_u16(buf + offset);
+       _ao_fat_sector_put(buf, 0);
+       return ret;
+}
+
+void
+dump_fat(void)
+{
+       int     e;
+
+       printf ("\n **** FAT ****\n\n");
+       for (e = 0; e < number_cluster; e++) {
+               if ((e & 0xf) == 0x0)
+                       printf ("%04x: ", e);
+               if (fat32)
+                       printf (" %08x", ao_fat_entry_raw_read(e, 0));
+               else
+                       printf (" %04x", ao_fat_entry_raw_read(e, 0));
+               if ((e & 0xf) == 0xf)
+                       putchar ('\n');
+       }
+       if (e & 0xf)
+               putchar('\n');
+}
+
+void
+fat_list(void)
+{
+       dirent_t                entry = 0;
+       struct ao_fat_dirent    dirent;
+
+       printf ("  **** Root directory ****\n");
+       while (ao_fat_readdir(&entry, &dirent)) {
+               printf ("%04x: %-8.8s.%-3.3s %02x %04x %d\n",
+                       entry,
+                       dirent.name,
+                       dirent.name + 8,
+                       dirent.attr,
+                       dirent.cluster,
+                       dirent.size);
+       }
+
+       printf ("  **** End of root directory ****\n");
+}
+
+void
+fatal(char *msg, ...)
+{
+//     dump_fat();
+//     fat_list();
+
+       va_list l;
+       va_start(l, msg);
+       vfprintf(stderr, msg, l);
+       va_end(l);
+
+       abort();
+}
+
+void
+check_fat(void)
+{
+       cluster_t       e;
+       int             f;
+
+       for (e = 0; e < number_cluster; e++) {
+               cluster_t       v = ao_fat_entry_raw_read(e, 0);
+               for (f = 1; f < number_fat; f++) {
+                       cluster_t       o = ao_fat_entry_raw_read(e, f);
+                       if (o != v)
+                               fatal ("fats differ at %08x (0 %08x %d %08x)\n", e, v, f, o);
+               }
+       }
+}
+
+cluster_t
+check_file(dirent_t dent, cluster_t first_cluster, dirent_t *used)
+{
+       cluster_t       clusters = 0;
+       cluster_t       cluster;
+
+       if (!first_cluster)
+               return 0;
+       
+       for (cluster = first_cluster;
+            fat32 ? !AO_FAT_IS_LAST_CLUSTER(cluster) : !AO_FAT_IS_LAST_CLUSTER16(cluster);
+            cluster = ao_fat_entry_raw_read(cluster, 0))
+       {
+               if (!_ao_fat_cluster_valid(cluster))
+                       fatal("file %d: invalid cluster %08x\n", dent, cluster);
+               if (used[cluster])
+                       fatal("file %d: duplicate cluster %08x also in file %d\n", dent, cluster, used[cluster]-1);
+               used[cluster] = dent;
+               clusters++;
+       }
+       return clusters;
+}
+
+void
+check_fs(void)
+{
+       dirent_t        r;
+       cluster_t       cluster, chain;
+       dirent_t        *used;
+       uint8_t         *dent;
+
+       check_fat();
+
+       used = calloc(sizeof (dirent_t), number_cluster);
+
+       for (r = 0; (dent = _ao_fat_root_get(r)); r++) {
+               cluster_t       clusters;
+               offset_t        size;
+               cluster_t       first_cluster;
+               char            name[11];
+
+               if (!dent)
+                       fatal("cannot map dent %d\n", r);
+               memcpy(name, dent+0, 11);
+               first_cluster = get_u16(dent + 0x1a);
+               if (fat32)
+                       first_cluster |= (cluster_t) get_u16(dent + 0x14) << 16;
+               size = get_u32(dent + 0x1c);
+               _ao_fat_root_put(dent, r, 0);
+
+               if (name[0] == AO_FAT_DENT_END) {
+                       break;
+               }
+
+               clusters = check_file(r + 1, first_cluster, used);
+               if (size == 0) {
+                       if (clusters != 0)
+                               fatal("file %d: zero sized, but %d clusters\n", clusters);
+               } else {
+                       if (size > clusters * bytes_per_cluster)
+                               fatal("file %d: size %u beyond clusters %d (%u)\n",
+                                     r, size, clusters, clusters * bytes_per_cluster);
+                       if (size <= (clusters - 1) * bytes_per_cluster)
+                               fatal("file %d: size %u too small clusters %d (%u)\n",
+                                     r, size, clusters, clusters * bytes_per_cluster);
+               }
+       }
+       if (!fat32) {
+               for (; r < root_entries; r++) {
+                       uint8_t *dent = _ao_fat_root_get(r);
+                       if (!dent)
+                               fatal("cannot map dent %d\n", r);
+                       if (dent[0] != AO_FAT_DENT_END)
+                               fatal("found non-zero dent past end %d\n", r);
+                       _ao_fat_root_put(dent, r, 0);
+               }
+       } else {
+               check_file((dirent_t) -1, root_cluster, used);
+       }
+
+       for (cluster = 0; cluster < 2; cluster++) {
+               chain = ao_fat_entry_raw_read(cluster, 0);
+
+               if (fat32) {
+                       if ((chain & 0xffffff8) != 0xffffff8)
+                               fatal("cluster %d: not marked busy\n", cluster);
+               } else {
+                       if ((chain & 0xfff8) != 0xfff8)
+                               fatal("cluster %d: not marked busy\n", cluster);
+               }
+       }
+       for (; cluster < number_cluster; cluster++) {
+               chain = ao_fat_entry_raw_read(cluster, 0);
+
+               if (chain != 0) {
+                       if (used[cluster] == 0)
+                               fatal("cluster %d: marked busy, but not in any file\n", cluster);
+               } else {
+                       if (used[cluster] != 0)
+                               fatal("cluster %d: marked free, but found in file %d\n", cluster, used[cluster]-1);
+               }
+       }
+}
+
+#define NUM_FILES      100
+#define LINES_FILE     500000
+
+uint32_t               sizes[NUM_FILES];
+
+unsigned char          md5[NUM_FILES][MD5_DIGEST_LENGTH];
+
+void
+micro_test_fs(void)
+{
+       int8_t  fd;
+       char    name[] = "FOO        ";
+       char    buf[512];
+       int     len;
+
+       printf ("write once\n");
+       if ((fd = ao_fat_creat(name)) >= 0) {
+               ao_fat_write(fd, "hello world\n", 12);
+               ao_fat_close(fd);
+       }
+
+       printf ("read first\n");
+       if ((fd = ao_fat_open(name, AO_FAT_OPEN_READ)) >= 0) {
+               len = ao_fat_read(fd, buf, sizeof(buf));
+               write (1, buf, len);
+               ao_fat_close(fd);
+       }
+       
+       printf ("write again\n");
+       if ((fd = ao_fat_creat(name)) >= 0) {
+               ao_fat_write(fd, "hi\n", 3);
+               ao_fat_close(fd);
+       }
+
+       printf ("read again\n");
+       if ((fd = ao_fat_open(name, AO_FAT_OPEN_READ)) >= 0) {
+               len = ao_fat_read(fd, buf, sizeof(buf));
+               write (1, buf, len);
+               ao_fat_close(fd);
+       }
+
+       printf ("write 3\n");
+       if ((fd = ao_fat_creat(name)) >= 0) {
+               int     l;
+               char    c;
+
+               for (l = 0; l < 10; l++) {
+                       for (c = ' '; c < '~'; c++)
+                               ao_fat_write(fd, &c, 1);
+                       c = '\n';
+                       ao_fat_write(fd, &c, 1);
+               }
+               ao_fat_close(fd);
+       }
+
+       printf ("read 3\n");
+       if ((fd = ao_fat_open(name, AO_FAT_OPEN_READ)) >= 0) {
+               while ((len = ao_fat_read(fd, buf, sizeof(buf))) > 0)
+                       write (1, buf, len);
+               ao_fat_close(fd);
+       }
+
+       check_fs();
+       printf ("all done\n");
+}
+
+
+void
+short_test_fs(void)
+{
+       int     len;
+       int8_t  fd;
+       char    buf[345];
+
+       if ((fd = ao_fat_open("HELLO   TXT",AO_FAT_OPEN_READ)) >= 0) {
+               printf ("File contents for HELLO.TXT\n");
+               while ((len = ao_fat_read(fd, buf, sizeof(buf))))
+                       write(1, buf, len);
+               ao_fat_close(fd);
+       }
+       
+       if ((fd = ao_fat_creat("NEWFILE TXT")) >= 0) {
+               printf ("Create new file\n");
+               for (len = 0; len < 2; len++)
+                       ao_fat_write(fd, "hello, world!\n", 14);
+               ao_fat_seek(fd, 0, AO_FAT_SEEK_SET);
+               printf ("read new file\n");
+               while ((len = ao_fat_read(fd, buf, sizeof (buf))))
+                       write (1, buf, len);
+               ao_fat_close(fd);
+       }
+
+       check_fs();
+}
+
+void
+long_test_fs(void)
+{
+       char    name[12];
+       int     id;
+       MD5_CTX ctx;
+       unsigned char   md5_check[MD5_DIGEST_LENGTH];
+       char buf[337];
+       int     len;
+       int8_t  fd;
+       uint64_t        total_file_size = 0;
+
+       total_reads = total_writes = 0;
+
+       printf ("   **** Creating %d files\n", NUM_FILES);
+
+       memset(sizes, '\0', sizeof (sizes));
+       for (id = 0; id < NUM_FILES; id++) {
+               sprintf(name, "D%07dTXT", id);
+               if ((id % ((NUM_FILES+49)/50)) == 0) {
+                       printf ("."); fflush(stdout);
+               }
+               if ((fd = ao_fat_creat(name)) >= 0) {
+                       int j;
+                       char    line[64];
+                       check_bufio("file created");
+                       MD5_Init(&ctx);
+                       for (j = 0; j < LINES_FILE; j++) {
+                               int len, ret;
+                               sprintf (line, "Hello, world %d %d\r\n", id, j);
+                               len = strlen(line);
+                               ret = ao_fat_write(fd, line, len);
+                               if (ret <= 0)
+                                       break;
+                               total_file_size += ret;
+                               MD5_Update(&ctx, line, ret);
+                               sizes[id] += ret;
+                               if (ret != len)
+                                       printf ("write failed %d\n", ret);
+                       }
+                       ao_fat_close(fd);
+                       MD5_Final(&md5[id][0], &ctx);
+                       check_bufio("file written");
+               }
+       }
+
+       printf ("\n   **** Write IO: read %llu write %llu data sectors %llu\n", total_reads, total_writes, (total_file_size + 511) / 512);
+
+       check_bufio("all files created");
+       printf ("   **** All done creating files\n");
+       check_fs();
+
+       total_reads = total_writes = 0;
+
+       printf ("   **** Comparing %d files\n", NUM_FILES);
+
+       for (id = 0; id < NUM_FILES; id++) {
+               uint32_t size;
+               sprintf(name, "D%07dTXT", id);
+               size = 0;
+               if ((id % ((NUM_FILES+49)/50)) == 0) {
+                       printf ("."); fflush(stdout);
+               }
+               if ((fd = ao_fat_open(name, AO_FAT_OPEN_READ)) >= 0) {
+                       MD5_Init(&ctx);
+                       while ((len = ao_fat_read(fd, buf, sizeof(buf))) > 0) {
+                               MD5_Update(&ctx, buf, len);
+                               size += len;
+                       }
+                       ao_fat_close(fd);
+                       MD5_Final(md5_check, &ctx);
+                       if (size != sizes[id])
+                               fatal("file %d: size differs %d written %d read\n",
+                                     id, sizes[id], size);
+                       if (memcmp(md5_check, &md5[id][0], sizeof (md5_check)) != 0)
+                               fatal ("file %d: checksum failed\n", id);
+                       check_bufio("file shown");
+               }
+       }
+       printf ("\n  **** Read IO: read %llu write %llu\n", total_reads, total_writes);
+}
+
+char *params[] = {
+       "-F 16 -C %s 16384",
+       "-F 32 -C %s 16384",
+       "-F 16 -C %s 65536",
+       "-F 32 -C %s 65536",
+       "-F 16 -C %s 1048576",
+       "-F 32 -C %s 1048576",
+       NULL
+};
+
+void
+do_test(void (*test)(void))
+{
+       ao_fat_init();
+
+       check_bufio("top");
+       _ao_fat_setup();
+
+       check_fs();
+       check_bufio("after setup");
+       (*test)();
+       ao_fat_unmount();
+}
+
+int
+main(int argc, char **argv)
+{
+       int     p;
+
+       if (argv[1])
+               fs = argv[1];
+
+       for (p = 0; fs_params[p].fat; p++) {
+               param = &fs_params[p];
+
+               do_test(micro_test_fs);
+               do_test(short_test_fs);
+               do_test(long_test_fs);
+       }
+       unlink (fs);
+
+       return 0;
+}
index cdd1f2369898726ce1739ded988043d6c4924386..99bed7eeb94df17dbd94f3560d5b7fe93a2c2ca8 100644 (file)
@@ -35,7 +35,7 @@
 #define AO_MS_TO_SPEED(ms)     ((int16_t) ((ms) * 16))
 #define AO_MSS_TO_ACCEL(mss)   ((int16_t) ((mss) * 16))
 
-#if MEGAMETRUM
+#if TELEMEGA
 #define AO_ADC_NUM_SENSE       6
 #define HAS_MS5607             1
 #define HAS_MPU6000            1
@@ -195,7 +195,7 @@ struct ao_cmds {
 #define ao_xmemcmp(d,s,c) memcmp(d,s,c)
 
 #define AO_NEED_ALTITUDE_TO_PRES 1
-#if MEGAMETRUM
+#if TELEMEGA
 #include "ao_convert_pa.c"
 #include <ao_ms5607.h>
 struct ao_ms5607_prom  ms5607_prom;
@@ -333,7 +333,7 @@ ao_insert(void)
 #else
                double  accel = 0.0;
 #endif
-#if MEGAMETRUM
+#if TELEMEGA
                double  height;
 
                ao_ms5607_convert(&ao_data_static.ms5607_raw, &ao_data_static.ms5607_cooked);
@@ -373,7 +373,7 @@ ao_insert(void)
 
                if (!ao_summary) {
                        printf("%7.2f height %8.2f accel %8.3f "
-#if MEGAMETRUM
+#if TELEMEGA
                               "roll %8.3f angle %8.3f qangle %8.3f "
                               "accel_x %8.3f accel_y %8.3f accel_z %8.3f gyro_x %8.3f gyro_y %8.3f gyro_z %8.3f "
 #endif
@@ -381,7 +381,7 @@ ao_insert(void)
                               time,
                               height,
                               accel,
-#if MEGAMETRUM
+#if TELEMEGA
                               ao_mpu6000_gyro(ao_sample_roll_angle) / 100.0,
                               ao_mpu6000_gyro(ao_sample_angle) / 100.0,
                               ao_sample_qangle,
@@ -555,7 +555,7 @@ int32(uint8_t *bytes, int off)
 
 static int log_format;
 
-#if MEGAMETRUM
+#if TELEMEGA
 
 static double
 ao_vec_norm(double x, double y, double z)
@@ -774,7 +774,7 @@ ao_sleep(void *wchan)
                for (;;) {
                        if (ao_records_read > 2 && ao_flight_state == ao_flight_startup)
                        {
-#if MEGAMETRUM
+#if TELEMEGA
                                ao_data_static.mpu6000 = ao_ground_mpu6000;
 #else
                                ao_data_static.adc.accel = ao_flight_ground_accel;
@@ -800,8 +800,8 @@ ao_sleep(void *wchan)
                                if (words[nword] == NULL)
                                        break;
                        }
-#if MEGAMETRUM
-                       if (log_format == AO_LOG_FORMAT_MEGAMETRUM && nword == 30 && strlen(words[0]) == 1) {
+#if TELEMEGA
+                       if (log_format == AO_LOG_FORMAT_TELEMEGA && nword == 30 && strlen(words[0]) == 1) {
                                int     i;
                                struct ao_ms5607_value  value;
 
@@ -885,7 +885,7 @@ ao_sleep(void *wchan)
                                continue;
                        }
 #else
-                       if (nword == 4 && log_format != AO_LOG_FORMAT_MEGAMETRUM) {
+                       if (nword == 4 && log_format != AO_LOG_FORMAT_TELEMEGA) {
                                type = words[0][0];
                                tick = strtoul(words[1], NULL, 16);
                                a = strtoul(words[2], NULL, 16);
@@ -1002,7 +1002,7 @@ ao_sleep(void *wchan)
                        if (type != 'F' && !ao_flight_started)
                                continue;
 
-#if MEGAMETRUM
+#if TELEMEGA
                        (void) a;
                        (void) b;
 #else
index b4a84b97c8824666ac10832d913f092ac68d1117..386dd2869ef1e12f688cb35aa9c27845854e2490 100755 (executable)
@@ -24,24 +24,88 @@ DefaultDestDir      = 12
 %TeleMetrum%   = AltusMetrum.Install, USB\VID_FFFE&PID_000B, AltusMetrumSerial\r
 %TeleDongle%   = AltusMetrum.Install, USB\VID_FFFE&PID_000C, AltusMetrumSerial\r
 %TeleTerra%    = AltusMetrum.Install, USB\VID_FFFE&PID_000D, AltusMetrumSerial\r
+%TeleBT%       = AltusMetrum.Install, USB\VID_FFFE&PID_000e, AltusMetrumSerial\r
+%TeleLaunch%   = AltusMetrum.Install, USB\VID_FFFE&PID_000f, AltusMetrumSerial\r
+%TeleLCO%      = AltusMetrum.Install, USB\VID_FFFE&PID_0010, AltusMetrumSerial\r
+%TeleScience%  = AltusMetrum.Install, USB\VID_FFFE&PID_0011, AltusMetrumSerial\r
+%TelePyro%     = AltusMetrum.Install, USB\VID_FFFE&PID_0012, AltusMetrumSerial\r
+%TeleShield%   = AltusMetrum.Install, USB\VID_FFFE&PID_0013, AltusMetrumSerial\r
+%TeleMega%     = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial\r
+%MegaDongle    = AltusMetrum.Install, USB\VID_FFFE&PID_0024, AltusMetrumSerial\r
+%TeleGPS%      = AltusMetrum.Install, USB\VID_FFFE&PID_0025, AltusMetrumSerial\r
+%AltusMetrum26%        = AltusMetrum.Install, USB\VID_FFFE&PID_0026, AltusMetrumSerial\r
+%AltusMetrum27%        = AltusMetrum.Install, USB\VID_FFFE&PID_0027, AltusMetrumSerial\r
+%AltusMetrum28%        = AltusMetrum.Install, USB\VID_FFFE&PID_0028, AltusMetrumSerial\r
+%AltusMetrum29%        = AltusMetrum.Install, USB\VID_FFFE&PID_0029, AltusMetrumSerial\r
+%AltusMetrum2a%        = AltusMetrum.Install, USB\VID_FFFE&PID_002a, AltusMetrumSerial\r
+%AltusMetrum2b%        = AltusMetrum.Install, USB\VID_FFFE&PID_002b, AltusMetrumSerial\r
+%AltusMetrum2c%        = AltusMetrum.Install, USB\VID_FFFE&PID_002c, AltusMetrumSerial\r
 \r
 [Models.NTx86]\r
 %AltusMetrum%  = AltusMetrum.Install, USB\VID_FFFE&PID_000A, AltusMetrumSerial\r
 %TeleMetrum%   = AltusMetrum.Install, USB\VID_FFFE&PID_000B, AltusMetrumSerial\r
 %TeleDongle%   = AltusMetrum.Install, USB\VID_FFFE&PID_000C, AltusMetrumSerial\r
 %TeleTerra%    = AltusMetrum.Install, USB\VID_FFFE&PID_000D, AltusMetrumSerial\r
+%TeleBT%       = AltusMetrum.Install, USB\VID_FFFE&PID_000e, AltusMetrumSerial\r
+%TeleLaunch%   = AltusMetrum.Install, USB\VID_FFFE&PID_000f, AltusMetrumSerial\r
+%TeleLCO%      = AltusMetrum.Install, USB\VID_FFFE&PID_0010, AltusMetrumSerial\r
+%TeleScience%  = AltusMetrum.Install, USB\VID_FFFE&PID_0011, AltusMetrumSerial\r
+%TelePyro%     = AltusMetrum.Install, USB\VID_FFFE&PID_0012, AltusMetrumSerial\r
+%TeleShield%   = AltusMetrum.Install, USB\VID_FFFE&PID_0013, AltusMetrumSerial\r
+%TeleMega%     = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial\r
+%MegaDongle    = AltusMetrum.Install, USB\VID_FFFE&PID_0024, AltusMetrumSerial\r
+%TeleGPS%      = AltusMetrum.Install, USB\VID_FFFE&PID_0025, AltusMetrumSerial\r
+%AltusMetrum26%        = AltusMetrum.Install, USB\VID_FFFE&PID_0026, AltusMetrumSerial\r
+%AltusMetrum27%        = AltusMetrum.Install, USB\VID_FFFE&PID_0027, AltusMetrumSerial\r
+%AltusMetrum28%        = AltusMetrum.Install, USB\VID_FFFE&PID_0028, AltusMetrumSerial\r
+%AltusMetrum29%        = AltusMetrum.Install, USB\VID_FFFE&PID_0029, AltusMetrumSerial\r
+%AltusMetrum2a%        = AltusMetrum.Install, USB\VID_FFFE&PID_002a, AltusMetrumSerial\r
+%AltusMetrum2b%        = AltusMetrum.Install, USB\VID_FFFE&PID_002b, AltusMetrumSerial\r
+%AltusMetrum2c%        = AltusMetrum.Install, USB\VID_FFFE&PID_002c, AltusMetrumSerial\r
 \r
 [Models.NTamd64]\r
 %AltusMetrum%  = AltusMetrum.Install, USB\VID_FFFE&PID_000A, AltusMetrumSerial\r
 %TeleMetrum%   = AltusMetrum.Install, USB\VID_FFFE&PID_000B, AltusMetrumSerial\r
 %TeleDongle%   = AltusMetrum.Install, USB\VID_FFFE&PID_000C, AltusMetrumSerial\r
 %TeleTerra%    = AltusMetrum.Install, USB\VID_FFFE&PID_000D, AltusMetrumSerial\r
+%TeleBT%       = AltusMetrum.Install, USB\VID_FFFE&PID_000e, AltusMetrumSerial\r
+%TeleLaunch%   = AltusMetrum.Install, USB\VID_FFFE&PID_000f, AltusMetrumSerial\r
+%TeleLCO%      = AltusMetrum.Install, USB\VID_FFFE&PID_0010, AltusMetrumSerial\r
+%TeleScience%  = AltusMetrum.Install, USB\VID_FFFE&PID_0011, AltusMetrumSerial\r
+%TelePyro%     = AltusMetrum.Install, USB\VID_FFFE&PID_0012, AltusMetrumSerial\r
+%TeleShield%   = AltusMetrum.Install, USB\VID_FFFE&PID_0013, AltusMetrumSerial\r
+%TeleMega%     = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial\r
+%MegaDongle    = AltusMetrum.Install, USB\VID_FFFE&PID_0024, AltusMetrumSerial\r
+%TeleGPS%      = AltusMetrum.Install, USB\VID_FFFE&PID_0025, AltusMetrumSerial\r
+%AltusMetrum26%        = AltusMetrum.Install, USB\VID_FFFE&PID_0026, AltusMetrumSerial\r
+%AltusMetrum27%        = AltusMetrum.Install, USB\VID_FFFE&PID_0027, AltusMetrumSerial\r
+%AltusMetrum28%        = AltusMetrum.Install, USB\VID_FFFE&PID_0028, AltusMetrumSerial\r
+%AltusMetrum29%        = AltusMetrum.Install, USB\VID_FFFE&PID_0029, AltusMetrumSerial\r
+%AltusMetrum2a%        = AltusMetrum.Install, USB\VID_FFFE&PID_002a, AltusMetrumSerial\r
+%AltusMetrum2b%        = AltusMetrum.Install, USB\VID_FFFE&PID_002b, AltusMetrumSerial\r
+%AltusMetrum2c%        = AltusMetrum.Install, USB\VID_FFFE&PID_002c, AltusMetrumSerial\r
 \r
 [Models.NTia64]\r
 %AltusMetrum%  = AltusMetrum.Install, USB\VID_FFFE&PID_000A, AltusMetrumSerial\r
 %TeleMetrum%   = AltusMetrum.Install, USB\VID_FFFE&PID_000B, AltusMetrumSerial\r
 %TeleDongle%   = AltusMetrum.Install, USB\VID_FFFE&PID_000C, AltusMetrumSerial\r
 %TeleTerra%    = AltusMetrum.Install, USB\VID_FFFE&PID_000D, AltusMetrumSerial\r
+%TeleBT%       = AltusMetrum.Install, USB\VID_FFFE&PID_000e, AltusMetrumSerial\r
+%TeleLaunch%   = AltusMetrum.Install, USB\VID_FFFE&PID_000f, AltusMetrumSerial\r
+%TeleLCO%      = AltusMetrum.Install, USB\VID_FFFE&PID_0010, AltusMetrumSerial\r
+%TeleScience%  = AltusMetrum.Install, USB\VID_FFFE&PID_0011, AltusMetrumSerial\r
+%TelePyro%     = AltusMetrum.Install, USB\VID_FFFE&PID_0012, AltusMetrumSerial\r
+%TeleShield%   = AltusMetrum.Install, USB\VID_FFFE&PID_0013, AltusMetrumSerial\r
+%TeleMega%     = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial\r
+%MegaDongle    = AltusMetrum.Install, USB\VID_FFFE&PID_0024, AltusMetrumSerial\r
+%TeleGPS%      = AltusMetrum.Install, USB\VID_FFFE&PID_0025, AltusMetrumSerial\r
+%AltusMetrum26%        = AltusMetrum.Install, USB\VID_FFFE&PID_0026, AltusMetrumSerial\r
+%AltusMetrum27%        = AltusMetrum.Install, USB\VID_FFFE&PID_0027, AltusMetrumSerial\r
+%AltusMetrum28%        = AltusMetrum.Install, USB\VID_FFFE&PID_0028, AltusMetrumSerial\r
+%AltusMetrum29%        = AltusMetrum.Install, USB\VID_FFFE&PID_0029, AltusMetrumSerial\r
+%AltusMetrum2a%        = AltusMetrum.Install, USB\VID_FFFE&PID_002a, AltusMetrumSerial\r
+%AltusMetrum2b%        = AltusMetrum.Install, USB\VID_FFFE&PID_002b, AltusMetrumSerial\r
+%AltusMetrum2c%        = AltusMetrum.Install, USB\VID_FFFE&PID_002c, AltusMetrumSerial\r
 \r
 ;----------------------------------------------------------------------------\r
 ; Installation sections\r