2 * http://ad7zj.net/kd7lmo/aprsbeacon_code.html
4 * @mainpage Pico Beacon
6 * @section overview_sec Overview
8 * The Pico Beacon is an APRS based tracking beacon that operates in the UHF 420-450MHz band. The device utilizes a
9 * Microchip PIC 18F2525 embedded controller, Motorola M12+ GPS engine, and Analog Devices AD9954 DDS. The device is capable
10 * of generating a 1200bps A-FSK and 9600 bps FSK AX.25 compliant APRS (Automatic Position Reporting System) message.
14 * @section history_sec Revision History
16 * @subsection v305 V3.05
17 * 23 Dec 2006, Change include; (1) change printf format width to conform to ANSI standard when new CCS 4.xx compiler released.
20 * @subsection v304 V3.04
21 * 10 Jan 2006, Change include; (1) added amplitude control to engineering mode,
22 * (2) corrected number of bytes reported in log,
23 * (3) add engineering command to set high rate position reports (5 seconds), and
24 * (4) corrected size of LOG_COORD block when searching for end of log.
26 * @subsection v303 V3.03
27 * 15 Sep 2005, Change include; (1) removed AD9954 setting SDIO as input pin,
28 * (2) additional comments and Doxygen tags,
29 * (3) integration and test code calculates DDS FTW,
30 * (4) swapped bus and reference analog input ports (hardware change),
31 * (5) added message that indicates we are reading flash log and reports length,
32 * (6) report bus voltage in 10mV steps, and
33 * (7) change log type enumerated values to XORed nibbles for error detection.
36 * @subsection v302 V3.02
37 * 6 Apr 2005, Change include; (1) corrected tracked satellite count in NMEA-0183 $GPGGA message,
38 * (2) Doxygen documentation clean up and additions, and
39 * (3) added integration and test code to baseline.
42 * @subsection v301 V3.01
43 * 13 Jan 2005, Renamed project and files to Pico Beacon.
46 * @subsection v300 V3.00
47 * 15 Nov 2004, Change include; (1) Micro Beacon extreme hardware changes including integral transmitter,
48 * (2) PIC18F2525 processor,
49 * (3) AD9954 DDS support functions,
50 * (4) added comments and formatting for doxygen,
51 * (5) process GPS data with native Motorola protocol,
52 * (6) generate plain text $GPGGA and $GPRMC messages,
53 * (7) power down GPS 5 hours after lock,
54 * (8) added flight data recorder, and
55 * (9) added diagnostics terminal mode.
58 * @subsection v201 V2.01
59 * 30 Jan 2004, Change include; (1) General clean up of in-line documentation, and
60 * (2) changed temperature resolution to 0.1 degrees F.
63 * @subsection v200 V2.00
64 * 26 Oct 2002, Change include; (1) Micro Beacon II hardware changes including PIC18F252 processor,
66 * (3) GPS power control,
67 * (4) additional ADC input, and
68 * (5) LM60 temperature sensor.
71 * @subsection v101 V1.01
72 * 5 Dec 2001, Change include; (1) Changed startup message, and
73 * (2) applied SEPARATE pragma to several methods for memory usage.
76 * @subsection v100 V1.00
77 * 25 Sep 2001, Initial release. Flew ANSR-3 and ANSR-4.
83 * @section copyright_sec Copyright
85 * Copyright (c) 2001-2009 Michael Gray, KD7LMO
90 * @section gpl_sec GNU General Public License
92 * This program is free software; you can redistribute it and/or modify
93 * it under the terms of the GNU General Public License as published by
94 * the Free Software Foundation; either version 2 of the License, or
95 * (at your option) any later version.
97 * This program is distributed in the hope that it will be useful,
98 * but WITHOUT ANY WARRANTY; without even the implied warranty of
99 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
100 * GNU General Public License for more details.
102 * You should have received a copy of the GNU General Public License
103 * along with this program; if not, write to the Free Software
104 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
110 * @section design Design Details
112 * Provides design details on a variety of the components that make up the Pico Beacon.
118 * @page power Power Consumption
120 * Measured DC power consumption.
122 * 3VDC prime power current
127 * 18mA Processor running, all I/O off
131 * 120mA GPS running w/antenna
133 * 250mA DDS running and GPS w/antenna
135 * 420mA DDS running, GPS w/antenna, and PA chain on with no RF
149 typedef int32_t int32;
153 // Public methods, constants, and data structures for each class.
155 /// Operational modes of the AD9954 DDS for the ddsSetMode function.
158 /// Device has not been initialized.
159 DDS_MODE_NOT_INITIALIZED,
161 /// Device in lowest power down mode.
164 /// Generate FM modulated audio tones.
167 /// Generate true FSK tones.
172 void ddsSetAmplitude (uint8_t amplitude);
173 void ddsSetOutputScale (uint16_t amplitude);
174 void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1);
175 void ddsSetFreq (uint32_t freq);
176 void ddsSetFTW (uint32_t ftw);
177 void ddsSetMode (DDS_MODE mode);
185 /// 2D (Latitude/Longitude) fix.
188 /// 3D (Latitude/Longitude/Altitude) fix.
192 /// GPS Position information.
195 /// Flag that indicates the position information has been updated since it was last checked.
198 /// Month in UTC time.
201 /// Day of month in UTC time.
204 /// Hours in UTC time.
207 /// Minutes in UTC time.
210 /// Seconds in UTC time.
213 /// Year in UTC time.
216 /// Latitude in milli arc-seconds where + is North, - is South.
219 /// Longitude in milli arc-seconds where + is East, - is West.
225 /// Calculated altitude in feet
226 int32_t altitudeFeet;
228 /// 3D speed in cm/second.
231 /// 2D speed in cm/second.
234 /// Heading units of 0.1 degrees.
237 /// DOP (Dilution of Precision)
240 /// 16-bit number that represents status of GPS engine.
243 /// Number of tracked satellites used in the fix position.
246 /// Number of visible satellites.
248 } GPSPOSITION_STRUCT;
250 GPSPOSITION_STRUCT gpsPosition;
254 GPS_FIX_TYPE gpsGetFixType();
255 int32_t gpsGetPeakAltitude();
260 uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc);
262 uint8_t timeGetTicks();
264 void timeSetDutyCycle (uint8_t dutyCycle);
267 /// Operational modes of the TNC for the tncSetMode function.
270 /// No operation waiting for setup and configuration.
273 /// 1200 bps using A-FSK (Audio FSK) tones.
276 /// 9600 bps using true FSK tones.
282 void tncHighRate(bool_t state);
283 void tncSetMode (TNC_DATA_MODE dataMode);
284 void tnc1200TimerTick();
285 void tnc9600TimerTick();
286 void tncTxByte (uint8_t value);
287 void tncTxPacket(TNC_DATA_MODE dataMode);
292 * @defgroup DDS AD9954 DDS (Direct Digital Synthesizer)
294 * Functions to control the Analog Devices AD9954 DDS.
299 /// AD9954 CFR1 - Control functions including RAM, profiles, OSK, sync, sweep, SPI, and power control settings.
300 #define DDS_AD9954_CFR1 0x00
302 /// AD9954 CFR2 - Control functions including sync, PLL multiplier, VCO range, and charge pump current.
303 #define DDS_AD9954_CFR2 0x01
305 /// AD9954 ASF - Auto ramp rate speed control and output scale factor (0x0000 to 0x3fff).
306 #define DDS_AD9954_ASF 0x02
308 /// AD9954 ARR - Amplitude ramp rate for OSK function.
309 #define DDS_AD9954_ARR 0x03
311 /// AD9954 FTW0 - Frequency tuning word 0.
312 #define DDS_AD9954_FTW0 0x04
314 /// AD9954 FTW1 - Frequency tuning word 1
315 #define DDS_AD9954_FTW1 0x06
317 /// AD9954 NLSCW - Negative Linear Sweep Control Word used for spectral shaping in FSK mode
318 #define DDS_AD9954_NLSCW 0x07
320 /// AD9954 PLSCW - Positive Linear Sweep Control Word used for spectral shaping in FSK mode
321 #define DDS_AD9954_PLSCW 0x08
323 /// AD9954 RSCW0 - RAM Segment Control Word 0
324 #define DDS_AD9954_RWCW0 0x07
326 /// AD9954 RSCW0 - RAM Segment Control Word 1
327 #define DDS_AD9954_RWCW1 0x08
329 /// AD9954 RAM segment
332 /// Current operational mode.
335 /// Number of digits in DDS frequency to FTW conversion.
336 #define DDS_FREQ_TO_FTW_DIGITS 9
338 /// Array of multiplication factors used to convert frequency to the FTW.
339 const uint32_t DDS_MULT[DDS_FREQ_TO_FTW_DIGITS] = { 11, 7, 7, 3, 4, 8, 4, 9, 1 };
341 /// Array of divisors used to convert frequency to the FTW.
342 const uint32_t DDS_DIVISOR[DDS_FREQ_TO_FTW_DIGITS - 1] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
344 /// Lookup table to convert dB amplitude scale in 0.5 steps to a linear DDS scale factor.
345 const uint16_t DDS_AMP_TO_SCALE[] =
347 16383, 15467, 14601, 13785, 13013, 12286, 11598, 10949, 10337, 9759, 9213, 8697,
348 8211, 7752, 7318, 6909, 6522, 6157, 5813, 5488, 5181, 4891, 4617, 4359, 4115, 3885, 3668, 3463,
349 3269, 3086, 2913, 2750, 2597, 2451, 2314, 2185, 2062, 1947, 1838, 1735, 1638
353 /// Frequency Word List - 4.0KHz FM frequency deviation at 81.15MHz (445.950MHz)
354 const uint32_t freqTable[256] =
356 955418300, 955419456, 955420611, 955421765, 955422916, 955424065, 955425210, 955426351,
357 955427488, 955428618, 955429743, 955430861, 955431971, 955433073, 955434166, 955435249,
358 955436322, 955437385, 955438435, 955439474, 955440500, 955441513, 955442511, 955443495,
359 955444464, 955445417, 955446354, 955447274, 955448176, 955449061, 955449926, 955450773,
360 955451601, 955452408, 955453194, 955453960, 955454704, 955455426, 955456126, 955456803,
361 955457457, 955458088, 955458694, 955459276, 955459833, 955460366, 955460873, 955461354,
362 955461809, 955462238, 955462641, 955463017, 955463366, 955463688, 955463983, 955464250,
363 955464489, 955464701, 955464884, 955465040, 955465167, 955465266, 955465337, 955465380,
364 955465394, 955465380, 955465337, 955465266, 955465167, 955465040, 955464884, 955464701,
365 955464489, 955464250, 955463983, 955463688, 955463366, 955463017, 955462641, 955462238,
366 955461809, 955461354, 955460873, 955460366, 955459833, 955459276, 955458694, 955458088,
367 955457457, 955456803, 955456126, 955455426, 955454704, 955453960, 955453194, 955452408,
368 955451601, 955450773, 955449926, 955449061, 955448176, 955447274, 955446354, 955445417,
369 955444464, 955443495, 955442511, 955441513, 955440500, 955439474, 955438435, 955437385,
370 955436322, 955435249, 955434166, 955433073, 955431971, 955430861, 955429743, 955428618,
371 955427488, 955426351, 955425210, 955424065, 955422916, 955421765, 955420611, 955419456,
372 955418300, 955417144, 955415989, 955414836, 955413684, 955412535, 955411390, 955410249,
373 955409113, 955407982, 955406857, 955405740, 955404629, 955403528, 955402435, 955401351,
374 955400278, 955399216, 955398165, 955397126, 955396100, 955395088, 955394089, 955393105,
375 955392136, 955391183, 955390246, 955389326, 955388424, 955387540, 955386674, 955385827,
376 955385000, 955384192, 955383406, 955382640, 955381896, 955381174, 955380474, 955379797,
377 955379143, 955378513, 955377906, 955377324, 955376767, 955376235, 955375728, 955375246,
378 955374791, 955374362, 955373959, 955373583, 955373234, 955372912, 955372618, 955372350,
379 955372111, 955371900, 955371716, 955371560, 955371433, 955371334, 955371263, 955371220,
380 955371206, 955371220, 955371263, 955371334, 955371433, 955371560, 955371716, 955371900,
381 955372111, 955372350, 955372618, 955372912, 955373234, 955373583, 955373959, 955374362,
382 955374791, 955375246, 955375728, 955376235, 955376767, 955377324, 955377906, 955378513,
383 955379143, 955379797, 955380474, 955381174, 955381896, 955382640, 955383406, 955384192,
384 955385000, 955385827, 955386674, 955387540, 955388424, 955389326, 955390246, 955391183,
385 955392136, 955393105, 955394089, 955395088, 955396100, 955397126, 955398165, 955399216,
386 955400278, 955401351, 955402435, 955403528, 955404629, 955405740, 955406857, 955407982,
387 955409113, 955410249, 955411390, 955412535, 955413684, 955414836, 955415989, 955417144
391 * Set DDS frequency tuning word. The output frequency is equal to RefClock * (ftw / 2 ^ 32).
393 * @param ftw Frequency Tuning Word
395 void ddsSetFTW (uint32_t ftw)
397 int x = ftw - freqTable[0];
398 putchar (x > 0 ? 0xc0 : 0x40);
402 * Convert frequency in hertz to 32-bit DDS FTW (Frequency Tune Word).
404 * @param freq frequency in Hertz
407 void ddsSetFreq(uint32_t freq)
412 // To avoid rounding errors with floating point math, we do a long multiply on the data.
413 ftw = freq * DDS_MULT[0];
415 for (i = 0; i < DDS_FREQ_TO_FTW_DIGITS - 1; ++i)
416 ftw += (freq * DDS_MULT[i+1]) / DDS_DIVISOR[i];
422 * Set DDS frequency tuning word for the FSK 0 and 1 values. The output frequency is equal
423 * to RefClock * (ftw / 2 ^ 32).
425 * @param ftw0 frequency tuning word for the FSK 0 value
426 * @param ftw1 frequency tuning word for the FSK 1 value
428 void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1)
430 // printf ("ftw0 %d ftw1 %d\n", ftw0, ftw1);
434 * Set the DDS to run in A-FSK, FSK, or PSK31 mode
436 * @param mode DDS_MODE_APRS, DDS_MODE_PSK31, or DDS_MODE_HF_APRS constant
438 void ddsSetMode (DDS_MODE mode)
440 // printf ("mode %d\n", mode);
446 * @defgroup GPS Motorola M12+ GPS Engine
448 * Functions to control the Motorola M12+ GPS engine in native binary protocol mode.
453 /// The maximum length of a binary GPS engine message.
454 #define GPS_BUFFER_SIZE 50
456 /// GPS parse engine state machine values.
459 /// 1st start character '@'
462 /// 2nd start character '@'
465 /// Upper case 'A' - 'Z' message type
468 /// Lower case 'a' - 'z' message type
471 /// 0 - xx bytes based on message type 'Aa'
477 /// End of message - Carriage Return
480 /// End of message - Line Feed
482 } GPS_PARSE_STATE_MACHINE;
484 /// Index into gpsBuffer used to store message data.
487 /// State machine used to parse the GPS message stream.
488 GPS_PARSE_STATE_MACHINE gpsParseState;
490 /// Buffer to store data as it is read from the GPS engine.
491 uint8_t gpsBuffer[GPS_BUFFER_SIZE];
493 /// Peak altitude detected while GPS is in 3D fix mode.
494 int32_t gpsPeakAltitude;
496 /// Checksum used to verify binary message from GPS engine.
499 /// Last verified GPS message received.
500 GPSPOSITION_STRUCT gpsPosition;
503 * Get the type of fix.
505 * @return gps fix type enumeration
507 GPS_FIX_TYPE gpsGetFixType()
509 // The upper 3-bits determine the fix type.
510 switch (gpsPosition.status & 0xe000)
524 * Peak altitude detected while GPS is in 3D fix mode since the system was booted.
526 * @return altitude in feet
528 int32_t gpsGetPeakAltitude()
530 return gpsPeakAltitude;
534 * Initialize the GPS subsystem.
538 // Initial parse state.
539 gpsParseState = GPS_START1;
541 // Assume we start at sea level.
544 // Clear the structure that stores the position message.
545 memset (&gpsPosition, 0, sizeof(GPSPOSITION_STRUCT));
547 // Setup the timers used to measure the 1-PPS time period.
548 // setup_timer_3(T3_INTERNAL | T3_DIV_BY_1);
549 // setup_ccp2 (CCP_CAPTURE_RE | CCP_USE_TIMER3);
553 * Determine if new GPS message is ready to process. This function is a one shot and
554 * typically returns true once a second for each GPS position fix.
556 * @return true if new message available; otherwise false
561 if (gpsPosition.updateFlag)
563 gpsPosition.updateFlag = false;
571 * Calculate NMEA-0183 message checksum of buffer that is length bytes long.
573 * @param buffer pointer to data buffer.
574 * @param length number of bytes in buffer.
576 * @return checksum of buffer
578 uint8_t gpsNMEAChecksum (uint8_t *buffer, uint8_t length)
584 for (i = 0; i < length; ++i)
585 checksum ^= buffer[i];
591 * Verify the GPS engine is sending the @@Hb position report message. If not,
592 * configure the GPS engine to send the desired report.
594 * @return true if GPS engine operation; otherwise false
598 uint8_t startTime, retryCount;
600 // We wait 10 seconds for the GPS engine to respond to our message request.
601 startTime = timeGetTicks();
604 while (++retryCount < 10)
606 // Read the serial FIFO and process the GPS messages.
609 // If a GPS data set is available, then GPS is operational.
612 // timeSetDutyCycle (TIME_DUTYCYCLE_10);
616 if (timeGetTicks() > startTime)
618 puts ("@@Hb\001\053\015\012");
628 * Parse the Motorola @@Hb (Short position/message) report.
630 void gpsParsePositionMessage()
632 // Convert the binary stream into data elements. We will scale to the desired units
633 // as the values are used.
634 gpsPosition.updateFlag = true;
636 gpsPosition.month = gpsBuffer[0];
637 gpsPosition.day = gpsBuffer[1];
638 gpsPosition.year = ((uint16_t) gpsBuffer[2] << 8) | gpsBuffer[3];
639 gpsPosition.hours = gpsBuffer[4];
640 gpsPosition.minutes = gpsBuffer[5];
641 gpsPosition.seconds = gpsBuffer[6];
642 gpsPosition.latitude = ((int32) gpsBuffer[11] << 24) | ((int32) gpsBuffer[12] << 16) | ((int32) gpsBuffer[13] << 8) | (int32) gpsBuffer[14];
643 gpsPosition.longitude = ((int32) gpsBuffer[15] << 24) | ((int32) gpsBuffer[16] << 16) | ((int32) gpsBuffer[17] << 8) | gpsBuffer[18];
644 gpsPosition.altitudeCM = ((int32) gpsBuffer[19] << 24) | ((int32) gpsBuffer[20] << 16) | ((int32) gpsBuffer[21] << 8) | gpsBuffer[22];
645 gpsPosition.altitudeFeet = gpsPosition.altitudeCM * 100l / 3048l;
646 gpsPosition.vSpeed = ((uint16_t) gpsBuffer[27] << 8) | gpsBuffer[28];
647 gpsPosition.hSpeed = ((uint16_t) gpsBuffer[29] << 8) | gpsBuffer[30];
648 gpsPosition.heading = ((uint16_t) gpsBuffer[31] << 8) | gpsBuffer[32];
649 gpsPosition.dop = ((uint16_t) gpsBuffer[33] << 8) | gpsBuffer[34];
650 gpsPosition.visibleSats = gpsBuffer[35];
651 gpsPosition.trackedSats = gpsBuffer[36];
652 gpsPosition.status = ((uint16_t) gpsBuffer[37] << 8) | gpsBuffer[38];
654 // Update the peak altitude if we have a valid 3D fix.
655 if (gpsGetFixType() == GPS_3D_FIX)
656 if (gpsPosition.altitudeFeet > gpsPeakAltitude)
657 gpsPeakAltitude = gpsPosition.altitudeFeet;
661 * Turn on the GPS engine power and serial interface.
665 // 3.0 VDC LDO control line.
666 // output_high (IO_GPS_PWR);
671 * Turn off the GPS engine power and serial interface.
675 // 3.0 VDC LDO control line.
676 // output_low (IO_GPS_PWR);
683 * @defgroup sys System Library Functions
685 * Generic system functions similiar to the run-time C library.
691 * Calculate the CRC-16 CCITT of buffer that is length bytes long.
692 * The crc parameter allow the calculation on the CRC on multiple buffers.
694 * @param buffer Pointer to data buffer.
695 * @param length number of bytes in data buffer
696 * @param crc starting value
698 * @return CRC-16 of buffer[0 .. length]
700 uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc)
702 uint8_t i, bit, value;
704 for (i = 0; i < length; ++i)
708 for (bit = 0; bit < 8; ++bit)
710 crc ^= (value & 0x01);
711 crc = ( crc & 0x01 ) ? ( crc >> 1 ) ^ 0x8408 : ( crc >> 1 );
722 * @defgroup rtc Real Time Interrupt tick
724 * Manage the built-in real time interrupt. The interrupt clock PRI is 104uS (9600 bps).
729 /// A counter that ticks every 100mS.
732 /// Counts the number of 104uS interrupts for a 100mS time period.
733 uint16_t timeInterruptCount;
735 /// Counts the number of 100mS time periods in 1 second.
738 /// System time in seconds.
741 /// System time in minutes.
744 /// System time in hours.
747 /// Desired LED duty cycle 0 to 9 where 0 = 0% and 9 = 90%.
748 uint8_t timeDutyCycle;
750 /// Current value of the timer 1 compare register used to generate 104uS interrupt rate (9600bps).
751 uint16_t timeCompare;
753 /// 16-bit NCO where the upper 8-bits are used to index into the frequency generation table.
756 /// Audio tone NCO update step (phase).
757 uint16_t timeNCOFreq;
759 /// Counter used to deciminate down from the 104uS to 833uS interrupt rate. (9600 to 1200 baud)
760 uint8_t timeLowRateCount;
762 /// Current TNC mode (standby, 1200bps A-FSK, or 9600bps FSK)
763 TNC_DATA_MODE tncDataMode;
765 /// Flag set true once per second.
766 bool_t timeUpdateFlag;
768 /// Flag that indicate the flight time should run.
771 /// The change in the CCP_1 register for each 104uS (9600bps) interrupt period.
772 #define TIME_RATE 125
775 * Running 8-bit counter that ticks every 100mS.
777 * @return 100mS time tick
779 uint8_t timeGetTicks()
785 * Initialize the real-time clock.
790 timeInterruptCount = 0;
795 timeCompare = TIME_RATE;
796 timeUpdateFlag = false;
798 timeLowRateCount = 0;
799 timeNCOFreq = 0x2000;
800 tncDataMode = TNC_MODE_STANDBY;
805 * Function return true once a second based on real-time clock.
807 * @return true on one second tick; otherwise false
809 bool_t timeIsUpdate()
813 timeUpdateFlag = false;
821 * Set a flag to indicate the flight time should run. This flag is typically set when the payload
824 void timeSetRunFlag()
830 * Timer interrupt handler called every 104uS (9600 times/second).
834 // Setup the next interrupt for the operational mode.
835 timeCompare += TIME_RATE;
836 // CCP_1 = timeCompare;
840 case TNC_MODE_STANDBY:
843 case TNC_MODE_1200_AFSK:
844 ddsSetFTW (freqTable[timeNCO >> 8]);
846 timeNCO += timeNCOFreq;
848 if (++timeLowRateCount == 8)
850 timeLowRateCount = 0;
855 case TNC_MODE_9600_FSK:
864 * @defgroup tnc TNC (Terminal Node Controller)
866 * Functions that provide a subset of the TNC functions.
871 /// The number of start flag bytes to send before the packet message. (360bits * 1200bps = 300mS)
872 #define TNC_TX_DELAY 45
874 /// The size of the TNC output buffer.
875 #define TNC_BUFFER_SIZE 80
877 /// States that define the current mode of the 1200 bps (A-FSK) state machine.
880 /// Stand by state ready to accept new message.
883 /// 0x7E bit stream pattern used to define start of APRS message.
886 /// Transmit the AX.25 header that contains the source/destination call signs, APRS path, and flags.
889 /// Transmit the message data.
892 /// Transmit the end flag sequence.
894 } TNC_TX_1200BPS_STATE;
896 /// Enumeration of the messages we can transmit.
899 /// Startup message that contains software version information.
902 /// Plain text status message.
905 /// Message that contains GPS NMEA-0183 $GPGGA message.
908 /// Message that contains GPS NMEA-0183 $GPRMC message.
912 /// AX.25 compliant packet header that contains destination, station call sign, and path.
913 /// 0x76 for SSID-11, 0x78 for SSID-12
914 uint8_t TNC_AX25_HEADER[30] = {
915 'A' << 1, 'P' << 1, 'R' << 1, 'S' << 1, ' ' << 1, ' ' << 1, 0x60, \
916 'K' << 1, 'D' << 1, '7' << 1, 'S' << 1, 'Q' << 1, 'G' << 1, 0x76, \
917 'G' << 1, 'A' << 1, 'T' << 1, 'E' << 1, ' ' << 1, ' ' << 1, 0x60, \
918 'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '3' << 1, ' ' << 1, 0x67, \
922 /// The next bit to transmit.
925 /// Current mode of the 1200 bps state machine.
926 TNC_TX_1200BPS_STATE tncMode;
928 /// Counter for each bit (0 - 7) that we are going to transmit.
931 /// A shift register that holds the data byte as we bit shift it for transmit.
934 /// Index into the APRS header and data array for each byte as we transmit it.
937 /// The number of bytes in the message portion of the AX.25 message.
940 /// A copy of the last 5 bits we've transmitted to determine if we need to bit stuff on the next bit.
943 /// Pointer to TNC buffer as we save each byte during message preparation.
944 uint8_t *tncBufferPnt;
946 /// The type of message to tranmit in the next packet.
947 TNC_MESSAGE_TYPE tncPacketType;
949 /// Buffer to hold the message portion of the AX.25 packet as we prepare it.
950 uint8_t tncBuffer[TNC_BUFFER_SIZE];
952 /// Flag that indicates we want to transmit every 5 seconds.
953 bool_t tncHighRateFlag;
956 * Initialize the TNC internal variables.
961 tncMode = TNC_TX_READY;
962 tncPacketType = TNC_BOOT_MESSAGE;
963 tncHighRateFlag = false;
967 * Determine if the hardware if ready to transmit a 1200 baud packet.
969 * @return true if ready; otherwise false
973 if (tncMode == TNC_TX_READY)
979 void tncHighRate(bool_t state)
981 tncHighRateFlag = state;
985 * Configure the TNC for the desired data mode.
987 * @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK
989 void tncSetMode(TNC_DATA_MODE dataMode)
993 case TNC_MODE_1200_AFSK:
994 ddsSetMode (DDS_MODE_AFSK);
997 case TNC_MODE_9600_FSK:
998 ddsSetMode (DDS_MODE_FSK);
1000 // FSK tones at 445.947 and 445.953 MHz
1001 ddsSetFSKFreq (955382980, 955453621);
1003 case TNC_MODE_STANDBY:
1007 tncDataMode = dataMode;
1011 * Determine if the seconds value timeSeconds is a valid time slot to transmit
1012 * a message. Time seconds is in UTC.
1014 * @param timeSeconds UTC time in seconds
1016 * @return true if valid time slot; otherwise false
1018 bool_t tncIsTimeSlot (uint8_t timeSeconds)
1020 if (tncHighRateFlag)
1022 if ((timeSeconds % 5) == 0)
1028 switch (timeSeconds)
1042 * Method that is called every 833uS to transmit the 1200bps A-FSK data stream.
1043 * The provides the pre and postamble as well as the bit stuffed data stream.
1045 void tnc1200TimerTick()
1047 // Set the A-FSK frequency.
1048 if (tncTxBit == 0x00)
1049 timeNCOFreq = 0x2000;
1051 timeNCOFreq = 0x3aab;
1056 // Generate a test signal alteranting between high and low tones.
1057 tncTxBit = (tncTxBit == 0 ? 1 : 0);
1061 // The variable tncShift contains the lastest data byte.
1062 // NRZI enocde the data stream.
1063 if ((tncShift & 0x01) == 0x00) {
1070 // When the flag is done, determine if we need to send more or data.
1071 if (++tncBitCount == 8)
1076 // Once we transmit x mS of flags, send the data.
1077 // txDelay bytes * 8 bits/byte * 833uS/bit = x mS
1078 if (++tncIndex == TNC_TX_DELAY)
1081 tncShift = TNC_AX25_HEADER[0];
1083 tncMode = TNC_TX_HEADER;
1086 tncShift = tncShift >> 1;
1090 // Determine if we have sent 5 ones in a row, if we have send a zero.
1091 if (tncBitStuff == 0x1f)
1102 // The variable tncShift contains the lastest data byte.
1103 // NRZI enocde the data stream.
1104 if ((tncShift & 0x01) == 0x00) {
1111 // Save the data stream so we can determine if bit stuffing is
1112 // required on the next bit time.
1113 tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
1115 // If all the bits were shifted, get the next byte.
1116 if (++tncBitCount == 8)
1120 // After the header is sent, then send the data.
1121 if (++tncIndex == sizeof(TNC_AX25_HEADER))
1124 tncShift = tncBuffer[0];
1125 tncMode = TNC_TX_DATA;
1127 tncShift = TNC_AX25_HEADER[tncIndex];
1130 tncShift = tncShift >> 1;
1135 // Determine if we have sent 5 ones in a row, if we have send a zero.
1136 if (tncBitStuff == 0x1f)
1147 // The variable tncShift contains the lastest data byte.
1148 // NRZI enocde the data stream.
1149 if ((tncShift & 0x01) == 0x00) {
1156 // Save the data stream so we can determine if bit stuffing is
1157 // required on the next bit time.
1158 tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
1160 // If all the bits were shifted, get the next byte.
1161 if (++tncBitCount == 8)
1165 // If everything was sent, transmit closing flags.
1166 if (++tncIndex == tncLength)
1170 tncMode = TNC_TX_END;
1172 tncShift = tncBuffer[tncIndex];
1175 tncShift = tncShift >> 1;
1180 // The variable tncShift contains the lastest data byte.
1181 // NRZI enocde the data stream.
1182 if ((tncShift & 0x01) == 0x00) {
1189 // If all the bits were shifted, get the next one.
1190 if (++tncBitCount == 8)
1195 // Transmit two closing flags.
1196 if (++tncIndex == 2)
1198 tncMode = TNC_TX_READY;
1200 // Tell the TNC time interrupt to stop generating the frequency words.
1201 tncDataMode = TNC_MODE_STANDBY;
1204 // output_low (IO_OSK);
1205 // output_low (IO_PTT);
1206 ddsSetMode (DDS_MODE_POWERDOWN);
1211 tncShift = tncShift >> 1;
1218 * Method that is called every 104uS to transmit the 9600bps FSK data stream.
1220 void tnc9600TimerTick()
1226 * Write character to the TNC buffer. Maintain the pointer
1227 * and length to the buffer. The pointer tncBufferPnt and tncLength
1228 * must be set before calling this function for the first time.
1230 * @param character to save to telemetry buffer
1232 void tncTxByte (uint8_t character)
1234 *tncBufferPnt++ = character;
1239 tncPrintf(char *fmt, ...)
1245 c = vsprintf((char *) tncBufferPnt, fmt, ap);
1247 fprintf (stderr, "\n");
1249 vfprintf(stderr, fmt, ap);
1256 * Generate the plain text position packet. Data is written through the tncTxByte
1259 void tncPositionPacket(void)
1261 int32_t latitude = 45.4694766 * 10000000;
1262 int32_t longitude = -122.7376250 * 10000000;
1263 uint32_t altitude = 10000;
1271 char lat_sign = 'N', lon_sign = 'E';
1273 // tncPrintf (">ANSR ");
1276 latitude = -latitude;
1279 if (longitude < 0) {
1281 longitude = -longitude;
1284 lat_deg = latitude / 10000000;
1285 latitude -= lat_deg * 10000000;
1287 lat_min = latitude / 10000000;
1288 latitude -= lat_min * 10000000;
1289 lat_frac = (latitude + 50000) / 100000;
1291 lon_deg = longitude / 10000000;
1292 longitude -= lon_deg * 10000000;
1294 lon_min = longitude / 10000000;
1295 longitude -= lon_min * 10000000;
1296 lon_frac = (longitude + 50000) / 100000;
1298 tncPrintf ("=%02u%02u.%02u%c\\%03u%02u.%02u%cO",
1299 lat_deg, lat_min, lat_frac, lat_sign,
1300 lon_deg, lon_min, lon_frac, lon_sign);
1302 tncPrintf (" /A=%06u", altitude * 100 / 3048);
1306 * Prepare an AX.25 data packet. Each time this method is called, it automatically
1307 * rotates through 1 of 3 messages.
1309 * @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK
1311 void tncTxPacket(TNC_DATA_MODE dataMode)
1315 // Only transmit if there is not another message in progress.
1316 if (tncMode != TNC_TX_READY)
1319 // Configure the DDS for the desired operational.
1320 tncSetMode (dataMode);
1322 // Set a pointer to our TNC output buffer.
1323 tncBufferPnt = tncBuffer;
1325 // Set the message length counter.
1328 tncPositionPacket();
1330 // Add the end of message character.
1333 // Calculate the CRC for the header and message.
1334 crc = sysCRC16(TNC_AX25_HEADER, sizeof(TNC_AX25_HEADER), 0xffff);
1335 crc = sysCRC16(tncBuffer, tncLength, crc ^ 0xffff);
1337 // Save the CRC in the message.
1338 *tncBufferPnt++ = crc & 0xff;
1339 *tncBufferPnt = (crc >> 8) & 0xff;
1341 // Update the length to include the CRC bytes.
1344 // Prepare the variables that are used in the real-time clock interrupt.
1349 tncMode = TNC_TX_SYNC;
1351 // Turn on the PA chain.
1352 // output_high (IO_PTT);
1354 // Wait for the PA chain to power up.
1358 // output_high (IO_OSK);
1360 // Log the battery and reference voltage just after we key the transmitter.
1362 while (tncMode != TNC_TX_READY)