From 0c2c47dd7af2fc95de852178c4244daba02f44ed Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 19:44:09 -0800 Subject: altos: Add test scaffolding for APRS This moves some test code out of ao_aprs.c and into ao_aprs_test.c, and then adds Makefile fragments to compile and run the resulting program, creating a wav file as output Signed-off-by: Keith Packard --- src/test/Makefile | 14 ++++- src/test/ao_aprs_test.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 src/test/ao_aprs_test.c (limited to 'src/test') diff --git a/src/test/Makefile b/src/test/Makefile index 0dcdc949..092bf360 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -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 index 00000000..d791e930 --- /dev/null +++ b/src/test/ao_aprs_test.c @@ -0,0 +1,145 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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 +#include +#include +#include +#include + +#include + +#define AO_APRS_TEST + +#include + +/* + * @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 + * + + */ + +// This is where we go after reset. +int main(int argc, char **argv) +{ + uint8_t utcSeconds, lockLostCounter; + +//test(); + + // Configure the basic systems. +// sysInit(); + + // Wait for the power converter chains to stabilize. +// delay_ms (100); + + // Setup the subsystems. +// adcInit(); +// flashInit(); + gpsInit(); +// logInit(); +// timeInit(); +// serialInit(); + tncInit(); + + // Program the DDS. +// ddsInit(); + + // Transmit software version packet on start up. + tncTxPacket(TNC_MODE_1200_AFSK); + + exit(0); + // Counters to send packets if the GPS time stamp is not available. + lockLostCounter = 5; + utcSeconds = 55; + + // This is the main loop that process GPS data and waits for the once per second timer tick. + for (;;) + { + // Read the GPS engine serial port FIFO and process the GPS data. +// gpsUpdate(); + + if (gpsIsReady()) + { + // Start the flight timer when we get a valid 3D fix. + if (gpsGetFixType() == GPS_3D_FIX) + timeSetRunFlag(); + + // Generate our packets based on the GPS time. + if (tncIsTimeSlot(gpsPosition.seconds)) + tncTxPacket(TNC_MODE_1200_AFSK); + + // Sync the internal clock to GPS UTC time. + utcSeconds = gpsPosition.seconds; + + // This counter is reset every time we receive the GPS message. + lockLostCounter = 0; + + // Log the data to flash. +// sysLogGPSData(); + } // END if gpsIsReady + + // Processing that occurs once a second. + if (timeIsUpdate()) + { + // We maintain the UTC time in seconds if we shut off the GPS engine or it fails. + if (++utcSeconds == 60) + utcSeconds = 0; + + // If we loose information for more than 5 seconds, + // we will determine when to send a packet based on internal time. + if (lockLostCounter == 5) + { + if (tncIsTimeSlot(utcSeconds)) + tncTxPacket(TNC_MODE_1200_AFSK); + } else + ++lockLostCounter; + + // Update the ADC filters. +// adcUpdate(); + + if (timeHours == 5 && timeMinutes == 0 && timeSeconds == 0) + gpsPowerOff(); + + } // END if timeIsUpdate + + } // END for +} + + + + -- cgit v1.2.3 From 8b1f186a574c22cebd9daba9d352ec82556c3b28 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 20:10:54 -0800 Subject: altos: Generate all of the APRS messages Note that two of them are in NMEA form, which some receivers appear not to parse Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 8 ++++++-- src/test/ao_aprs_test.c | 35 ++++++++++++++--------------------- 2 files changed, 20 insertions(+), 23 deletions(-) (limited to 'src/test') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index df68278c..0a41d5fd 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -395,7 +395,7 @@ const uint32_t freqTable[256] = void ddsSetFTW (uint32_t ftw) { int x = ftw - freqTable[0]; - putchar (x > 0 ? 0xff : 0x0); + putchar (x > 0 ? 0xc0 : 0x40); } /** @@ -1243,6 +1243,10 @@ tncPrintf(char *fmt, ...) va_start(ap, fmt); c = vsprintf((char *) tncBufferPnt, fmt, ap); + if (*fmt == '\015') + fprintf (stderr, "\n"); + else + vfprintf(stderr, fmt, ap); va_end(ap); tncBufferPnt += c; tncLength += c; @@ -1378,7 +1382,7 @@ void tncStatusPacket(int16_t temperature) tncPrintf (">ANSR "); // Display the flight time. - tncPrintf ("%02U:%02U:%02U ", timeHours, timeMinutes, timeSeconds); + tncPrintf ("%02u:%02u:%02u ", timeHours, timeMinutes, timeSeconds); // Altitude in feet. tncPrintf ("%ld' ", gpsPosition.altitudeFeet); diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index d791e930..1c0b252b 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -54,42 +54,34 @@ */ -// This is where we go after reset. -int main(int argc, char **argv) +static void +audio_gap(int secs) { - uint8_t utcSeconds, lockLostCounter; - -//test(); + int samples = secs * 9600; - // Configure the basic systems. -// sysInit(); - - // Wait for the power converter chains to stabilize. -// delay_ms (100); + while (samples--) + putchar(0x7f); +} - // Setup the subsystems. -// adcInit(); -// flashInit(); +// This is where we go after reset. +int main(int argc, char **argv) +{ + uint8_t utcSeconds, lockLostCounter, i; gpsInit(); -// logInit(); -// timeInit(); -// serialInit(); tncInit(); - // Program the DDS. -// ddsInit(); - + audio_gap(1); // Transmit software version packet on start up. tncTxPacket(TNC_MODE_1200_AFSK); - exit(0); // Counters to send packets if the GPS time stamp is not available. lockLostCounter = 5; utcSeconds = 55; // This is the main loop that process GPS data and waits for the once per second timer tick. - for (;;) + for (i = 0; i < 5; i++) { + audio_gap(10); // Read the GPS engine serial port FIFO and process the GPS data. // gpsUpdate(); @@ -138,6 +130,7 @@ int main(int argc, char **argv) } // END if timeIsUpdate } // END for + return 0; } -- cgit v1.2.3 From fe820a8a2dc6248b5edb96a9521536d41b936116 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 21:01:59 -0800 Subject: Signed-off-by: Keith Packard altos: Switch APRS to standard position reporting form Stop using NMEA sentences for position --- src/drivers/ao_aprs.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++- src/test/ao_aprs_test.c | 2 +- 2 files changed, 55 insertions(+), 2 deletions(-) (limited to 'src/test') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 0a41d5fd..cea802bb 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -1370,6 +1370,58 @@ void tncGPRMCPacket() tncPrintf ("*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1)); } +/** + * Generate the plain text position packet. Data is written through the tncTxByte + * callback function + */ +void tncPositionPacket(void) +{ + int32_t latitude = 45.4694766 * 10000000; + int32_t longitude = -122.7376250 * 10000000; + uint32_t altitude = 10000; + 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'; + +// tncPrintf (">ANSR "); + 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; + + tncPrintf ("=%02u%02u.%02u%c\\%03u%02u.%02u%cO", + lat_deg, lat_min, lat_frac, lat_sign, + lon_deg, lon_min, lon_frac, lon_sign); + + tncPrintf (" /A=%06u", altitude * 100 / 3048); +} + + + /** * Generate the plain text status packet. Data is written through the tncTxByte * callback function. @@ -1466,7 +1518,8 @@ void tncTxPacket(TNC_DATA_MODE dataMode) break; case TNC_GGA: - tncGPGGAPacket(); + tncPositionPacket(); +// tncGPGGAPacket(); // Select the next packet we will generate. tncPacketType = TNC_RMC; diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index 1c0b252b..cec4d617 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -79,7 +79,7 @@ int main(int argc, char **argv) utcSeconds = 55; // This is the main loop that process GPS data and waits for the once per second timer tick. - for (i = 0; i < 5; i++) + for (i = 0; i < 3; i++) { audio_gap(10); // Read the GPS engine serial port FIFO and process the GPS data. -- cgit v1.2.3 From 3e1254c4f3261f66d8070250898fe906eb80d8f2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 21:08:19 -0800 Subject: altos: Strip out everything but the basic position reporting from APRS Any useful data will be sent over the digital link; APRS is strictly for position tracking Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 208 +----------------------------------------------- src/test/ao_aprs_test.c | 62 +-------------- 2 files changed, 4 insertions(+), 266 deletions(-) (limited to 'src/test') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index cea802bb..3a51ae90 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -1252,124 +1252,6 @@ tncPrintf(char *fmt, ...) tncLength += c; } -/** - * Generate the GPS NMEA standard UTC time stamp. Data is written through the tncTxByte - * callback function. - */ -void tncNMEATime() -{ - // UTC of position fix. - tncPrintf ("%02d%02d%02d,", gpsPosition.hours, gpsPosition.minutes, gpsPosition.seconds); -} - -/** - * Generate the GPS NMEA standard latitude/longitude fix. Data is written through the tncTxByte - * callback function. - */ -void tncNMEAFix() -{ - uint8_t dirChar; - uint32_t coord, coordMin; - - // Latitude value. - coord = gpsPosition.latitude; - - if (gpsPosition.latitude < 0) - { - coord = gpsPosition.latitude * -1; - dirChar = 'S'; - } else { - coord = gpsPosition.latitude; - dirChar = 'N'; - } - - coordMin = (coord % 3600000) / 6; - tncPrintf ("%02ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar); - - - // Longitude value. - if (gpsPosition.longitude < 0) - { - coord = gpsPosition.longitude * - 1; - dirChar = 'W'; - } else { - coord = gpsPosition.longitude; - dirChar = 'E'; - } - - coordMin = (coord % 3600000) / 6; - tncPrintf ("%03ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar); - -} - -/** - * Generate the GPS NMEA-0183 $GPGGA packet. Data is written through the tncTxByte - * callback function. - */ -void tncGPGGAPacket() -{ - // Generate the GPGGA message. - tncPrintf ("$GPGGA,"); - - // Standard NMEA time. - tncNMEATime(); - - // Standard NMEA-0183 latitude/longitude. - tncNMEAFix(); - - // GPS status where 0: not available, 1: available - if (gpsGetFixType() != GPS_NO_FIX) - tncPrintf ("1,"); - else - tncPrintf ("0,"); - - // Number of visible birds. - tncPrintf ("%02d,", gpsPosition.trackedSats); - - // DOP - tncPrintf ("%ld.%01ld,", gpsPosition.dop / 10, gpsPosition.dop % 10); - - // Altitude in meters. - tncPrintf ("%ld.%02ld,M,,M,,", (int32_t) (gpsPosition.altitudeCM / 100l), (int32_t) (gpsPosition.altitudeCM % 100)); - - // Checksum, we add 1 to skip over the $ character. - tncPrintf ("*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1)); -} - -/** - * Generate the GPS NMEA-0183 $GPRMC packet. Data is written through the tncTxByte - * callback function. - */ -void tncGPRMCPacket() -{ - uint32_t temp; - - // Generate the GPRMC message. - tncPrintf ("$GPRMC,"); - - // Standard NMEA time. - tncNMEATime(); - - // GPS status. - if (gpsGetFixType() != GPS_NO_FIX) - tncPrintf ("A,"); - else - tncPrintf ("V,"); - - // Standard NMEA-0183 latitude/longitude. - tncNMEAFix(); - - // Speed knots and heading. - temp = (int32_t) gpsPosition.hSpeed * 75000 / 385826; - tncPrintf ("%ld.%ld,%ld.%ld,", (int16_t) (temp / 10), (int16_t) (temp % 10), gpsPosition.heading / 10, gpsPosition.heading % 10); - - // Date - tncPrintf ("%02d%02d%02ld,,", gpsPosition.day, gpsPosition.month, gpsPosition.year % 100); - - // Checksum, skip over the $ character. - tncPrintf ("*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1)); -} - /** * Generate the plain text position packet. Data is written through the tncTxByte * callback function @@ -1420,62 +1302,6 @@ void tncPositionPacket(void) tncPrintf (" /A=%06u", altitude * 100 / 3048); } - - -/** - * Generate the plain text status packet. Data is written through the tncTxByte - * callback function. - */ -void tncStatusPacket(int16_t temperature) -{ -// uint16_t voltage; - - // Plain text telemetry. - tncPrintf (">ANSR "); - - // Display the flight time. - tncPrintf ("%02u:%02u:%02u ", timeHours, timeMinutes, timeSeconds); - - // Altitude in feet. - tncPrintf ("%ld' ", gpsPosition.altitudeFeet); - - // Peak altitude in feet. - tncPrintf ("%ld'pk ", gpsGetPeakAltitude()); - - // GPS hdop or pdop - tncPrintf ("%lu.%lu", gpsPosition.dop / 10, gpsPosition.dop % 10); - - // The text 'pdop' for a 3D fix, 'hdop' for a 2D fix, and 'dop' for no fix. - switch (gpsGetFixType()) - { - case GPS_NO_FIX: - tncPrintf ("dop "); - break; - - case GPS_2D_FIX: - tncPrintf ("hdop "); - break; - - - case GPS_3D_FIX: - tncPrintf ("pdop "); - break; - } // END switch - - // Number of satellites in the solution. - tncPrintf ("%utrk ", gpsPosition.trackedSats); - - // Display main bus voltage. -// voltage = adcGetMainBusVolt(); -// tncPrintf ("%lu.%02luvdc ", voltage / 100, voltage % 100); - - // Display internal temperature. -// tncPrintf ("%ld.%01ldF ", temperature / 10, abs(temperature % 10)); - - // Print web address link. - tncPrintf ("www.altusmetrum.org"); -} - /** * Prepare an AX.25 data packet. Each time this method is called, it automatically * rotates through 1 of 3 messages. @@ -1484,7 +1310,6 @@ void tncStatusPacket(int16_t temperature) */ void tncTxPacket(TNC_DATA_MODE dataMode) { - int16_t temperature = 20; uint16_t crc; // Only transmit if there is not another message in progress. @@ -1500,38 +1325,7 @@ void tncTxPacket(TNC_DATA_MODE dataMode) // Set the message length counter. tncLength = 0; - // Determine the contents of the packet. - switch (tncPacketType) - { - case TNC_BOOT_MESSAGE: - tncPrintf (">MegaMetrum v1.0 Beacon"); - - // Select the next packet we will generate. - tncPacketType = TNC_STATUS; - break; - - case TNC_STATUS: - tncStatusPacket(temperature); - - // Select the next packet we will generate. - tncPacketType = TNC_GGA; - break; - - case TNC_GGA: - tncPositionPacket(); -// tncGPGGAPacket(); - - // Select the next packet we will generate. - tncPacketType = TNC_RMC; - break; - - case TNC_RMC: - tncGPRMCPacket(); - - // Select the next packet we will generate. - tncPacketType = TNC_STATUS; - break; - } + tncPositionPacket(); // Add the end of message character. tncPrintf ("\015"); diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index cec4d617..947a02b4 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -66,71 +66,15 @@ audio_gap(int secs) // This is where we go after reset. int main(int argc, char **argv) { - uint8_t utcSeconds, lockLostCounter, i; gpsInit(); tncInit(); audio_gap(1); - // Transmit software version packet on start up. + + /* Transmit one packet */ tncTxPacket(TNC_MODE_1200_AFSK); - // Counters to send packets if the GPS time stamp is not available. - lockLostCounter = 5; - utcSeconds = 55; - - // This is the main loop that process GPS data and waits for the once per second timer tick. - for (i = 0; i < 3; i++) - { - audio_gap(10); - // Read the GPS engine serial port FIFO and process the GPS data. -// gpsUpdate(); - - if (gpsIsReady()) - { - // Start the flight timer when we get a valid 3D fix. - if (gpsGetFixType() == GPS_3D_FIX) - timeSetRunFlag(); - - // Generate our packets based on the GPS time. - if (tncIsTimeSlot(gpsPosition.seconds)) - tncTxPacket(TNC_MODE_1200_AFSK); - - // Sync the internal clock to GPS UTC time. - utcSeconds = gpsPosition.seconds; - - // This counter is reset every time we receive the GPS message. - lockLostCounter = 0; - - // Log the data to flash. -// sysLogGPSData(); - } // END if gpsIsReady - - // Processing that occurs once a second. - if (timeIsUpdate()) - { - // We maintain the UTC time in seconds if we shut off the GPS engine or it fails. - if (++utcSeconds == 60) - utcSeconds = 0; - - // If we loose information for more than 5 seconds, - // we will determine when to send a packet based on internal time. - if (lockLostCounter == 5) - { - if (tncIsTimeSlot(utcSeconds)) - tncTxPacket(TNC_MODE_1200_AFSK); - } else - ++lockLostCounter; - - // Update the ADC filters. -// adcUpdate(); - - if (timeHours == 5 && timeMinutes == 0 && timeSeconds == 0) - gpsPowerOff(); - - } // END if timeIsUpdate - - } // END for - return 0; + exit(0); } -- cgit v1.2.3 From 0bb7200f85db1bc6e39e72e671be9a7aef9c8f09 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 21:22:55 -0800 Subject: altos: Remove more unused APRS code Getting down to a reasonable amount of code. Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 545 ++---------------------------------------------- src/test/ao_aprs_test.c | 3 +- 2 files changed, 17 insertions(+), 531 deletions(-) (limited to 'src/test') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index b45ef8c5..c1a800a9 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -152,110 +152,12 @@ typedef int32_t int32; // Public methods, constants, and data structures for each class. -/// Operational modes of the AD9954 DDS for the ddsSetMode function. -typedef enum -{ - /// Device has not been initialized. - DDS_MODE_NOT_INITIALIZED, - - /// Device in lowest power down mode. - DDS_MODE_POWERDOWN, - - /// Generate FM modulated audio tones. - DDS_MODE_AFSK, - - /// Generate true FSK tones. - DDS_MODE_FSK -} DDS_MODE; - void ddsInit(); void ddsSetAmplitude (uint8_t amplitude); void ddsSetOutputScale (uint16_t amplitude); void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1); void ddsSetFreq (uint32_t freq); void ddsSetFTW (uint32_t ftw); -void ddsSetMode (DDS_MODE mode); - -/// Type of GPS fix. -typedef enum -{ - /// No GPS FIX - GPS_NO_FIX, - - /// 2D (Latitude/Longitude) fix. - GPS_2D_FIX, - - /// 3D (Latitude/Longitude/Altitude) fix. - GPS_3D_FIX -} GPS_FIX_TYPE; - -/// GPS Position information. -typedef struct -{ - /// Flag that indicates the position information has been updated since it was last checked. - bool_t updateFlag; - - /// Month in UTC time. - uint8_t month; - - /// Day of month in UTC time. - uint8_t day; - - /// Hours in UTC time. - uint8_t hours; - - /// Minutes in UTC time. - uint8_t minutes; - - /// Seconds in UTC time. - uint8_t seconds; - - /// Year in UTC time. - uint16_t year; - - /// Latitude in milli arc-seconds where + is North, - is South. - int32_t latitude; - - /// Longitude in milli arc-seconds where + is East, - is West. - int32_t longitude; - - /// Altitude in cm - int32_t altitudeCM; - - /// Calculated altitude in feet - int32_t altitudeFeet; - - /// 3D speed in cm/second. - uint16_t vSpeed; - - /// 2D speed in cm/second. - uint16_t hSpeed; - - /// Heading units of 0.1 degrees. - uint16_t heading; - - /// DOP (Dilution of Precision) - uint16_t dop; - - /// 16-bit number that represents status of GPS engine. - uint16_t status; - - /// Number of tracked satellites used in the fix position. - uint8_t trackedSats; - - /// Number of visible satellites. - uint8_t visibleSats; -} GPSPOSITION_STRUCT; - -GPSPOSITION_STRUCT gpsPosition; - -void gpsInit(); -bool_t gpsIsReady(); -GPS_FIX_TYPE gpsGetFixType(); -int32_t gpsGetPeakAltitude(); -void gpsPowerOn(); -bool_t gpsSetup(); -void gpsUpdate(); uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc); @@ -264,27 +166,12 @@ void timeInit(); void timeSetDutyCycle (uint8_t dutyCycle); void timeUpdate(); -/// Operational modes of the TNC for the tncSetMode function. -typedef enum -{ - /// No operation waiting for setup and configuration. - TNC_MODE_STANDBY, - - /// 1200 bps using A-FSK (Audio FSK) tones. - TNC_MODE_1200_AFSK, - - /// 9600 bps using true FSK tones. - TNC_MODE_9600_FSK -} TNC_DATA_MODE; - void tncInit(); bool_t tncIsFree(); void tncHighRate(bool_t state); -void tncSetMode (TNC_DATA_MODE dataMode); void tnc1200TimerTick(); -void tnc9600TimerTick(); void tncTxByte (uint8_t value); -void tncTxPacket(TNC_DATA_MODE dataMode); +void tncTxPacket(void); /** @} */ @@ -296,42 +183,6 @@ void tncTxPacket(TNC_DATA_MODE dataMode); * @{ */ -/// AD9954 CFR1 - Control functions including RAM, profiles, OSK, sync, sweep, SPI, and power control settings. -#define DDS_AD9954_CFR1 0x00 - -/// AD9954 CFR2 - Control functions including sync, PLL multiplier, VCO range, and charge pump current. -#define DDS_AD9954_CFR2 0x01 - -/// AD9954 ASF - Auto ramp rate speed control and output scale factor (0x0000 to 0x3fff). -#define DDS_AD9954_ASF 0x02 - -/// AD9954 ARR - Amplitude ramp rate for OSK function. -#define DDS_AD9954_ARR 0x03 - -/// AD9954 FTW0 - Frequency tuning word 0. -#define DDS_AD9954_FTW0 0x04 - -/// AD9954 FTW1 - Frequency tuning word 1 -#define DDS_AD9954_FTW1 0x06 - -/// AD9954 NLSCW - Negative Linear Sweep Control Word used for spectral shaping in FSK mode -#define DDS_AD9954_NLSCW 0x07 - -/// AD9954 PLSCW - Positive Linear Sweep Control Word used for spectral shaping in FSK mode -#define DDS_AD9954_PLSCW 0x08 - -/// AD9954 RSCW0 - RAM Segment Control Word 0 -#define DDS_AD9954_RWCW0 0x07 - -/// AD9954 RSCW0 - RAM Segment Control Word 1 -#define DDS_AD9954_RWCW1 0x08 - -/// AD9954 RAM segment -#define DDS_RAM 0x0b - -/// Current operational mode. -DDS_MODE ddsMode; - /// Number of digits in DDS frequency to FTW conversion. #define DDS_FREQ_TO_FTW_DIGITS 9 @@ -398,287 +249,8 @@ void ddsSetFTW (uint32_t ftw) putchar (x > 0 ? 0xc0 : 0x40); } -/** - * Convert frequency in hertz to 32-bit DDS FTW (Frequency Tune Word). - * - * @param freq frequency in Hertz - * - */ -void ddsSetFreq(uint32_t freq) -{ - uint8_t i; - uint32_t ftw; - - // To avoid rounding errors with floating point math, we do a long multiply on the data. - ftw = freq * DDS_MULT[0]; - - for (i = 0; i < DDS_FREQ_TO_FTW_DIGITS - 1; ++i) - ftw += (freq * DDS_MULT[i+1]) / DDS_DIVISOR[i]; - - ddsSetFTW (ftw); -} - -/** - * Set DDS frequency tuning word for the FSK 0 and 1 values. The output frequency is equal - * to RefClock * (ftw / 2 ^ 32). - * - * @param ftw0 frequency tuning word for the FSK 0 value - * @param ftw1 frequency tuning word for the FSK 1 value - */ -void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1) -{ -// printf ("ftw0 %d ftw1 %d\n", ftw0, ftw1); -} - -/** - * Set the DDS to run in A-FSK, FSK, or PSK31 mode - * - * @param mode DDS_MODE_APRS, DDS_MODE_PSK31, or DDS_MODE_HF_APRS constant - */ -void ddsSetMode (DDS_MODE mode) -{ -// printf ("mode %d\n", mode); -} - -/** @} */ - -/** - * @defgroup GPS Motorola M12+ GPS Engine - * - * Functions to control the Motorola M12+ GPS engine in native binary protocol mode. - * - * @{ - */ - -/// The maximum length of a binary GPS engine message. -#define GPS_BUFFER_SIZE 50 - -/// GPS parse engine state machine values. -typedef enum -{ - /// 1st start character '@' - GPS_START1, - - /// 2nd start character '@' - GPS_START2, - - /// Upper case 'A' - 'Z' message type - GPS_COMMAND1, - - /// Lower case 'a' - 'z' message type - GPS_COMMAND2, - - /// 0 - xx bytes based on message type 'Aa' - GPS_READMESSAGE, - - /// 8-bit checksum - GPS_CHECKSUMMESSAGE, - - /// End of message - Carriage Return - GPS_EOMCR, - - /// End of message - Line Feed - GPS_EOMLF -} GPS_PARSE_STATE_MACHINE; - -/// Index into gpsBuffer used to store message data. -uint8_t gpsIndex; - -/// State machine used to parse the GPS message stream. -GPS_PARSE_STATE_MACHINE gpsParseState; - -/// Buffer to store data as it is read from the GPS engine. -uint8_t gpsBuffer[GPS_BUFFER_SIZE]; - -/// Peak altitude detected while GPS is in 3D fix mode. -int32_t gpsPeakAltitude; - -/// Checksum used to verify binary message from GPS engine. -uint8_t gpsChecksum; - -/// Last verified GPS message received. -GPSPOSITION_STRUCT gpsPosition; - -/** - * Get the type of fix. - * - * @return gps fix type enumeration - */ -GPS_FIX_TYPE gpsGetFixType() -{ - // The upper 3-bits determine the fix type. - switch (gpsPosition.status & 0xe000) - { - case 0xe000: - return GPS_3D_FIX; - - case 0xc000: - return GPS_2D_FIX; - - default: - return GPS_NO_FIX; - } // END switch -} - -/** - * Peak altitude detected while GPS is in 3D fix mode since the system was booted. - * - * @return altitude in feet - */ -int32_t gpsGetPeakAltitude() -{ - return gpsPeakAltitude; -} - -/** - * Initialize the GPS subsystem. - */ -void gpsInit() -{ - // Initial parse state. - gpsParseState = GPS_START1; - - // Assume we start at sea level. - gpsPeakAltitude = 0; - - // Clear the structure that stores the position message. - memset (&gpsPosition, 0, sizeof(GPSPOSITION_STRUCT)); - - // Setup the timers used to measure the 1-PPS time period. -// setup_timer_3(T3_INTERNAL | T3_DIV_BY_1); -// setup_ccp2 (CCP_CAPTURE_RE | CCP_USE_TIMER3); -} - -/** - * Determine if new GPS message is ready to process. This function is a one shot and - * typically returns true once a second for each GPS position fix. - * - * @return true if new message available; otherwise false - */ -bool_t gpsIsReady() -{ - return true; - if (gpsPosition.updateFlag) - { - gpsPosition.updateFlag = false; - return true; - } // END if - - return false; -} - -/** - * Calculate NMEA-0183 message checksum of buffer that is length bytes long. - * - * @param buffer pointer to data buffer. - * @param length number of bytes in buffer. - * - * @return checksum of buffer - */ -uint8_t gpsNMEAChecksum (uint8_t *buffer, uint8_t length) -{ - uint8_t i, checksum; - - checksum = 0; - - for (i = 0; i < length; ++i) - checksum ^= buffer[i]; - - return checksum; -} - -/** - * Verify the GPS engine is sending the @@Hb position report message. If not, - * configure the GPS engine to send the desired report. - * - * @return true if GPS engine operation; otherwise false - */ -bool_t gpsSetup() -{ - uint8_t startTime, retryCount; - - // We wait 10 seconds for the GPS engine to respond to our message request. - startTime = timeGetTicks(); - retryCount = 0; - - while (++retryCount < 10) - { - // Read the serial FIFO and process the GPS messages. -// gpsUpdate(); - - // If a GPS data set is available, then GPS is operational. - if (gpsIsReady()) - { -// timeSetDutyCycle (TIME_DUTYCYCLE_10); - return true; - } - - if (timeGetTicks() > startTime) - { - puts ("@@Hb\001\053\015\012"); - startTime += 10; - } // END if - - } // END while - - return false; -} - -/** - * Parse the Motorola @@Hb (Short position/message) report. - */ -void gpsParsePositionMessage() -{ - // Convert the binary stream into data elements. We will scale to the desired units - // as the values are used. - gpsPosition.updateFlag = true; - - gpsPosition.month = gpsBuffer[0]; - gpsPosition.day = gpsBuffer[1]; - gpsPosition.year = ((uint16_t) gpsBuffer[2] << 8) | gpsBuffer[3]; - gpsPosition.hours = gpsBuffer[4]; - gpsPosition.minutes = gpsBuffer[5]; - gpsPosition.seconds = gpsBuffer[6]; - gpsPosition.latitude = ((int32) gpsBuffer[11] << 24) | ((int32) gpsBuffer[12] << 16) | ((int32) gpsBuffer[13] << 8) | (int32) gpsBuffer[14]; - gpsPosition.longitude = ((int32) gpsBuffer[15] << 24) | ((int32) gpsBuffer[16] << 16) | ((int32) gpsBuffer[17] << 8) | gpsBuffer[18]; - gpsPosition.altitudeCM = ((int32) gpsBuffer[19] << 24) | ((int32) gpsBuffer[20] << 16) | ((int32) gpsBuffer[21] << 8) | gpsBuffer[22]; - gpsPosition.altitudeFeet = gpsPosition.altitudeCM * 100l / 3048l; - gpsPosition.vSpeed = ((uint16_t) gpsBuffer[27] << 8) | gpsBuffer[28]; - gpsPosition.hSpeed = ((uint16_t) gpsBuffer[29] << 8) | gpsBuffer[30]; - gpsPosition.heading = ((uint16_t) gpsBuffer[31] << 8) | gpsBuffer[32]; - gpsPosition.dop = ((uint16_t) gpsBuffer[33] << 8) | gpsBuffer[34]; - gpsPosition.visibleSats = gpsBuffer[35]; - gpsPosition.trackedSats = gpsBuffer[36]; - gpsPosition.status = ((uint16_t) gpsBuffer[37] << 8) | gpsBuffer[38]; - - // Update the peak altitude if we have a valid 3D fix. - if (gpsGetFixType() == GPS_3D_FIX) - if (gpsPosition.altitudeFeet > gpsPeakAltitude) - gpsPeakAltitude = gpsPosition.altitudeFeet; -} - -/** - * Turn on the GPS engine power and serial interface. - */ -void gpsPowerOn() -{ - // 3.0 VDC LDO control line. -// output_high (IO_GPS_PWR); - -} - -/** - * Turn off the GPS engine power and serial interface. - */ -void gpsPowerOff() -{ - // 3.0 VDC LDO control line. -// output_low (IO_GPS_PWR); -} - /** @} */ - /** * @defgroup sys System Library Functions * @@ -759,9 +331,6 @@ uint16_t timeNCOFreq; /// Counter used to deciminate down from the 104uS to 833uS interrupt rate. (9600 to 1200 baud) uint8_t timeLowRateCount; -/// Current TNC mode (standby, 1200bps A-FSK, or 9600bps FSK) -TNC_DATA_MODE tncDataMode; - /// Flag set true once per second. bool_t timeUpdateFlag; @@ -797,7 +366,6 @@ void timeInit() timeNCO = 0x00; timeLowRateCount = 0; timeNCOFreq = 0x2000; - tncDataMode = TNC_MODE_STANDBY; timeRunFlag = false; } @@ -833,29 +401,16 @@ void timeUpdate() { // Setup the next interrupt for the operational mode. timeCompare += TIME_RATE; -// CCP_1 = timeCompare; - - switch (tncDataMode) - { - case TNC_MODE_STANDBY: - break; - case TNC_MODE_1200_AFSK: - ddsSetFTW (freqTable[timeNCO >> 8]); + ddsSetFTW (freqTable[timeNCO >> 8]); - timeNCO += timeNCOFreq; + timeNCO += timeNCOFreq; - if (++timeLowRateCount == 8) - { - timeLowRateCount = 0; - tnc1200TimerTick(); - } // END if - break; - - case TNC_MODE_9600_FSK: - tnc9600TimerTick(); - break; - } // END switch + if (++timeLowRateCount == 8) + { + timeLowRateCount = 0; + tnc1200TimerTick(); + } // END if } /** @} */ @@ -981,32 +536,6 @@ void tncHighRate(bool_t state) tncHighRateFlag = state; } -/** - * Configure the TNC for the desired data mode. - * - * @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK - */ -void tncSetMode(TNC_DATA_MODE dataMode) -{ - switch (dataMode) - { - case TNC_MODE_1200_AFSK: - ddsSetMode (DDS_MODE_AFSK); - break; - - case TNC_MODE_9600_FSK: - ddsSetMode (DDS_MODE_FSK); - - // FSK tones at 445.947 and 445.953 MHz - ddsSetFSKFreq (955382980, 955453621); - break; - case TNC_MODE_STANDBY: - break; - } // END switch - - tncDataMode = dataMode; -} - /** * Determine if the seconds value timeSeconds is a valid time slot to transmit * a message. Time seconds is in UTC. @@ -1197,14 +726,6 @@ void tnc1200TimerTick() { tncMode = TNC_TX_READY; - // Tell the TNC time interrupt to stop generating the frequency words. - tncDataMode = TNC_MODE_STANDBY; - - // Key off the DDS. -// output_low (IO_OSK); -// output_low (IO_PTT); - ddsSetMode (DDS_MODE_POWERDOWN); - return; } // END if } else @@ -1222,36 +743,6 @@ void tnc9600TimerTick() } -/** - * Write character to the TNC buffer. Maintain the pointer - * and length to the buffer. The pointer tncBufferPnt and tncLength - * must be set before calling this function for the first time. - * - * @param character to save to telemetry buffer - */ -void tncTxByte (uint8_t character) -{ - *tncBufferPnt++ = character; - ++tncLength; -} - -static void -tncPrintf(char *fmt, ...) -{ - va_list ap; - int c; - - va_start(ap, fmt); - c = vsprintf((char *) tncBufferPnt, fmt, ap); - if (*fmt == '\015') - fprintf (stderr, "\n"); - else - vfprintf(stderr, fmt, ap); - va_end(ap); - tncBufferPnt += c; - tncLength += c; -} - /** * Generate the plain text position packet. Data is written through the tncTxByte * callback function @@ -1267,6 +758,7 @@ void tncPositionPacket(void) uint16_t lat_frac; uint16_t lon_min; uint16_t lon_frac; + int c; char lat_sign = 'N', lon_sign = 'E'; @@ -1294,10 +786,12 @@ void tncPositionPacket(void) longitude -= lon_min * 10000000; lon_frac = (longitude + 50000) / 100000; - tncPrintf ("=%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 * 100 / 3048); + c = sprintf ((char *) tncBufferPnt, "=%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 * 100 / 3048); + tncBufferPnt += c; + tncLength += c; } /** @@ -1306,17 +800,10 @@ void tncPositionPacket(void) * * @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK */ -void tncTxPacket(TNC_DATA_MODE dataMode) +void tncTxPacket(void) { uint16_t crc; - // Only transmit if there is not another message in progress. - if (tncMode != TNC_TX_READY) - return; - - // Configure the DDS for the desired operational. - tncSetMode (dataMode); - // Set a pointer to our TNC output buffer. tncBufferPnt = tncBuffer; diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index 947a02b4..93dab577 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -66,13 +66,12 @@ audio_gap(int secs) // This is where we go after reset. int main(int argc, char **argv) { - gpsInit(); tncInit(); audio_gap(1); /* Transmit one packet */ - tncTxPacket(TNC_MODE_1200_AFSK); + tncTxPacket(); exit(0); } -- cgit v1.2.3 From 74969483736381858484dca9ebb528d9d2d73f5b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 22:23:46 -0800 Subject: altos: Start restructuring APRS code to create and send packets Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 52 +++++++++++++++++++++++-------------------------- src/test/ao_aprs_test.c | 52 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 32 deletions(-) (limited to 'src/test') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 7e9013a0..b8d17bd9 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -238,22 +238,6 @@ void timeInit() timeNCOFreq = 0x2000; } -/** - * Timer interrupt handler called every 104uS (9600 times/second). - */ -void timeUpdate() -{ - putchar ((timeNCO >> 8) < 0x80 ? 0xc0 : 0x40); - - timeNCO += timeNCOFreq; - - if (++timeLowRateCount == 8) - { - timeLowRateCount = 0; - tnc1200TimerTick(); - } // END if -} - /** @} */ /** @@ -553,6 +537,28 @@ void tncPositionPacket(void) tncLength += c; } +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. @@ -589,19 +595,9 @@ void tncTxPacket(void) tncIndex = 0; tncMode = TNC_TX_SYNC; - // Turn on the PA chain. -// output_high (IO_PTT); - - // Wait for the PA chain to power up. -// delay_ms (10); - - // Key the DDS. -// output_high (IO_OSK); + timeInit(); - // Log the battery and reference voltage just after we key the transmitter. -// sysLogVoltage(); - while (tncMode != TNC_TX_READY) - timeUpdate(); + ao_radio_send_lots(tncFill); } /** @} */ diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index 93dab577..d350ca0d 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -25,6 +25,27 @@ #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 /* @@ -57,10 +78,12 @@ static void audio_gap(int secs) { +#if !DEBUG int samples = secs * 9600; while (samples--) - putchar(0x7f); + ao_aprs_bit(0); +#endif } // This is where we go after reset. @@ -76,6 +99,27 @@ int main(int argc, char **argv) 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; + } + } + } +} -- cgit v1.2.3 From 51ef826372f466f44901c4c609ed6a987d30fda4 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 23:39:47 -0800 Subject: altos: Prepare APRS for use within altos itself Make all variables static, const-ify constants, change the public name of the single entry point. Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 76 +++++++++++++++++++------------------------------ src/drivers/ao_aprs.h | 3 -- src/test/ao_aprs_test.c | 4 +-- 3 files changed, 30 insertions(+), 53 deletions(-) (limited to 'src/test') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index b8d17bd9..1a074ba5 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -152,23 +152,10 @@ typedef int32_t int32; // Public methods, constants, and data structures for each class. -void ddsInit(); -void ddsSetAmplitude (uint8_t amplitude); -void ddsSetOutputScale (uint16_t amplitude); -void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1); -void ddsSetFreq (uint32_t freq); -void ddsSetFTW (uint32_t ftw); +static void timeInit(void); -uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc); - -void timeInit(); -void timeSetDutyCycle (uint8_t dutyCycle); -void timeUpdate(); - -void tncInit(); -void tnc1200TimerTick(); -void tncTxByte (uint8_t value); -void tncTxPacket(void); +static void tncInit(void); +static void tnc1200TimerTick(void); /** @} */ @@ -190,7 +177,7 @@ void tncTxPacket(void); * * @return CRC-16 of buffer[0 .. length] */ -uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc) +static uint16_t sysCRC16(const uint8_t *buffer, uint8_t length, uint16_t crc) { uint8_t i, bit, value; @@ -220,21 +207,17 @@ uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc) */ /// 16-bit NCO where the upper 8-bits are used to index into the frequency generation table. -uint16_t timeNCO; +static uint16_t timeNCO; /// Audio tone NCO update step (phase). -uint16_t timeNCOFreq; - -/// Counter used to deciminate down from the 104uS to 833uS interrupt rate. (9600 to 1200 baud) -uint8_t timeLowRateCount; +static uint16_t timeNCOFreq; /** * Initialize the real-time clock. */ -void timeInit() +static void timeInit() { timeNCO = 0x00; - timeLowRateCount = 0; timeNCOFreq = 0x2000; } @@ -252,7 +235,7 @@ void timeInit() #define TNC_TX_DELAY 45 /// The size of the TNC output buffer. -#define TNC_BUFFER_SIZE 80 +#define TNC_BUFFER_SIZE 40 /// States that define the current mode of the 1200 bps (A-FSK) state machine. typedef enum @@ -275,44 +258,43 @@ typedef enum /// AX.25 compliant packet header that contains destination, station call sign, and path. /// 0x76 for SSID-11, 0x78 for SSID-12 -uint8_t TNC_AX25_HEADER[30] = { - 'A' << 1, 'P' << 1, 'R' << 1, 'S' << 1, ' ' << 1, ' ' << 1, 0x60, \ - 'K' << 1, 'D' << 1, '7' << 1, 'S' << 1, 'Q' << 1, 'G' << 1, 0x76, \ - 'G' << 1, 'A' << 1, 'T' << 1, 'E' << 1, ' ' << 1, ' ' << 1, 0x60, \ - 'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '3' << 1, ' ' << 1, 0x67, \ +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. -uint8_t tncTxBit; +static uint8_t tncTxBit; /// Current mode of the 1200 bps state machine. -TNC_TX_1200BPS_STATE tncMode; +static TNC_TX_1200BPS_STATE tncMode; /// Counter for each bit (0 - 7) that we are going to transmit. -uint8_t tncBitCount; +static uint8_t tncBitCount; /// A shift register that holds the data byte as we bit shift it for transmit. -uint8_t tncShift; +static uint8_t tncShift; /// Index into the APRS header and data array for each byte as we transmit it. -uint8_t tncIndex; +static uint8_t tncIndex; /// The number of bytes in the message portion of the AX.25 message. -uint8_t tncLength; +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. -uint8_t tncBitStuff; +static uint8_t tncBitStuff; /// Pointer to TNC buffer as we save each byte during message preparation. -uint8_t *tncBufferPnt; +static uint8_t *tncBufferPnt; /// Buffer to hold the message portion of the AX.25 packet as we prepare it. -uint8_t tncBuffer[TNC_BUFFER_SIZE]; +static uint8_t tncBuffer[TNC_BUFFER_SIZE]; /** * Initialize the TNC internal variables. */ -void tncInit() +static void tncInit() { tncTxBit = 0; tncMode = TNC_TX_READY; @@ -322,7 +304,7 @@ void tncInit() * 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. */ -void tnc1200TimerTick() +static void tnc1200TimerTick() { // Set the A-FSK frequency. if (tncTxBit == 0x00) @@ -487,10 +469,9 @@ void tnc1200TimerTick() } /** - * Generate the plain text position packet. Data is written through the tncTxByte - * callback function + * Generate the plain text position packet. */ -void tncPositionPacket(void) +static void tncPositionPacket(void) { int32_t latitude = 45.4694766 * 10000000; int32_t longitude = -122.7376250 * 10000000; @@ -565,10 +546,13 @@ tncFill(uint8_t *buf, int16_t len) * * @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK */ -void tncTxPacket(void) +void ao_aprs_send(void) { uint16_t crc; + timeInit(); + tncInit(); + // Set a pointer to our TNC output buffer. tncBufferPnt = tncBuffer; @@ -595,8 +579,6 @@ void tncTxPacket(void) tncIndex = 0; tncMode = TNC_TX_SYNC; - timeInit(); - ao_radio_send_lots(tncFill); } diff --git a/src/drivers/ao_aprs.h b/src/drivers/ao_aprs.h index fe3c6349..a033fa0b 100644 --- a/src/drivers/ao_aprs.h +++ b/src/drivers/ao_aprs.h @@ -21,7 +21,4 @@ void ao_aprs_send(void); -void -ao_aprs_init(void); - #endif /* _AO_APRS_H_ */ diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index d350ca0d..d0cfb52d 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -89,12 +89,10 @@ audio_gap(int secs) // This is where we go after reset. int main(int argc, char **argv) { - tncInit(); - audio_gap(1); /* Transmit one packet */ - tncTxPacket(); + ao_aprs_send(); exit(0); } -- cgit v1.2.3 From c1e6fa32b856b91afa355cd272d2d7287d3ccca1 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 6 Dec 2012 10:12:11 -0800 Subject: altos: Hook APRS up to the radio This adds an arbitrary-length packet writing function to the radio code. Signed-off-by: Keith Packard --- src/core/ao.h | 5 ++ src/drivers/ao_aprs.c | 48 +++++------- src/drivers/ao_cc1120.c | 175 +++++++++++++++++++++++++++++++++++++------ src/megametrum-v0.1/Makefile | 1 + src/test/ao_aprs_test.c | 6 ++ 5 files changed, 183 insertions(+), 52 deletions(-) (limited to 'src/test') diff --git a/src/core/ao.h b/src/core/ao.h index 54018b37..d6e27707 100644 --- a/src/core/ao.h +++ b/src/core/ao.h @@ -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: * diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 1a074ba5..e273908f 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -145,11 +145,6 @@ #include -typedef int bool_t; -typedef int32_t int32; -#define false 0 -#define true 1 - // Public methods, constants, and data structures for each class. static void timeInit(void); @@ -285,9 +280,6 @@ 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; -/// Pointer to TNC buffer as we save each byte during message preparation. -static uint8_t *tncBufferPnt; - /// Buffer to hold the message portion of the AX.25 packet as we prepare it. static uint8_t tncBuffer[TNC_BUFFER_SIZE]; @@ -471,18 +463,18 @@ static void tnc1200TimerTick() /** * Generate the plain text position packet. */ -static void tncPositionPacket(void) +static int tncPositionPacket(void) { - int32_t latitude = 45.4694766 * 10000000; - int32_t longitude = -122.7376250 * 10000000; - uint32_t altitude = 10000; + 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; - int c; char lat_sign = 'N', lon_sign = 'E'; @@ -510,12 +502,15 @@ static void tncPositionPacket(void) longitude -= lon_min * 10000000; lon_frac = (longitude + 50000) / 100000; - c = sprintf ((char *) tncBufferPnt, "=%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 * 100 / 3048); - tncBufferPnt += c; - tncLength += c; + 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 @@ -553,24 +548,15 @@ void ao_aprs_send(void) timeInit(); tncInit(); - // Set a pointer to our TNC output buffer. - tncBufferPnt = tncBuffer; - - // Set the message length counter. - tncLength = 0; - - tncPositionPacket(); + 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. - *tncBufferPnt++ = crc & 0xff; - *tncBufferPnt = (crc >> 8) & 0xff; - - // Update the length to include the CRC bytes. - tncLength += 2; + tncBuffer[tncLength++] = crc & 0xff; + tncBuffer[tncLength++] = (crc >> 8) & 0xff; // Prepare the variables that are used in the real-time clock interrupt. tncBitCount = 0; diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index f27958f9..ed26e28d 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -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(); } diff --git a/src/megametrum-v0.1/Makefile b/src/megametrum-v0.1/Makefile index 7d6c7388..25d0ed03 100644 --- a/src/megametrum-v0.1/Makefile +++ b/src/megametrum-v0.1/Makefile @@ -90,6 +90,7 @@ ALTOS_SRC = \ ao_packet.c \ ao_companion.c \ ao_pyro.c \ + ao_aprs.c \ $(PROFILE) \ $(SAMPLE_PROFILE) \ $(STACK_GUARD) diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index d0cfb52d..f16c94e8 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -23,6 +23,8 @@ #include +struct ao_telemetry_location ao_gps_data; + #define AO_APRS_TEST typedef int16_t (*ao_radio_fill_func)(uint8_t *buffer, int16_t len); @@ -91,6 +93,10 @@ 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(); -- cgit v1.2.3 From 4e3ac3f2038cc3a43252fc8f820a1373a637ab83 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 16 Dec 2012 13:31:45 -0800 Subject: altos: Test APRS rounding by using coordinates near the boundary This selects lat/lon and altitude near the rounding boundary to check that the resulting APRS data is correctly computed. Signed-off-by: Keith Packard --- src/test/ao_aprs_test.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/test') diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index f16c94e8..3b31f2d3 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -93,13 +93,16 @@ 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; + ao_gps_data.latitude = (45.0 + 28.25 / 60.0) * 10000000; + ao_gps_data.longitude = (-(122 + 44.2649 / 60.0)) * 10000000; + ao_gps_data.altitude = 84; /* Transmit one packet */ ao_aprs_send(); + tncBuffer[strlen((char *) tncBuffer) - 2] = '\0'; + fprintf(stderr, "packet: %s\n", tncBuffer); + exit(0); } -- cgit v1.2.3 From b1d37be4c024e9690107c693d9819229025966fa Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 17 Dec 2012 17:03:41 -0800 Subject: altos: Average MPU6000 values on ground for later use Having long-term ground averages recorded to the eeprom file will make post-flight analysis of the data better. Signed-off-by: Keith Packard --- src/core/ao_data.h | 21 +++++++++++ src/core/ao_log.h | 14 +++++-- src/core/ao_sample.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++- src/core/ao_sqrt.c | 2 + src/test/ao_flight_test.c | 17 ++++++++- 5 files changed, 142 insertions(+), 7 deletions(-) (limited to 'src/test') diff --git a/src/core/ao_data.h b/src/core/ao_data.h index 6fdd19cb..7e2f85d8 100644 --- a/src/core/ao_data.h +++ b/src/core/ao_data.h @@ -288,4 +288,25 @@ typedef int16_t accel_t; #endif +#if !HAS_GYRO && HAS_MPU6000 + +#define HAS_GYRO 1 + +typedef int16_t gyro_t; +typedef int32_t angle_t; + +/* Y axis is aligned with the direction of motion (along) */ +/* X axis is aligned in the other board axis (across) */ +/* Z axis is aligned perpendicular to the board (through) */ + +#define ao_data_along(packet) ((packet)->mpu6000.accel_y) +#define ao_data_across(packet) ((packet)->mpu6000.accel_x) +#define ao_data_through(packet) ((packet)->mpu6000.accel_z) + +#define ao_data_roll(packet) ((packet)->mpu6000.gyro_y) +#define ao_data_pitch(packet) ((packet)->mpu6000.gyro_x) +#define ao_data_yaw(packet) ((packet)->mpu6000.gyro_z) + +#endif + #endif /* _AO_DATA_H_ */ diff --git a/src/core/ao_log.h b/src/core/ao_log.h index 4eaed420..93b01778 100644 --- a/src/core/ao_log.h +++ b/src/core/ao_log.h @@ -199,10 +199,16 @@ struct ao_log_mega { union { /* 4 */ /* AO_LOG_FLIGHT */ struct { - uint16_t flight; /* 4 */ - int16_t ground_accel; /* 6 */ - uint32_t ground_pres; /* 8 */ - } flight; /* 12 */ + uint16_t flight; /* 4 */ + int16_t ground_accel; /* 6 */ + uint32_t ground_pres; /* 8 */ + int16_t ground_accel_along; /* 16 */ + int16_t ground_accel_across; /* 12 */ + int16_t ground_accel_through; /* 14 */ + int16_t ground_gyro_roll; /* 18 */ + int16_t ground_gyro_pitch; /* 20 */ + int16_t ground_gyro_yaw; /* 22 */ + } flight; /* 24 */ /* AO_LOG_STATE */ struct { uint16_t state; diff --git a/src/core/ao_sample.c b/src/core/ao_sample.c index 985c0940..7a1eff8e 100644 --- a/src/core/ao_sample.c +++ b/src/core/ao_sample.c @@ -37,6 +37,16 @@ __pdata alt_t ao_sample_height; #if HAS_ACCEL __pdata accel_t ao_sample_accel; #endif +#if HAS_GYRO +__pdata accel_t ao_sample_accel_along; +__pdata accel_t ao_sample_accel_across; +__pdata accel_t ao_sample_accel_through; +__pdata gyro_t ao_sample_roll; +__pdata gyro_t ao_sample_pitch; +__pdata gyro_t ao_sample_yaw; +__pdata angle_t ao_sample_angle; +__pdata angle_t ao_sample_roll_angle; +#endif __data uint8_t ao_sample_data; @@ -53,6 +63,15 @@ __pdata accel_t ao_accel_2g; /* factory accel calibration */ __pdata int32_t ao_accel_scale; /* sensor to m/s² conversion */ #endif +#if HAS_GYRO +__pdata accel_t ao_ground_accel_along; +__pdata accel_t ao_ground_accel_across; +__pdata accel_t ao_ground_accel_through; +__pdata gyro_t ao_ground_pitch; +__pdata gyro_t ao_ground_yaw; +__pdata gyro_t ao_ground_roll; +#endif + static __pdata uint8_t ao_preflight; /* in preflight mode */ static __pdata uint16_t nsamples; @@ -60,6 +79,14 @@ __pdata int32_t ao_sample_pres_sum; #if HAS_ACCEL __pdata int32_t ao_sample_accel_sum; #endif +#if HAS_GYRO +__pdata int32_t ao_sample_accel_along_sum; +__pdata int32_t ao_sample_accel_across_sum; +__pdata int32_t ao_sample_accel_through_sum; +__pdata int32_t ao_sample_pitch_sum; +__pdata int32_t ao_sample_yaw_sum; +__pdata int32_t ao_sample_roll_sum; +#endif static void ao_sample_preflight_add(void) @@ -68,6 +95,14 @@ ao_sample_preflight_add(void) ao_sample_accel_sum += ao_sample_accel; #endif ao_sample_pres_sum += ao_sample_pres; +#if HAS_GYRO + ao_sample_accel_along_sum += ao_sample_accel_along; + ao_sample_accel_across_sum += ao_sample_accel_across; + ao_sample_accel_through_sum += ao_sample_accel_through; + ao_sample_pitch_sum += ao_sample_pitch; + ao_sample_yaw_sum += ao_sample_yaw; + ao_sample_roll_sum += ao_sample_roll; +#endif ++nsamples; } @@ -80,8 +115,23 @@ ao_sample_preflight_set(void) #endif ao_ground_pres = ao_sample_pres_sum >> 9; ao_ground_height = pres_to_altitude(ao_ground_pres); - nsamples = 0; ao_sample_pres_sum = 0; +#if HAS_GYRO + ao_ground_accel_along = ao_sample_accel_along_sum >> 9; + ao_ground_accel_across = ao_sample_accel_across_sum >> 9; + ao_ground_accel_through = ao_sample_accel_through_sum >> 9; + ao_ground_pitch = ao_sample_pitch_sum >> 9; + ao_ground_yaw = ao_sample_yaw_sum >> 9; + ao_ground_roll = ao_sample_roll_sum >> 9; + ao_sample_accel_along_sum = 0; + ao_sample_accel_across_sum = 0; + ao_sample_accel_through_sum = 0; + ao_sample_pitch_sum = 0; + ao_sample_yaw_sum = 0; + ao_sample_roll_sum = 0; + ao_sample_angle = 0; +#endif + nsamples = 0; } static void @@ -122,6 +172,24 @@ ao_sample_preflight_update(void) ao_sample_preflight_set(); } +#if HAS_GYRO + +static int32_t p_filt; +static int32_t y_filt; + +static gyro_t inline ao_gyro(void) { + gyro_t p = ao_sample_pitch - ao_ground_pitch; + gyro_t y = ao_sample_yaw - ao_ground_yaw; + + p_filt = p_filt - (p_filt >> 6) + p; + y_filt = y_filt - (y_filt >> 6) + y; + + p = p_filt >> 6; + y = y_filt >> 6; + return ao_sqrt(p*p + y*y); +} +#endif + uint8_t ao_sample(void) { @@ -147,6 +215,12 @@ ao_sample(void) ao_sample_accel = ao_data_accel_invert(ao_sample_accel); ao_data_set_accel(ao_data, ao_sample_accel); #endif +#if HAS_GYRO + ao_sample_accel_along = ao_data_along(ao_data); + ao_sample_pitch = ao_data_pitch(ao_data); + ao_sample_yaw = ao_data_yaw(ao_data); + ao_sample_roll = ao_data_roll(ao_data); +#endif if (ao_preflight) ao_sample_preflight(); @@ -154,6 +228,10 @@ ao_sample(void) if (ao_flight_state < ao_flight_boost) ao_sample_preflight_update(); ao_kalman(); +#if HAS_GYRO + ao_sample_angle += ao_gyro(); + ao_sample_roll_angle += (ao_sample_roll - ao_ground_roll); +#endif } ao_sample_data = ao_data_ring_next(ao_sample_data); } @@ -170,6 +248,21 @@ ao_sample_init(void) #if HAS_ACCEL ao_sample_accel_sum = 0; ao_sample_accel = 0; +#endif +#if HAS_GYRO + ao_sample_accel_along_sum = 0; + ao_sample_accel_across_sum = 0; + ao_sample_accel_through_sum = 0; + ao_sample_accel_along = 0; + ao_sample_accel_across = 0; + ao_sample_accel_through = 0; + ao_sample_pitch_sum = 0; + ao_sample_yaw_sum = 0; + ao_sample_roll_sum = 0; + ao_sample_pitch = 0; + ao_sample_yaw = 0; + ao_sample_roll = 0; + ao_sample_angle = 0; #endif ao_sample_data = ao_data_head; ao_preflight = TRUE; diff --git a/src/core/ao_sqrt.c b/src/core/ao_sqrt.c index 09c2e319..3a550eaa 100644 --- a/src/core/ao_sqrt.c +++ b/src/core/ao_sqrt.c @@ -15,7 +15,9 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#ifndef AO_FLIGHT_TEST #include "ao.h" +#endif /* Adapted from int_sqrt.c in the linux kernel, which is licensed GPLv2 */ /** diff --git a/src/test/ao_flight_test.c b/src/test/ao_flight_test.c index acdf4d92..cdd1f236 100644 --- a/src/test/ao_flight_test.c +++ b/src/test/ao_flight_test.c @@ -236,10 +236,14 @@ extern int32_t ao_accel_scale; extern alt_t ao_ground_height; extern alt_t ao_sample_alt; +double ao_sample_qangle; + int ao_sample_prev_tick; uint16_t prev_tick; + #include "ao_kalman.c" +#include "ao_sqrt.c" #include "ao_sample.c" #include "ao_flight.c" @@ -309,7 +313,7 @@ ao_mpu6000_accel(int16_t sensor) } static double -ao_mpu6000_gyro(int16_t sensor) +ao_mpu6000_gyro(int32_t sensor) { return sensor / 32767.0 * MPU6000_GYRO_FULLSCALE; } @@ -370,6 +374,7 @@ ao_insert(void) if (!ao_summary) { printf("%7.2f height %8.2f accel %8.3f " #if MEGAMETRUM + "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 "state %-8.8s k_height %8.2f k_speed %8.3f k_accel %8.3f avg_height %5d drogue %4d main %4d error %5d\n", @@ -377,6 +382,9 @@ ao_insert(void) height, accel, #if MEGAMETRUM + ao_mpu6000_gyro(ao_sample_roll_angle) / 100.0, + ao_mpu6000_gyro(ao_sample_angle) / 100.0, + ao_sample_qangle, ao_mpu6000_accel(ao_data_static.mpu6000.accel_x), ao_mpu6000_accel(ao_data_static.mpu6000.accel_y), ao_mpu6000_accel(ao_data_static.mpu6000.accel_z), @@ -715,12 +723,14 @@ update_orientation (double rate_x, double rate_y, double rate_z, int tick) q_dot.q2 = 0.5 * (ao_orient.q0 * rate_y + ao_orient.q3 * rate_x - ao_orient.q1 * rate_z) + lambda * ao_orient.q2; q_dot.q3 = 0.5 * (ao_orient.q0 * rate_z + ao_orient.q1 * rate_y - ao_orient.q2 * rate_x) + lambda * ao_orient.q3; +#if 0 printf ("update_orientation %g %g %g (%g s)\n", rate_x, rate_y, rate_z, dt); printf ("q_dot (%g) %g %g %g\n", q_dot.q0, q_dot.q1, q_dot.q2, q_dot.q3); +#endif ao_orient.q0 += q_dot.q0 * dt; ao_orient.q1 += q_dot.q1 * dt; @@ -731,6 +741,8 @@ update_orientation (double rate_x, double rate_y, double rate_z, int tick) ao_quat_rot(&ao_current, &ao_up, &ao_orient); + ao_sample_qangle = 180 / M_PI * acos(ao_current.q3 * sqrt(2)); +#if 0 printf ("orient (%g) %g %g %g current (%g) %g %g %g\n", ao_orient.q0, ao_orient.q1, @@ -740,6 +752,7 @@ update_orientation (double rate_x, double rate_y, double rate_z, int tick) ao_current.q1, ao_current.q2, ao_current.q3); +#endif } #endif @@ -845,7 +858,7 @@ ao_sleep(void *wchan) double rate_y = ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_y - ao_ground_mpu6000.gyro_y); double rate_z = ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_z - ao_ground_mpu6000.gyro_z); - update_orientation(rate_x, rate_z, rate_y, tick); + update_orientation(rate_x * M_PI / 180, rate_z * M_PI / 180, rate_y * M_PI / 180, tick); } ao_records_read++; ao_insert(); -- cgit v1.2.3