Merge branch 'master' into aprs
authorKeith Packard <keithp@keithp.com>
Fri, 7 Dec 2012 18:14:11 +0000 (10:14 -0800)
committerKeith Packard <keithp@keithp.com>
Fri, 7 Dec 2012 18:14:11 +0000 (10:14 -0800)
src/core/ao.h
src/core/ao_config.c
src/core/ao_telemetry.c
src/drivers/ao_aprs.c [new file with mode: 0644]
src/drivers/ao_aprs.h [new file with mode: 0644]
src/drivers/ao_cc1120.c
src/megametrum-v0.1/Makefile
src/megametrum-v0.1/ao_pins.h
src/test/Makefile
src/test/ao_aprs_test.c [new file with mode: 0644]

index 54018b371a058d60ba888b8020c45bbbda41d70f..fa873efeba12b49f67c754677d0c4baa7c529e74 100644 (file)
@@ -529,6 +529,11 @@ ao_radio_recv_abort(void);
 void
 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);
+
 /*
  * Compute the packet length as follows:
  *
@@ -712,6 +717,11 @@ struct ao_config {
 #define AO_IGNITE_MODE_APOGEE          1
 #define AO_IGNITE_MODE_MAIN            2
 
+#define AO_RADIO_ENABLE_CORE           1
+#define AO_RADIO_ENABLE_APRS           2
+#define AO_RADIO_DISABLE_TELEMETRY     4
+#define AO_RADIO_DISABLE_RDF           8
+
 #define AO_PAD_ORIENTATION_ANTENNA_UP  0
 #define AO_PAD_ORIENTATION_ANTENNA_DOWN        1
 
index 797fe7ec6aedfec9db3eae4fbda5b13b9ad2c936..631581586e5c2c6168d69416b63a8be3843b24dc 100644 (file)
@@ -128,7 +128,7 @@ _ao_config_get(void)
                if (minor < 6)
                        ao_config.pad_orientation = AO_CONFIG_DEFAULT_PAD_ORIENTATION;
                if (minor < 8)
-                       ao_config.radio_enable = TRUE;
+                       ao_config.radio_enable = AO_RADIO_ENABLE_CORE;
                if (minor < 9)
                        ao_xmemset(&ao_config.aes_key, '\0', AO_AES_LEN);
                if (minor < 10)
index 52ac94891adebd99f915cf97a20e95ddf171a18c..cfc72e048fe4b3c19f4c71fd41184be2dc110dcc 100644 (file)
@@ -22,6 +22,12 @@ static __pdata uint16_t ao_telemetry_interval;
 static __pdata uint8_t ao_rdf = 0;
 static __pdata uint16_t ao_rdf_time;
 
+#if HAS_APRS
+static __pdata uint16_t ao_aprs_time;
+
+#include <ao_aprs.h>
+#endif
+
 #if defined(MEGAMETRUM)
 #define AO_SEND_MEGA   1
 #endif
@@ -288,30 +294,41 @@ ao_telemetry(void)
                while (ao_telemetry_interval == 0)
                        ao_sleep(&telemetry);
                time = ao_rdf_time = ao_time();
+#if HAS_APRS
+               ao_aprs_time = time;
+#endif
                while (ao_telemetry_interval) {
 
 
+#if HAS_APRS
+                       if (!(ao_config.radio_enable & AO_RADIO_DISABLE_TELEMETRY))
+#endif
+                       {
 #ifdef AO_SEND_ALL_BARO
-                       ao_send_baro();
+                               ao_send_baro();
 #endif
 #ifdef AO_SEND_MEGA
-                       ao_send_mega_sensor();
-                       ao_send_mega_data();
+                               ao_send_mega_sensor();
+                               ao_send_mega_data();
 #else
-                       ao_send_sensor();
+                               ao_send_sensor();
 #endif
 
 #if HAS_COMPANION
-                       if (ao_companion_running)
-                               ao_send_companion();
+                               if (ao_companion_running)
+                                       ao_send_companion();
 #endif
-                       ao_send_configuration();
+                               ao_send_configuration();
 #if HAS_GPS
-                       ao_send_location();
-                       ao_send_satellite();
+                               ao_send_location();
+                               ao_send_satellite();
 #endif
+                       }
 #ifndef AO_SEND_ALL_BARO
                        if (ao_rdf &&
+#if HAS_APRS
+                           !(ao_config.radio_enable & AO_RADIO_DISABLE_RDF) &&
+#endif
                            (int16_t) (ao_time() - ao_rdf_time) >= 0)
                        {
 #if HAS_IGNITE_REPORT
@@ -325,6 +342,14 @@ ao_telemetry(void)
 #endif
                                        ao_radio_rdf();
                        }
+#if HAS_APRS
+                       if ((ao_config.radio_enable & AO_RADIO_ENABLE_APRS) &&
+                           (int16_t) (ao_time() - ao_aprs_time) >= 0)
+                       {
+                               ao_aprs_time = ao_time() + AO_APRS_INTERVAL_TICKS;
+                               ao_aprs_send();
+                       }
+#endif
 #endif
                        time += ao_telemetry_interval;
                        delay = time - ao_time();
@@ -389,8 +414,9 @@ ao_rdf_set(uint8_t rdf)
        ao_rdf = rdf;
        if (rdf == 0)
                ao_radio_rdf_abort();
-       else
+       else {
                ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
+       }
 }
 
 __xdata struct ao_task ao_telemetry_task;
diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c
new file mode 100644 (file)
index 0000000..e273908
--- /dev/null
@@ -0,0 +1,571 @@
+/** 
+ * http://ad7zj.net/kd7lmo/aprsbeacon_code.html
+ *
+ * @mainpage Pico Beacon
+ *
+ * @section overview_sec Overview
+ *
+ * The Pico Beacon is an APRS based tracking beacon that operates in the UHF 420-450MHz band.  The device utilizes a 
+ * Microchip PIC 18F2525 embedded controller, Motorola M12+ GPS engine, and Analog Devices AD9954 DDS.  The device is capable
+ * of generating a 1200bps A-FSK and 9600 bps FSK AX.25 compliant APRS (Automatic Position Reporting System) message.
+
+
+ *
+ * @section history_sec Revision History
+ *
+ * @subsection v305 V3.05
+ * 23 Dec 2006, Change include; (1) change printf format width to conform to ANSI standard when new CCS 4.xx compiler released.
+ *
+ *
+ * @subsection v304 V3.04
+ * 10 Jan 2006, Change include; (1) added amplitude control to engineering mode,
+ *                                     (2) corrected number of bytes reported in log,
+ *                                     (3) add engineering command to set high rate position reports (5 seconds), and
+ *                                     (4) corrected size of LOG_COORD block when searching for end of log.
+ *
+ * @subsection v303 V3.03
+ * 15 Sep 2005, Change include; (1) removed AD9954 setting SDIO as input pin, 
+ *                                     (2) additional comments and Doxygen tags,
+ *                                     (3) integration and test code calculates DDS FTW,
+ *                                     (4) swapped bus and reference analog input ports (hardware change),
+ *                                     (5) added message that indicates we are reading flash log and reports length,
+ *                                     (6) report bus voltage in 10mV steps, and
+ *                                     (7) change log type enumerated values to XORed nibbles for error detection.
+ *
+ *
+ * @subsection v302 V3.02
+ * 6 Apr 2005, Change include; (1) corrected tracked satellite count in NMEA-0183 $GPGGA message,
+ *                                    (2) Doxygen documentation clean up and additions, and
+ *                                    (3) added integration and test code to baseline.
+ *
+ * 
+ * @subsection v301 V3.01
+ * 13 Jan 2005, Renamed project and files to Pico Beacon.
+ *
+ *
+ * @subsection v300 V3.00
+ * 15 Nov 2004, Change include; (1) Micro Beacon extreme hardware changes including integral transmitter,
+ *                                     (2) PIC18F2525 processor,
+ *                                     (3) AD9954 DDS support functions,
+ *                                     (4) added comments and formatting for doxygen,
+ *                                     (5) process GPS data with native Motorola protocol,
+ *                                     (6) generate plain text $GPGGA and $GPRMC messages,
+ *                                     (7) power down GPS 5 hours after lock,
+ *                                     (8) added flight data recorder, and
+ *                                     (9) added diagnostics terminal mode.
+ *
+ * 
+ * @subsection v201 V2.01
+ * 30 Jan 2004, Change include; (1) General clean up of in-line documentation, and 
+ *                                     (2) changed temperature resolution to 0.1 degrees F.
+ *
+ * 
+ * @subsection v200 V2.00
+ * 26 Oct 2002, Change include; (1) Micro Beacon II hardware changes including PIC18F252 processor,
+ *                                     (2) serial EEPROM, 
+ *                                     (3) GPS power control, 
+ *                                     (4) additional ADC input, and 
+ *                                     (5) LM60 temperature sensor.                            
+ *
+ *
+ * @subsection v101 V1.01
+ * 5 Dec 2001, Change include; (1) Changed startup message, and 
+ *                                    (2) applied SEPARATE pragma to several methods for memory usage.
+ *
+ *
+ * @subsection v100 V1.00
+ * 25 Sep 2001, Initial release.  Flew ANSR-3 and ANSR-4.
+ * 
+
+
+ *
+ *
+ * @section copyright_sec Copyright
+ *
+ * Copyright (c) 2001-2009 Michael Gray, KD7LMO
+
+
+ *
+ *
+ * @section gpl_sec GNU General Public License
+ *
+ *  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
+ *  
+
+
+ * 
+ * 
+ * @section design Design Details
+ *
+ * Provides design details on a variety of the components that make up the Pico Beacon.
+ *
+ *  @subpage power
+ */
+
+/**
+ *  @page power Power Consumption
+ *
+ *  Measured DC power consumption.
+ * 
+ *  3VDC prime power current 
+
+ *
+ *    7mA Held in reset 
+
+ *   18mA Processor running, all I/O off 
+
+ *  110mA GPS running 
+
+ *  120mA GPS running w/antenna 
+
+ *  250mA DDS running and GPS w/antenna 
+
+ *  420mA DDS running, GPS w/antenna, and PA chain on with no RF 
+
+ *  900mA Transmit 
+
+ *
+ */
+
+#ifndef AO_APRS_TEST
+#include <ao.h>
+#endif
+
+#include <ao_aprs.h>
+
+// Public methods, constants, and data structures for each class.
+
+static void timeInit(void);
+
+static void tncInit(void);
+static void tnc1200TimerTick(void);
+
+/** @} */
+
+/**
+ *  @defgroup sys System Library Functions
+ *
+ *  Generic system functions similiar to the run-time C library.
+ *
+ *  @{
+ */
+
+/**
+ *    Calculate the CRC-16 CCITT of buffer that is length bytes long.
+ *    The crc parameter allow the calculation on the CRC on multiple buffers.
+ *
+ *    @param buffer Pointer to data buffer.
+ *    @param length number of bytes in data buffer
+ *    @param crc starting value
+ *
+ *    @return CRC-16 of buffer[0 .. length]
+ */
+static uint16_t sysCRC16(const uint8_t *buffer, uint8_t length, uint16_t crc)
+{
+    uint8_t i, bit, value;
+
+    for (i = 0; i < length; ++i) 
+    {
+        value = buffer[i];
+
+        for (bit = 0; bit < 8; ++bit) 
+        {
+            crc ^= (value & 0x01);
+            crc = ( crc & 0x01 ) ? ( crc >> 1 ) ^ 0x8408 : ( crc >> 1 );
+            value = value >> 1;
+        } // END for
+    } // END for
+
+    return crc ^ 0xffff;
+}
+
+/** @} */
+
+/**
+ *  @defgroup rtc Real Time Interrupt tick
+ *
+ *  Manage the built-in real time interrupt.  The interrupt clock PRI is 104uS (9600 bps).
+ *
+ *  @{
+ */
+
+/// 16-bit NCO where the upper 8-bits are used to index into the frequency generation table.
+static uint16_t timeNCO;
+
+/// Audio tone NCO update step (phase).
+static uint16_t timeNCOFreq;
+
+/**
+ *   Initialize the real-time clock.
+ */
+static void timeInit()
+{
+    timeNCO = 0x00;
+    timeNCOFreq = 0x2000;
+}
+
+/** @} */
+
+/**
+ *  @defgroup tnc TNC (Terminal Node Controller)
+ *
+ *  Functions that provide a subset of the TNC functions.
+ *
+ *  @{
+ */
+
+/// The number of start flag bytes to send before the packet message.  (360bits * 1200bps = 300mS)
+#define TNC_TX_DELAY 45
+
+/// The size of the TNC output buffer.
+#define TNC_BUFFER_SIZE 40
+
+/// States that define the current mode of the 1200 bps (A-FSK) state machine.
+typedef enum
+{
+    /// Stand by state ready to accept new message.
+    TNC_TX_READY,
+
+    /// 0x7E bit stream pattern used to define start of APRS message.
+    TNC_TX_SYNC,
+
+    /// Transmit the AX.25 header that contains the source/destination call signs, APRS path, and flags.
+    TNC_TX_HEADER,
+
+    /// Transmit the message data.
+    TNC_TX_DATA,
+
+    /// Transmit the end flag sequence.
+    TNC_TX_END
+} TNC_TX_1200BPS_STATE;
+
+/// AX.25 compliant packet header that contains destination, station call sign, and path.
+/// 0x76 for SSID-11, 0x78 for SSID-12
+static const uint8_t TNC_AX25_HEADER[] = { 
+    'A' << 1, 'P' << 1, 'A' << 1, 'M' << 1, ' ' << 1, ' ' << 1, 0x60, \
+    'K' << 1, 'D' << 1, '7' << 1, 'S' << 1, 'Q' << 1, 'G' << 1, 0x78, \
+    'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '2' << 1, ' ' << 1, 0x65, \
+    0x03, 0xf0 };
+
+/// The next bit to transmit.
+static uint8_t tncTxBit;
+
+/// Current mode of the 1200 bps state machine.
+static TNC_TX_1200BPS_STATE tncMode;
+
+/// Counter for each bit (0 - 7) that we are going to transmit.
+static uint8_t tncBitCount;
+
+/// A shift register that holds the data byte as we bit shift it for transmit.
+static uint8_t tncShift;
+
+/// Index into the APRS header and data array for each byte as we transmit it.
+static uint8_t tncIndex;
+
+/// The number of bytes in the message portion of the AX.25 message.
+static uint8_t tncLength;
+
+/// A copy of the last 5 bits we've transmitted to determine if we need to bit stuff on the next bit.
+static uint8_t tncBitStuff;
+
+/// Buffer to hold the message portion of the AX.25 packet as we prepare it.
+static uint8_t tncBuffer[TNC_BUFFER_SIZE];
+
+/** 
+ *   Initialize the TNC internal variables.
+ */
+static void tncInit()
+{
+    tncTxBit = 0;
+    tncMode = TNC_TX_READY;
+}
+
+/**
+ *   Method that is called every 833uS to transmit the 1200bps A-FSK data stream.
+ *   The provides the pre and postamble as well as the bit stuffed data stream.
+ */
+static void tnc1200TimerTick()
+{
+    // Set the A-FSK frequency.
+    if (tncTxBit == 0x00)
+        timeNCOFreq = 0x2000;
+    else
+        timeNCOFreq = 0x3aab;
+
+    switch (tncMode) 
+    {
+        case TNC_TX_READY:
+            // Generate a test signal alteranting between high and low tones.
+            tncTxBit = (tncTxBit == 0 ? 1 : 0);
+            break;
+
+        case TNC_TX_SYNC:
+            // The variable tncShift contains the lastest data byte.
+            // NRZI enocde the data stream.
+            if ((tncShift & 0x01) == 0x00) {
+                if (tncTxBit == 0)
+                    tncTxBit = 1;
+                else
+                    tncTxBit = 0;
+           }
+                    
+            // When the flag is done, determine if we need to send more or data.
+            if (++tncBitCount == 8) 
+            {
+                tncBitCount = 0;
+                tncShift = 0x7e;
+
+                // Once we transmit x mS of flags, send the data.
+                // txDelay bytes * 8 bits/byte * 833uS/bit = x mS
+                if (++tncIndex == TNC_TX_DELAY) 
+                {
+                    tncIndex = 0;
+                    tncShift = TNC_AX25_HEADER[0];
+                    tncBitStuff = 0;
+                    tncMode = TNC_TX_HEADER;
+                } // END if
+            } else
+                tncShift = tncShift >> 1;
+            break;
+
+        case TNC_TX_HEADER:
+            // Determine if we have sent 5 ones in a row, if we have send a zero.
+            if (tncBitStuff == 0x1f) 
+            {
+                if (tncTxBit == 0)
+                    tncTxBit = 1;
+                else
+                    tncTxBit = 0;
+
+                tncBitStuff = 0x00;
+                return;
+            }    // END if
+
+            // The variable tncShift contains the lastest data byte.
+            // NRZI enocde the data stream.
+            if ((tncShift & 0x01) == 0x00) {
+                if (tncTxBit == 0)
+                    tncTxBit = 1;
+                else
+                    tncTxBit = 0;
+           }
+
+            // Save the data stream so we can determine if bit stuffing is 
+            // required on the next bit time.
+            tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
+
+            // If all the bits were shifted, get the next byte.
+            if (++tncBitCount == 8) 
+            {
+                tncBitCount = 0;
+
+                // After the header is sent, then send the data.
+                if (++tncIndex == sizeof(TNC_AX25_HEADER)) 
+                {
+                    tncIndex = 0;
+                    tncShift = tncBuffer[0];
+                    tncMode = TNC_TX_DATA;
+                } else
+                    tncShift = TNC_AX25_HEADER[tncIndex];
+
+            } else
+                tncShift = tncShift >> 1;
+
+            break;
+
+        case TNC_TX_DATA:
+            // Determine if we have sent 5 ones in a row, if we have send a zero.
+            if (tncBitStuff == 0x1f) 
+            {
+                if (tncTxBit == 0)
+                    tncTxBit = 1;
+                else
+                    tncTxBit = 0;
+
+                tncBitStuff = 0x00;
+                return;
+            }    // END if
+
+            // The variable tncShift contains the lastest data byte.
+            // NRZI enocde the data stream.
+            if ((tncShift & 0x01) == 0x00) {
+                if (tncTxBit == 0)
+                    tncTxBit = 1;
+                else
+                    tncTxBit = 0;
+           }
+
+            // Save the data stream so we can determine if bit stuffing is 
+            // required on the next bit time.
+            tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
+
+            // If all the bits were shifted, get the next byte.
+            if (++tncBitCount == 8) 
+            {
+                tncBitCount = 0;
+
+                // If everything was sent, transmit closing flags.
+                if (++tncIndex == tncLength) 
+                {
+                    tncIndex = 0;
+                    tncShift = 0x7e;
+                    tncMode = TNC_TX_END;
+                } else
+                    tncShift = tncBuffer[tncIndex];
+
+            } else
+                tncShift = tncShift >> 1;
+
+            break;
+
+        case TNC_TX_END:
+            // The variable tncShift contains the lastest data byte.
+            // NRZI enocde the data stream. 
+            if ((tncShift & 0x01) == 0x00) {
+                if (tncTxBit == 0)
+                    tncTxBit = 1;
+                else
+                    tncTxBit = 0;
+           }
+
+            // If all the bits were shifted, get the next one.
+            if (++tncBitCount == 8) 
+            {
+                tncBitCount = 0;
+                tncShift = 0x7e;
+    
+                // Transmit two closing flags.
+                if (++tncIndex == 2) 
+                {
+                    tncMode = TNC_TX_READY;
+
+                    return;
+                } // END if
+            } else
+                tncShift = tncShift >> 1;
+
+            break;
+    } // END switch
+}
+
+/**
+ *   Generate the plain text position packet.
+ */
+static int tncPositionPacket(void)
+{
+    int32_t    latitude = ao_gps_data.latitude;
+    int32_t    longitude = ao_gps_data.longitude;
+    int32_t    altitude = ao_gps_data.altitude;
+
+    uint16_t   lat_deg;
+    uint16_t   lon_deg;
+    uint16_t   lat_min;
+    uint16_t   lat_frac;
+    uint16_t   lon_min;
+    uint16_t   lon_frac;
+
+    char       lat_sign = 'N', lon_sign = 'E';
+
+    if (latitude < 0) {
+       lat_sign = 'S';
+       latitude = -latitude;
+    }
+
+    if (longitude < 0) {
+       lon_sign = 'W';
+       longitude = -longitude;
+    }
+
+    lat_deg = latitude / 10000000;
+    latitude -= lat_deg * 10000000;
+    latitude *= 60;
+    lat_min = latitude / 10000000;
+    latitude -= lat_min * 10000000;
+    lat_frac = (latitude + 50000) / 100000;
+
+    lon_deg = longitude / 10000000;
+    longitude -= lon_deg * 10000000;
+    longitude *= 60;
+    lon_min = longitude / 10000000;
+    longitude -= lon_min * 10000000;
+    lon_frac = (longitude + 50000) / 100000;
+
+    if (altitude < 0)
+       altitude = 0;
+
+    altitude = altitude * (int32_t) 1000 / (int32_t) 3048;
+    
+    return sprintf ((char *) tncBuffer, "=%02u%02u.%02u%c\\%03u%02u.%02u%cO /A=%06u\015",
+                   lat_deg, lat_min, lat_frac, lat_sign,
+                   lon_deg, lon_min, lon_frac, lon_sign,
+                   altitude);
+}
+
+static int16_t
+tncFill(uint8_t *buf, int16_t len)
+{
+    int16_t    l = 0;
+    uint8_t    b;
+    uint8_t    bit;
+
+    while (tncMode != TNC_TX_READY && l < len) {
+       b = 0;
+       for (bit = 0; bit < 8; bit++) {
+           b = b << 1 | (timeNCO >> 15);
+           timeNCO += timeNCOFreq;
+       }
+       *buf++ = b;
+       l++;
+       tnc1200TimerTick();
+    }
+    if (tncMode == TNC_TX_READY)
+       l = -l;
+    return l;
+}
+
+/** 
+ *    Prepare an AX.25 data packet.  Each time this method is called, it automatically
+ *    rotates through 1 of 3 messages.
+ *
+ *    @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK
+ */
+void ao_aprs_send(void)
+{
+    uint16_t crc;
+
+    timeInit();
+    tncInit();
+
+    tncLength = tncPositionPacket();
+
+    // Calculate the CRC for the header and message.
+    crc = sysCRC16(TNC_AX25_HEADER, sizeof(TNC_AX25_HEADER), 0xffff);
+    crc = sysCRC16(tncBuffer, tncLength, crc ^ 0xffff);
+
+    // Save the CRC in the message.
+    tncBuffer[tncLength++] = crc & 0xff;
+    tncBuffer[tncLength++] = (crc >> 8) & 0xff;
+
+    // Prepare the variables that are used in the real-time clock interrupt.
+    tncBitCount = 0;
+    tncShift = 0x7e;
+    tncTxBit = 0;
+    tncIndex = 0;
+    tncMode = TNC_TX_SYNC;
+
+    ao_radio_send_lots(tncFill);
+}
+
+/** @} */
diff --git a/src/drivers/ao_aprs.h b/src/drivers/ao_aprs.h
new file mode 100644 (file)
index 0000000..e00dd75
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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_APRS_H_
+#define _AO_APRS_H_
+
+#define AO_APRS_INTERVAL_TICKS AO_SEC_TO_TICKS(2)
+
+void
+ao_aprs_send(void);
+
+#endif /* _AO_APRS_H_ */
index f27958f96ae397776248f17a64f1f687da780b0b..ed26e28d3fbea55b4a25c91767b8ab09793bc8a7 100644 (file)
@@ -323,12 +323,12 @@ static const uint16_t packet_rx_setup[] = {
 /*
  * For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone)
  *
- *              (2**20 - DATARATE_M) * 2 ** DATARATE_E
+ *              (2**20 + DATARATE_M) * 2 ** DATARATE_E
  *     Rdata = -------------------------------------- * fosc
  *                          2 ** 39
  *
- *     DATARATE_M = 511705
- *     DATARATE_E = 6
+ *     DATARATE_M = 25166
+ *     DATARATE_E = 5
  *
  * To make the tone last for 200ms, we need 2000 * .2 = 400 bits or 50 bytes
  */
@@ -358,7 +358,64 @@ static const uint16_t rdf_setup[] = {
                                 (0 << CC1120_PKT_CFG0_UART_SWAP_EN)),
 };
 
-static uint8_t ao_radio_mode;
+/*
+ * APRS deviation is 5kHz
+ *
+ *     fdev = fosc >> 24 * (256 + dev_m) << dev_e
+ *
+ *             32e6Hz / (2 ** 24) * (256 + 71) * (2 ** 3) = 4989
+ */
+
+#define APRS_DEV_E     3
+#define APRS_DEV_M     71
+#define APRS_PACKET_LEN        50
+
+/*
+ * For our APRS beacon, set the symbol rate to 9.6kBaud (8x oversampling for 1200 baud data rate)
+ *
+ *              (2**20 + DATARATE_M) * 2 ** DATARATE_E
+ *     Rdata = -------------------------------------- * fosc
+ *                          2 ** 39
+ *
+ *     DATARATE_M = 239914
+ *     DATARATE_E = 7
+ *
+ *     Rdata = 9599.998593330383301
+ *
+ */
+#define APRS_DRATE_E   5
+#define APRS_DRATE_M   25166
+
+static const uint16_t aprs_setup[] = {
+       CC1120_DEVIATION_M,     APRS_DEV_M,
+       CC1120_MODCFG_DEV_E,    ((CC1120_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1120_MODCFG_DEV_E_MODEM_MODE) |
+                                (CC1120_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1120_MODCFG_DEV_E_MOD_FORMAT) |
+                                (APRS_DEV_E << CC1120_MODCFG_DEV_E_DEV_E)),
+       CC1120_DRATE2,          ((APRS_DRATE_E << CC1120_DRATE2_DATARATE_E) |
+                                (((APRS_DRATE_M >> 16) & CC1120_DRATE2_DATARATE_M_19_16_MASK) << CC1120_DRATE2_DATARATE_M_19_16)),
+       CC1120_DRATE1,          ((APRS_DRATE_M >> 8) & 0xff),
+       CC1120_DRATE0,          ((APRS_DRATE_M >> 0) & 0xff),
+       CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
+                                (CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)),
+       CC1120_PKT_CFG1,        ((0 << CC1120_PKT_CFG1_WHITE_DATA) |
+                                (CC1120_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1120_PKT_CFG1_ADDR_CHECK_CFG) |
+                                (CC1120_PKT_CFG1_CRC_CFG_DISABLED << CC1120_PKT_CFG1_CRC_CFG) |
+                                (0 << CC1120_PKT_CFG1_APPEND_STATUS)),
+};
+
+#define AO_PKT_CFG0_INFINITE ((0 << CC1120_PKT_CFG0_RESERVED7) |       \
+                             (CC1120_PKT_CFG0_LENGTH_CONFIG_INFINITE << CC1120_PKT_CFG0_LENGTH_CONFIG) | \
+                             (0 << CC1120_PKT_CFG0_PKG_BIT_LEN) |      \
+                             (0 << CC1120_PKT_CFG0_UART_MODE_EN) |     \
+                             (0 << CC1120_PKT_CFG0_UART_SWAP_EN))
+
+#define AO_PKT_CFG0_FIXED ((0 << CC1120_PKT_CFG0_RESERVED7) |          \
+                          (CC1120_PKT_CFG0_LENGTH_CONFIG_FIXED << CC1120_PKT_CFG0_LENGTH_CONFIG) | \
+                          (0 << CC1120_PKT_CFG0_PKG_BIT_LEN) |         \
+                          (0 << CC1120_PKT_CFG0_UART_MODE_EN) |        \
+                          (0 << CC1120_PKT_CFG0_UART_SWAP_EN))
+
+static uint16_t ao_radio_mode;
 
 #define AO_RADIO_MODE_BITS_PACKET      1
 #define AO_RADIO_MODE_BITS_PACKET_TX   2
@@ -366,17 +423,22 @@ static uint8_t ao_radio_mode;
 #define AO_RADIO_MODE_BITS_TX_FINISH   8
 #define AO_RADIO_MODE_BITS_PACKET_RX   16
 #define AO_RADIO_MODE_BITS_RDF         32
+#define AO_RADIO_MODE_BITS_APRS                64
+#define AO_RADIO_MODE_BITS_INFINITE    128
+#define AO_RADIO_MODE_BITS_FIXED       256
 
 #define AO_RADIO_MODE_NONE             0
 #define AO_RADIO_MODE_PACKET_TX_BUF    (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_BUF)
 #define AO_RADIO_MODE_PACKET_TX_FINISH (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_FINISH)
 #define AO_RADIO_MODE_PACKET_RX                (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_RX)
 #define AO_RADIO_MODE_RDF              (AO_RADIO_MODE_BITS_RDF | AO_RADIO_MODE_BITS_TX_FINISH)
+#define AO_RADIO_MODE_APRS_BUF         (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_INFINITE)
+#define AO_RADIO_MODE_APRS_FINISH      (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED)
 
 static void
-ao_radio_set_mode(uint8_t new_mode)
+ao_radio_set_mode(uint16_t new_mode)
 {
-       uint8_t changes;
+       uint16_t changes;
        int i;
 
        if (new_mode == ao_radio_mode)
@@ -404,6 +466,17 @@ ao_radio_set_mode(uint8_t new_mode)
        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(CC1120_PKT_CFG0, AO_PKT_CFG0_INFINITE);
+
+       if (changes & AO_RADIO_MODE_BITS_FIXED)
+               ao_radio_reg_write(CC1120_PKT_CFG0, AO_PKT_CFG0_FIXED);
+
        ao_radio_mode = new_mode;
 }
 
@@ -430,11 +503,21 @@ ao_radio_setup(void)
        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(CC1120_PKT_LEN, len);
+               last_len = len;
+       }
+}
+
 static void
 ao_radio_get(uint8_t len)
 {
        static uint32_t last_radio_setting;
-       static uint8_t  last_len;
 
        ao_mutex_get(&ao_radio_mutex);
        if (!ao_radio_configured)
@@ -445,10 +528,7 @@ ao_radio_get(uint8_t len)
                ao_radio_reg_write(CC1120_FREQ0, ao_config.radio_setting);
                last_radio_setting = ao_config.radio_setting;
        }
-       if (len != last_len) {
-               ao_radio_reg_write(CC1120_PKT_LEN, len);
-               last_len = len;
-       }
+       ao_radio_set_len(len);
 }
 
 #define ao_radio_put() ao_mutex_put(&ao_radio_mutex)
@@ -562,6 +642,24 @@ ao_radio_test_cmd(void)
        }
 }
 
+static uint8_t
+ao_radio_wait_tx(uint8_t wait_fifo)
+{
+       uint8_t fifo_space = 0;
+
+       do {
+               ao_radio_wake = 0;
+               ao_arch_block_interrupts();
+               while (!ao_radio_wake)
+                       ao_sleep(&ao_radio_wake);
+               ao_arch_release_interrupts();
+               if (!wait_fifo)
+                       return 0;
+               fifo_space = ao_radio_tx_fifo_space();
+       } while (!fifo_space);
+       return fifo_space;
+}
+
 static uint8_t tx_data[(AO_RADIO_MAX_SEND + 4) * 2];
 
 void
@@ -601,16 +699,51 @@ ao_radio_send(const void *d, uint8_t size)
                        ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
                }
                        
-               do {
-                       ao_radio_wake = 0;
-                       ao_arch_block_interrupts();
-                       while (!ao_radio_wake)
-                               ao_sleep(&ao_radio_wake);
-                       ao_arch_release_interrupts();
-                       if (!encode_len)
-                               break;
-                       fifo_space = ao_radio_tx_fifo_space();
-               } while (!fifo_space);
+               fifo_space = ao_radio_wait_tx(encode_len != 0);
+       }
+       ao_radio_put();
+}
+
+#define AO_RADIO_LOTS  64
+
+void
+ao_radio_send_lots(ao_radio_fill_func fill)
+{
+       uint8_t buf[AO_RADIO_LOTS], *b;
+       int     cnt;
+       int     total = 0;
+       uint8_t done = 0;
+       uint8_t started = 0;
+       uint8_t fifo_space;
+
+       ao_radio_get(0xff);
+       fifo_space = CC1120_FIFO_SIZE;
+       while (!done) {
+               cnt = (*fill)(buf, sizeof(buf));
+               if (cnt < 0) {
+                       done = 1;
+                       cnt = -cnt;
+               }
+               total += cnt;
+               if (done) {
+                       ao_radio_set_len(total & 0xff);
+                       ao_radio_set_mode(AO_RADIO_MODE_APRS_FINISH);
+               } else
+                       ao_radio_set_mode(AO_RADIO_MODE_APRS_BUF);
+               b = buf;
+               while (cnt) {
+                       uint8_t this_len = cnt;
+                       if (this_len > fifo_space)
+                               this_len = fifo_space;
+                       ao_radio_fifo_write(b, this_len);
+                       b += this_len;
+                       cnt -= this_len;
+                       if (!started) {
+                               ao_radio_start_tx();
+                               started = 1;
+                       }
+                       fifo_space = ao_radio_wait_tx(!done || cnt);
+               }
        }
        ao_radio_put();
 }
index 7d6c7388fa14f8328be8840be9b891ab03fa2323..25d0ed0399c44071648838c83647d2ee0c306dc3 100644 (file)
@@ -90,6 +90,7 @@ ALTOS_SRC = \
        ao_packet.c \
        ao_companion.c \
        ao_pyro.c \
+       ao_aprs.c \
        $(PROFILE) \
        $(SAMPLE_PROFILE) \
        $(STACK_GUARD)
index f07dc26ed975fc11b0d5324720bf14cf471d1f29..083c1b6fea390f8c58e2e34746796f2fd0a443ed 100644 (file)
@@ -70,6 +70,7 @@
 #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 */
index 0dcdc949318e54b0b50a3879815339efbb69e4cc..092bf3603b7eaf2e2aa4ba388966089a99f3938a 100644 (file)
@@ -1,7 +1,8 @@
 vpath % ..:../core:../drivers:../util
 
 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_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test ao_fec_test \
+       ao_aprs_test
 
 INCS=ao_kalman.h ao_ms5607.h ao_log.h ao_data.h altitude-pa.h altitude.h
 
@@ -9,7 +10,7 @@ KALMAN=make-kalman
 
 CFLAGS=-I.. -I. -I../core -I../drivers -O0 -g -Wall
 
-all: $(PROGS)
+all: $(PROGS) ao_aprs_data.wav
 
 clean:
        rm -f $(PROGS) run-out.baro run-out.full
@@ -49,5 +50,14 @@ ao_kalman.h: $(KALMAN)
 ao_fec_test: ao_fec_test.c ao_fec_tx.c ao_fec_rx.c
        cc $(CFLAGS) -DAO_FEC_DEBUG=1 -o $@ ao_fec_test.c ../core/ao_fec_tx.c ../core/ao_fec_rx.c -lm
 
+ao_aprs_test: ao_aprs_test.c ao_aprs.c
+       cc $(CFLAGS) -o $@ ao_aprs_test.c
+
+SOX_INPUT_ARGS=--type raw --encoding unsigned-integer -b 8 -c 1 -r 9600
+SOX_OUTPUT_ARGS=--type wav
+
+ao_aprs_data.wav: ao_aprs_test
+       ./ao_aprs_test | sox $(SOX_INPUT_ARGS) - $(SOX_OUTPUT_ARGS) $@
+
 check: ao_fec_test ao_flight_test ao_flight_test_baro run-tests
        ./ao_fec_test && ./run-tests
\ No newline at end of file
diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c
new file mode 100644 (file)
index 0000000..f16c94e
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#include <ao_telemetry.h>
+
+struct ao_telemetry_location ao_gps_data;
+
+#define AO_APRS_TEST
+
+typedef int16_t (*ao_radio_fill_func)(uint8_t *buffer, int16_t len);
+
+#define DEBUG 0
+#if DEBUG
+void
+ao_aprs_bit(uint8_t bit)
+{
+       static int      seq = 0;
+       printf ("%6d %d\n", seq++, bit ? 1 : 0);
+}
+#else
+void
+ao_aprs_bit(uint8_t bit)
+{
+       putchar (bit ? 0xc0 : 0x40);
+}
+#endif
+
+void
+ao_radio_send_lots(ao_radio_fill_func fill);
+
+#include <ao_aprs.c>
+
+/*
+ * @section copyright_sec Copyright
+ *
+ * Copyright (c) 2001-2009 Michael Gray, KD7LMO
+
+
+ *
+ *
+ * @section gpl_sec GNU General Public License
+ *
+ *  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
+ *  
+
+ */
+
+static void
+audio_gap(int secs)
+{
+#if !DEBUG
+       int     samples = secs * 9600;
+
+       while (samples--)
+               ao_aprs_bit(0);
+#endif
+}
+
+// This is where we go after reset.
+int main(int argc, char **argv)
+{
+    audio_gap(1);
+
+    ao_gps_data.latitude = 45.4694766 * 10000000;
+    ao_gps_data.longitude = -122.7376250 * 10000000;
+    ao_gps_data.altitude = 83;
+
+    /* Transmit one packet */
+    ao_aprs_send();
+
+    exit(0);
+}
+
+void
+ao_radio_send_lots(ao_radio_fill_func fill)
+{
+       int16_t len;
+       uint8_t done = 0;
+       uint8_t buf[16], *b, c;
+       uint8_t bit;
+
+       while (!done) {
+               len = (*fill)(buf, sizeof (buf));
+               if (len < 0) {
+                       done = 1;
+                       len = -len;
+               }
+               b = buf;
+               while (len--) {
+                       c = *b++;
+                       for (bit = 0; bit < 8; bit++) {
+                               ao_aprs_bit(c & 0x80);
+                               c <<= 1;
+                       }
+               }
+       }
+}