altos: Add Pico Beacon code as ao_aprs.c
[fw/altos] / src / drivers / ao_aprs.c
1 /** 
2  * http://ad7zj.net/kd7lmo/aprsbeacon_code.html
3  *
4  * @mainpage Pico Beacon
5  *
6  * @section overview_sec Overview
7  *
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.
11
12
13  *
14  * @section history_sec Revision History
15  *
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.
18  *
19  *
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.
25  *
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.
34  *
35  *
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.
40  *
41  * 
42  * @subsection v301 V3.01
43  * 13 Jan 2005, Renamed project and files to Pico Beacon.
44  *
45  *
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.
56  *
57  * 
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.
61  *
62  * 
63  * @subsection v200 V2.00
64  * 26 Oct 2002, Change include; (1) Micro Beacon II hardware changes including PIC18F252 processor,
65  *                                     (2) serial EEPROM, 
66  *                                     (3) GPS power control, 
67  *                                     (4) additional ADC input, and 
68  *                                     (5) LM60 temperature sensor.                            
69  *
70  *
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.
74  *
75  *
76  * @subsection v100 V1.00
77  * 25 Sep 2001, Initial release.  Flew ANSR-3 and ANSR-4.
78  * 
79
80
81  *
82  *
83  * @section copyright_sec Copyright
84  *
85  * Copyright (c) 2001-2009 Michael Gray, KD7LMO
86
87
88  *
89  *
90  * @section gpl_sec GNU General Public License
91  *
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.
96  *
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.
101  *
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
105  *  
106
107
108  * 
109  * 
110  * @section design Design Details
111  *
112  * Provides design details on a variety of the components that make up the Pico Beacon.
113  *
114  *  @subpage power
115  */
116
117 /**
118  *  @page power Power Consumption
119  *
120  *  Measured DC power consumption.
121  * 
122  *  3VDC prime power current 
123
124  *
125  *    7mA Held in reset 
126
127  *   18mA Processor running, all I/O off 
128
129  *  110mA GPS running 
130
131  *  120mA GPS running w/antenna 
132
133  *  250mA DDS running and GPS w/antenna 
134
135  *  420mA DDS running, GPS w/antenna, and PA chain on with no RF 
136
137  *  900mA Transmit 
138
139  *
140  */
141
142 // Hardware specific configuration.
143 #include <18f2525.h>
144 #device ADC=10
145
146 // NOTE: Even though we are using an external clock, we set the HS oscillator mode to
147 //       make the PIC 18F252 work with our external clock which is a clipped 1V P-P sine wave.
148 #fuses HS,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOLVP
149
150 // C runtime library definitions.
151 #include 
152 #include 
153
154 // These compiler directives set the clock, SPI/I2C ports, and I/O configuration.
155
156 // TCXO frequency
157 #use delay(clock=19200000)
158
159 // Engineering and data extracation port.
160 #use rs232(baud=57600, xmit=PIN_B7, rcv=PIN_B6, STREAM=PC_HOST)
161
162 // GPS engine 
163 #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
164
165 #use i2c (master, scl=PIN_C3, sda=PIN_C4)
166
167 #use fast_io(A)
168 #use fast_io(B)
169 #use fast_io(C)
170
171 // We define types that are used for all variables.  These are declared
172 // because each processor has a different sizes for int and long.
173 // The PIC compiler defines int8_t, int16_t, and int32_t.
174
175 /// Boolean value { false, true }
176 typedef boolean bool_t;
177
178 /// Signed 8-bit number in the range -128 through 127.
179 typedef signed int8 int8_t;
180
181 /// Unsigned 8-bit number in the range 0 through 255.
182 typedef unsigned int8 uint8_t;
183
184 /// Signed 16-bit number in the range -32768 through 32767.
185 typedef signed int16 int16_t;
186
187 /// Unsigned 16-bit number in the range 0 through 65535.
188 typedef unsigned int16 uint16_t;
189
190 /// Signed 32-bit number in the range -2147483648 through 2147483647.
191 typedef signed int32 int32_t;
192
193 /// Unsigned 32-bit number in the range 0 through 4294967296.
194 typedef unsigned int32 uint32_t;
195
196 // Function and structure prototypes.  These are declared at the start of
197 // the file much like a C++ header file.
198
199 // Map I/O pin names to hardware pins.
200
201 /// Heartbeat LED - Port A2
202 #define IO_LED PIN_A2
203
204 /// AD9954 DDS Profile Select 0 - Port A3
205 #define IO_PS0 PIN_A3
206
207 /// UHF amplifier and PA chain - Port A4
208 #define IO_PTT PIN_A4
209
210 /// AD9954 DDS Update - Port A5
211 #define IO_UPDATE PIN_A5
212
213 /// AD9954 CS (Chip Select) - Port B0
214 #define IO_CS PIN_B0
215
216 /// GPS Engine Power - Port B1
217 #define IO_GPS_PWR PIN_B1
218
219 /// AD9954 DDS Profile Select 1 - Port C0
220 #define IO_PS1 PIN_C0
221
222 /// AD9954 DDS OSK (Output Shift Key) - Port C2
223 #define IO_OSK PIN_C2
224
225 /// GPS engine serial transmit pin - Port C6
226 #define IO_GPS_TXD PIN_C6
227
228 // Public methods, constants, and data structures for each class.
229
230 /// Operational modes of the AD9954 DDS for the ddsSetMode function.
231 enum DDS_MODE
232 {
233     /// Device has not been initialized.
234     DDS_MODE_NOT_INITIALIZED,
235
236     /// Device in lowest power down mode.
237     DDS_MODE_POWERDOWN,
238
239     /// Generate FM modulated audio tones.
240     DDS_MODE_AFSK,
241
242     /// Generate true FSK tones.
243     DDS_MODE_FSK
244 };
245
246 void ddsInit();
247 void ddsSetAmplitude (uint8_t amplitude);
248 void ddsSetOutputScale (uint16_t amplitude);
249 void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1);
250 void ddsSetFreq (uint32_t freq);
251 void ddsSetFTW (uint32_t ftw);
252 void ddsSetMode (DDS_MODE mode);
253
254 void flashErase();
255 uint8_t flashGetByte ();
256 void flashReadBlock(uint32_t address, uint8_t *block, uint16_t length);
257 void flashSendByte(uint8_t value);
258 void flashSendAddress(uint32_t address);
259 void flashWriteBlock(uint32_t address, uint8_t *block, uint8_t length);
260
261 /// Type of GPS fix.
262 enum GPS_FIX_TYPE 
263 {
264     /// No GPS FIX
265     GPS_NO_FIX,
266
267     /// 2D (Latitude/Longitude) fix.
268     GPS_2D_FIX,
269
270     /// 3D (Latitude/Longitude/Altitude) fix.
271     GPS_3D_FIX
272 };
273
274 /// GPS Position information.
275 typedef struct 
276 {
277     /// Flag that indicates the position information has been updated since it was last checked.
278     bool_t updateFlag;
279
280     /// Month in UTC time.
281     uint8_t month;
282
283     /// Day of month in UTC time.
284     uint8_t day;
285
286     /// Hours in UTC time.
287     uint8_t hours;
288
289     /// Minutes in UTC time.
290     uint8_t minutes;
291
292     /// Seconds in UTC time.
293     uint8_t seconds;
294
295     /// Year in UTC time.
296     uint16_t year;
297
298     /// Latitude in milli arc-seconds where + is North, - is South.
299     int32_t latitude;
300
301     /// Longitude in milli arc-seconds where + is East, - is West.
302     int32_t longitude;
303
304     /// Altitude in cm
305     int32_t altitudeCM;
306
307     /// Calculated altitude in feet
308     int32_t altitudeFeet;
309
310     /// 3D speed in cm/second.
311     uint16_t vSpeed;
312
313     /// 2D speed in cm/second.
314     uint16_t hSpeed;
315
316     /// Heading units of 0.1 degrees.
317     uint16_t heading;
318
319     /// DOP (Dilution of Precision)
320     uint16_t dop;
321
322     /// 16-bit number that represents status of GPS engine.
323     uint16_t status;
324
325     /// Number of tracked satellites used in the fix position.
326     uint8_t trackedSats;
327
328     /// Number of visible satellites.
329     uint8_t visibleSats;
330 } GPSPOSITION_STRUCT;
331
332 void gpsInit();
333 bool_t gpsIsReady();
334 GPS_FIX_TYPE gpsGetFixType();
335 int32_t gpsGetPeakAltitude();
336 void gpsPowerOn();
337 bool_t gpsSetup();
338 void gpsUpdate();
339
340 int16_t lm92GetTemp();
341
342 /// Define the log record types.
343 enum LOG_TYPE 
344 {
345     /// Time stamp the log was started.
346     LOG_BOOTED = 0xb4,
347
348     /// GPS coordinates.
349     LOG_COORD = 0xa5,
350
351     /// Temperature
352     LOG_TEMPERATURE = 0x96,
353
354     /// Bus voltage.
355     LOG_VOLTAGE = 0x87
356 };
357
358 void logInit();
359 uint32_t logGetAddress();
360 void logType (LOG_TYPE type);
361 void logUint8 (uint8_t value);
362 void logInt16 (int16_t value);
363
364 bool_t serialHasData();
365 void serialInit();
366 uint8_t serialRead();
367 void serialUpdate();
368
369 uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc);
370 void sysInit();
371 void sysLogVoltage();
372
373 /// 0% duty cycle (LED Off) constant for function timeSetDutyCycle
374 #define TIME_DUTYCYCLE_0 0
375
376 /// 10% duty cycle constant for function timeSetDutyCycle
377 #define TIME_DUTYCYCLE_10 1
378
379 /// 70% duty cycle constant for function timeSetDutyCycle
380 #define TIME_DUTYCYCLE_70 7
381
382 uint8_t timeGetTicks();
383 void timeInit();
384 void timeSetDutyCycle (uint8_t dutyCycle);
385 void timeUpdate();
386
387 /// Operational modes of the TNC for the tncSetMode function.
388 enum TNC_DATA_MODE
389 {
390     /// No operation waiting for setup and configuration.
391     TNC_MODE_STANDBY, 
392
393     /// 1200 bps using A-FSK (Audio FSK) tones.
394     TNC_MODE_1200_AFSK,
395
396     /// 9600 bps using true FSK tones.
397     TNC_MODE_9600_FSK
398 };
399
400 void tncInit();
401 bool_t tncIsFree();
402 void tncHighRate(bool_t state);
403 void tncSetMode (TNC_DATA_MODE dataMode);
404 void tnc1200TimerTick();
405 void tnc9600TimerTick();
406 void tncTxByte (uint8_t value);
407 void tncTxPacket(TNC_DATA_MODE dataMode);
408
409 /**
410  *  @defgroup ADC Analog To Digital Converter
411  *
412  *  Control and manage the on board PIC A/D converter.
413  *
414  *  @{
415  */
416
417 /// Filtered voltages using a single pole, low pass filter.
418 uint16_t adcMainBusVolt;
419
420 /// PIC ADC Channel number of the reference voltage.
421 #define ADC_REF 0
422
423 /// PIC ADC Channel number of the main bus voltage.
424 #define ADC_MAINBUS 1
425
426 /// Input diode drop in units of 0.01 volts.
427 #define MAIN_BUS_VOLT_OFFSET 20
428
429 /**
430  *  Intialize the ADC subsystem.
431  */
432 void adcInit()
433 {
434     // Setup the ADC.
435     setup_adc_ports(AN0_TO_AN1);
436     setup_adc( ADC_CLOCK_DIV_32 );
437
438     // Zero the ADC filters.
439     adcMainBusVolt = 0;
440 }
441
442 /**
443  *   Filtered main bus voltage in 10mV resolution.
444  *
445  *   @return voltage in 10mV steps
446  */
447 uint16_t adcGetMainBusVolt()
448 {
449     uint32_t volts;
450
451     volts = (uint32_t) (adcMainBusVolt >> 3);
452
453     volts = (volts * 330l) / 1023l;
454
455     return (uint16_t) volts + MAIN_BUS_VOLT_OFFSET;
456 }
457
458 /** 
459  *   Get the current ADC value for the main bus voltage.
460  *
461  *   @return ADC value in the range 0 to 1023
462  */
463 uint16_t adcRawBusVolt()
464 {
465     set_adc_channel(ADC_MAINBUS);
466     delay_us(50);
467     return read_adc();
468 }
469
470 /** 
471  *   Get the current ADC value for the reference source voltage.
472  *
473  *   @return ADC value in the range 0 to 1023
474  */
475 uint16_t adcRawRefVolt()
476 {
477     set_adc_channel(ADC_REF);
478     delay_us(50);
479     return read_adc();
480 }
481
482 /**
483  *   Read and filter the ADC channels for bus voltages.
484  */
485 void adcUpdate(void)
486 {
487     // Filter the bus voltage using a single pole low pass filter.
488     set_adc_channel(ADC_MAINBUS);
489     delay_us(50);
490     adcMainBusVolt = read_adc() + adcMainBusVolt - (adcMainBusVolt >> 3);
491 }
492
493 /** @} */
494
495
496 /**
497  *  @defgroup diag Diagnostics and Control
498  *
499  *  Functions for diagnostics and control of the hardware and flight data recorder.
500  *
501  *  @{
502  */
503
504 /// Number of bytes per line to display when reading flight data recorder.
505 #define DIAG_BYTES_PER_LINE 32
506
507 /**
508  *   Process the command to erase the data logger flash.
509  */
510 void diagEraseFlash()
511 {
512     // Confirm we want to erase the flash with the key sequence 'yes' .
513     fprintf (PC_HOST, "Are you sure (yes)?  ");
514
515     if (fgetc(PC_HOST) != 'y')
516         return;
517
518     if (fgetc(PC_HOST) != 'e')
519         return;
520
521     if (fgetc(PC_HOST) != 's')
522         return;
523
524     if (fgetc(PC_HOST) != 13)
525         return;
526
527     // User feedback and erase the part.
528     fprintf (PC_HOST, "Erasing flash...");
529
530     flashErase();
531
532     fprintf (PC_HOST, "done.\n\r");
533 }
534
535 /**
536  *   Display the engineering mode menu.
537  */
538 void diagMenu()
539 {
540     // User interface.
541     fprintf (PC_HOST, "Options: (e)rase Flash, (r)ead Flash\n\r");
542     fprintf (PC_HOST, "         Toggle (L)ED\n\r");
543     fprintf (PC_HOST, "         (P)TT - Push To Transmit\n\r");
544     fprintf (PC_HOST, "         (f)requencey down, (F)requency up - 1KHz step\n\r");
545     fprintf (PC_HOST, "         (c)hannel down, (C)hannel up - 25KHz step\n\r");
546     fprintf (PC_HOST, "         (a)mplitude down, (A)mplitude up - 0.5 dB steps\n\r");
547     fprintf (PC_HOST, "         e(x)it engineering mode\n\r");
548 }
549
550 /**
551  *   Process the command to dump the contents of the data logger flash.
552  */
553 void diagReadFlash()
554 {
555     bool_t dataFoundFlag, userStopFlag;
556     uint8_t i, buffer[DIAG_BYTES_PER_LINE];
557     uint32_t address;
558
559     // Set the initial conditions to read the flash.
560     address = 0x0000;
561     userStopFlag = false;
562
563     do 
564     {
565         // Read each block from the flash device.
566         flashReadBlock (address, buffer, DIAG_BYTES_PER_LINE);
567
568         // This flag will get set if any data byte is not equal to 0xff (erase flash state)
569         dataFoundFlag = false;
570
571         // Display the address.
572         fprintf (PC_HOST, "%08lx ", address);
573
574         // Display each byte in the line.
575         for (i = 0; i < DIAG_BYTES_PER_LINE; ++i) 
576         {
577             fprintf (PC_HOST, "%02x", buffer[i]);
578
579             // Set this flag if the cell is not erased.
580             if (buffer[i] != 0xff)
581                 dataFoundFlag = true;
582
583             // Any key will abort the transfer.
584             if (kbhit(PC_HOST))
585                 userStopFlag = true;
586         } // END for
587
588         //  at the end of each line.
589         fprintf (PC_HOST, "\n\r");
590
591         // Advance to the next block of memory.
592         address += DIAG_BYTES_PER_LINE;
593     } while (dataFoundFlag && !userStopFlag);
594
595     // Feedback to let the user know why the transfer stopped.
596     if (userStopFlag)
597         fprintf (PC_HOST, "User aborted download!\n\r");
598 }
599
600 void diag1PPS()
601 {
602     uint16_t timeStamp, lastTimeStamp;
603
604     lastTimeStamp = 0x0000;
605
606     gpsPowerOn();
607
608     for (;;)
609     {
610         timeStamp = CCP_2;
611
612         if (timeStamp != lastTimeStamp)
613         {
614             delay_ms (10);
615
616             timeStamp = CCP_2;
617
618             fprintf (PC_HOST, "%lu %lu\n\r", timeStamp, (timeStamp - lastTimeStamp));
619
620             lastTimeStamp = timeStamp;
621         }
622     }
623 }
624
625 /**
626  *   Process diagnostic commands through the debug RS-232 port.
627  */
628 void diagPort()
629 {
630     bool_t diagDoneFlag, ledFlag, paFlag, showSettingsFlag;
631     uint8_t command, amplitude;
632     uint32_t freqHz;
633
634     // If the input is low, we aren't connected to the RS-232 device so continue to boot.
635     if (!input(PIN_B6))
636         return;
637
638     fprintf (PC_HOST, "Engineering Mode\n\r");
639     fprintf (PC_HOST, "Application Built %s %s\n\r", __DATE__, __TIME__);
640
641     // Current state of the status LED.
642     ledFlag = false;
643     output_bit (IO_LED, ledFlag);
644
645     // This flag indicates we are ready to leave the diagnostics mode.
646     diagDoneFlag = false;
647
648     // Current state of the PA.
649     paFlag = false;
650
651     // Flag that indicate we should show the current carrier frequency.
652     showSettingsFlag = false;
653
654     // Set the initial carrier frequency and amplitude.
655     freqHz = 445950000;
656     amplitude = 0;
657
658     // Wait for the exit command.
659     while (!diagDoneFlag) 
660     {
661         // Wait for the user command.
662         command = fgetc(PC_HOST);
663
664         // Decode and process the key stroke.
665         switch (command) 
666         {
667             case 'e':
668                 diagEraseFlash();
669                 logInit();
670                 break;
671
672             case 'l':
673             case 'L':
674                 ledFlag = (ledFlag ? false : true);
675                 output_bit (IO_LED, ledFlag);
676                 break;
677
678             case 'h':
679             case 'H':
680             case '?':
681                 diagMenu();
682                 break;
683
684             case 'r':
685                 diagReadFlash();
686                 break;
687
688             case 't':
689                 tncHighRate (true);
690                 fprintf (PC_HOST, "Set high rate TNC.\n\r");    
691                 break;
692
693             case 'f':
694                 freqHz -= 1000;
695                 ddsSetFreq (freqHz);
696
697                 // Display the new frequency.
698                 showSettingsFlag = true;
699                 break;
700
701             case 'F':
702                 freqHz += 1000;
703                 ddsSetFreq (freqHz);
704
705                 // Display the new frequency.
706                 showSettingsFlag = true;
707                 break;
708
709             case 'c':
710                 freqHz -= 25000;
711                 ddsSetFreq (freqHz);
712
713                 // Display the new frequency.
714                 showSettingsFlag = true;
715                 break;
716
717             case 'C':
718                 freqHz += 25000;
719                 ddsSetFreq (freqHz);
720
721                 // Display the new frequency.
722                 showSettingsFlag = true;
723                 break;
724
725             case 'p':
726             case 'P':
727                 ddsSetFreq (freqHz);
728
729                 paFlag = (paFlag ? false : true);
730                 output_bit (IO_PTT, paFlag);
731                 output_bit (IO_OSK, paFlag);
732
733                 if (paFlag)
734                 {
735                     ddsSetMode (DDS_MODE_AFSK);             
736                     ddsSetAmplitude (amplitude);
737                 } else
738                     ddsSetMode (DDS_MODE_POWERDOWN);
739
740                 break;
741
742             case 'a':
743                 if (amplitude != 200)
744                 {
745                     amplitude += 5;
746                     ddsSetAmplitude (amplitude);
747
748                     // Display the new amplitude.
749                     showSettingsFlag = true;
750                 }
751                 break;
752
753             case 'A':
754                 if (amplitude != 0)
755                 {
756                     amplitude -= 5;
757                     ddsSetAmplitude (amplitude);
758
759                     // Display the new amplitude.
760                     showSettingsFlag = true;
761                 }
762                 break;
763
764             case 'g':
765                 diag1PPS();
766                 break;
767
768             case 'x':
769                 diagDoneFlag = true;
770                 break;
771
772             default:
773                 fprintf (PC_HOST, "Invalid command.  (H)elp for menu.\n\r");
774                 break;
775         } // END switch
776
777         // Display the results of any user requests or commands.
778         if (showSettingsFlag) 
779         {
780             showSettingsFlag = false;
781
782             fprintf (PC_HOST, "%03ld.%03ld MHz  ", freqHz / 1000000, (freqHz / 1000) % 1000);
783             fprintf (PC_HOST, "%d.%01ddBc\n\r", amplitude / 10, amplitude % 10);
784
785         } // END if
786
787     } // END while
788
789     // Let the user know we are done with this mode.
790     fprintf (PC_HOST, "Exit diagnostic mode.\n\r");
791
792     return;
793 }
794
795 /** @} */
796
797
798 /**
799  *  @defgroup DDS AD9954 DDS (Direct Digital Synthesizer)
800  *
801  *  Functions to control the Analog Devices AD9954 DDS.
802  *
803  *  @{
804  */
805
806 /// AD9954 CFR1 - Control functions including RAM, profiles, OSK, sync, sweep, SPI, and power control settings.
807 #define DDS_AD9954_CFR1 0x00
808
809 /// AD9954 CFR2 - Control functions including sync, PLL multiplier, VCO range, and charge pump current.
810 #define DDS_AD9954_CFR2 0x01
811
812 /// AD9954 ASF - Auto ramp rate speed control and output scale factor (0x0000 to 0x3fff).
813 #define DDS_AD9954_ASF 0x02
814
815 /// AD9954 ARR - Amplitude ramp rate for OSK function.
816 #define DDS_AD9954_ARR 0x03
817
818 /// AD9954 FTW0 - Frequency tuning word 0.
819 #define DDS_AD9954_FTW0 0x04
820
821 /// AD9954 FTW1 - Frequency tuning word 1
822 #define DDS_AD9954_FTW1 0x06
823
824 /// AD9954 NLSCW - Negative Linear Sweep Control Word used for spectral shaping in FSK mode
825 #define DDS_AD9954_NLSCW 0x07
826
827 /// AD9954 PLSCW - Positive Linear Sweep Control Word used for spectral shaping in FSK mode
828 #define DDS_AD9954_PLSCW 0x08
829
830 /// AD9954 RSCW0 - RAM Segment Control Word 0
831 #define DDS_AD9954_RWCW0 0x07
832
833 /// AD9954 RSCW0 - RAM Segment Control Word 1
834 #define DDS_AD9954_RWCW1 0x08
835
836 /// AD9954 RAM segment
837 #define DDS_RAM 0x0b
838
839 /// Current operational mode.
840 DDS_MODE ddsMode;
841
842 /// Number of digits in DDS frequency to FTW conversion.
843 #define DDS_FREQ_TO_FTW_DIGITS 9
844
845 /// Array of multiplication factors used to convert frequency to the FTW.
846 const uint32_t DDS_MULT[DDS_FREQ_TO_FTW_DIGITS] = { 11, 7, 7, 3, 4, 8, 4, 9, 1 };
847
848 /// Array of divisors used to convert frequency to the FTW.
849 const uint32_t DDS_DIVISOR[DDS_FREQ_TO_FTW_DIGITS - 1] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
850
851 /// Lookup table to convert dB amplitude scale in 0.5 steps to a linear DDS scale factor.
852 const uint16_t DDS_AMP_TO_SCALE[] = 
853
854     16383, 15467, 14601, 13785, 13013, 12286, 11598, 10949, 10337, 9759, 9213, 8697, 
855     8211, 7752, 7318, 6909, 6522, 6157, 5813, 5488, 5181, 4891, 4617, 4359, 4115, 3885, 3668, 3463, 
856     3269, 3086, 2913, 2750, 2597, 2451, 2314, 2185, 2062, 1947, 1838, 1735, 1638 
857 };
858
859
860 /// Frequency Word List - 4.0KHz FM frequency deviation at 81.15MHz  (445.950MHz)
861 const uint32_t freqTable[256] = 
862 {
863     955418300, 955419456, 955420611, 955421765, 955422916, 955424065, 955425210, 955426351, 
864     955427488, 955428618, 955429743, 955430861, 955431971, 955433073, 955434166, 955435249, 
865     955436322, 955437385, 955438435, 955439474, 955440500, 955441513, 955442511, 955443495, 
866     955444464, 955445417, 955446354, 955447274, 955448176, 955449061, 955449926, 955450773, 
867     955451601, 955452408, 955453194, 955453960, 955454704, 955455426, 955456126, 955456803, 
868     955457457, 955458088, 955458694, 955459276, 955459833, 955460366, 955460873, 955461354, 
869     955461809, 955462238, 955462641, 955463017, 955463366, 955463688, 955463983, 955464250, 
870     955464489, 955464701, 955464884, 955465040, 955465167, 955465266, 955465337, 955465380, 
871     955465394, 955465380, 955465337, 955465266, 955465167, 955465040, 955464884, 955464701, 
872     955464489, 955464250, 955463983, 955463688, 955463366, 955463017, 955462641, 955462238, 
873     955461809, 955461354, 955460873, 955460366, 955459833, 955459276, 955458694, 955458088, 
874     955457457, 955456803, 955456126, 955455426, 955454704, 955453960, 955453194, 955452408, 
875     955451601, 955450773, 955449926, 955449061, 955448176, 955447274, 955446354, 955445417, 
876     955444464, 955443495, 955442511, 955441513, 955440500, 955439474, 955438435, 955437385, 
877     955436322, 955435249, 955434166, 955433073, 955431971, 955430861, 955429743, 955428618, 
878     955427488, 955426351, 955425210, 955424065, 955422916, 955421765, 955420611, 955419456, 
879     955418300, 955417144, 955415989, 955414836, 955413684, 955412535, 955411390, 955410249, 
880     955409113, 955407982, 955406857, 955405740, 955404629, 955403528, 955402435, 955401351, 
881     955400278, 955399216, 955398165, 955397126, 955396100, 955395088, 955394089, 955393105, 
882     955392136, 955391183, 955390246, 955389326, 955388424, 955387540, 955386674, 955385827, 
883     955385000, 955384192, 955383406, 955382640, 955381896, 955381174, 955380474, 955379797, 
884     955379143, 955378513, 955377906, 955377324, 955376767, 955376235, 955375728, 955375246, 
885     955374791, 955374362, 955373959, 955373583, 955373234, 955372912, 955372618, 955372350, 
886     955372111, 955371900, 955371716, 955371560, 955371433, 955371334, 955371263, 955371220, 
887     955371206, 955371220, 955371263, 955371334, 955371433, 955371560, 955371716, 955371900, 
888     955372111, 955372350, 955372618, 955372912, 955373234, 955373583, 955373959, 955374362, 
889     955374791, 955375246, 955375728, 955376235, 955376767, 955377324, 955377906, 955378513, 
890     955379143, 955379797, 955380474, 955381174, 955381896, 955382640, 955383406, 955384192, 
891     955385000, 955385827, 955386674, 955387540, 955388424, 955389326, 955390246, 955391183, 
892     955392136, 955393105, 955394089, 955395088, 955396100, 955397126, 955398165, 955399216, 
893     955400278, 955401351, 955402435, 955403528, 955404629, 955405740, 955406857, 955407982, 
894     955409113, 955410249, 955411390, 955412535, 955413684, 955414836, 955415989, 955417144
895 };
896
897 /**
898  *   Initialize the DDS regsiters and RAM.
899  */
900 void ddsInit()
901 {
902     // Setup the SPI port for the DDS interface.    
903     setup_spi( SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_4 | SPI_XMIT_L_TO_H );    
904
905     // Set the initial DDS mode.  The ddsSetMode function uses this value to make the desired DDS selections. 
906     ddsMode = DDS_MODE_NOT_INITIALIZED;
907
908     // Set the DDS operational mode.
909     ddsSetMode (DDS_MODE_POWERDOWN);
910
911     // Set the output to full scale.
912     ddsSetOutputScale (0x3fff);
913
914     // CFR2 (Control Function Register No. 2)
915     output_low (IO_CS);
916     spi_write (DDS_AD9954_CFR2);
917
918     spi_write (0x00);     // Unused register bits
919     spi_write (0x00);
920     spi_write (0x9c);     // 19x reference clock multipler, high VCO range, nominal charge pump current
921     output_high (IO_CS);
922
923     // ARR (Amplitude Ramp Rate) to 15mS for OSK
924     output_low (IO_CS);
925     spi_write (DDS_AD9954_ARR);
926
927     spi_write (83);
928     output_high (IO_CS);
929
930     // Strobe the part so we apply the updates.
931     output_high (IO_UPDATE);
932     output_low (IO_UPDATE);
933 }
934
935 /**
936  *  Set DDS amplitude value in the range 0 to 16383 where 16383 is full scale.  This value is a 
937  *  linear multiplier and needs to be scale for RF output power in log scale.
938  *
939  *  @param scale in the range 0 to 16383
940  */
941 void ddsSetOutputScale (uint16_t scale)
942 {
943     // Set ASF (Amplitude Scale Factor)
944     output_low (IO_CS);
945     spi_write (DDS_AD9954_ASF);
946
947     spi_write ((scale >> 8) & 0xff);
948     spi_write (scale & 0xff);
949
950     output_high (IO_CS);
951     
952     // Strobe the DDS to set the amplitude.
953     output_high (IO_UPDATE);
954     output_low (IO_UPDATE);
955 }
956
957 /**
958  *   Set the DDS amplitude in units of dBc of full scale where 1 is 0.1 dB.  For example, a value of 30 is 3dBc
959  *   or a value of 85 is 8.5dBc.
960  *
961  *   @param amplitude in 0.1 dBc of full scale
962  */ 
963 void ddsSetAmplitude (uint8_t amplitude)
964 {
965     // Range limit based on the lookup table size.
966     if (amplitude > 200)
967         return;
968
969     // Set the linear DDS ASF (Amplitude Scale Factor) based on the dB lookup table.
970     ddsSetOutputScale (DDS_AMP_TO_SCALE[amplitude / 5]);
971
972     // Toggle the DDS output low and then high to force it to ramp to the new output level setting.
973     output_low (IO_OSK);
974     delay_ms(25); 
975
976     output_high (IO_OSK);
977     delay_ms(25); 
978 }
979
980 /**
981  *  Set DDS frequency tuning word.  The output frequency is equal to RefClock * (ftw / 2 ^ 32).
982  *
983  *  @param ftw Frequency Tuning Word
984  */
985 void ddsSetFTW (uint32_t ftw)
986 {
987     // Set FTW0 (Frequency Tuning Word 0)
988     output_low (IO_CS);
989     spi_write (DDS_AD9954_FTW0);
990
991     spi_write ((ftw >> 24) & 0xff);
992     spi_write ((ftw >> 16) & 0xff);
993     spi_write ((ftw >> 8) & 0xff);
994     spi_write (ftw & 0xff);
995
996     output_high (IO_CS);
997     
998     // Strobe the DDS to set the frequency.
999     output_high (IO_UPDATE);
1000     output_low (IO_UPDATE);     
1001 }
1002
1003 /**
1004  *   Convert frequency in hertz to 32-bit DDS FTW (Frequency Tune Word).
1005  *
1006  *   @param freq frequency in Hertz
1007  *
1008  */
1009 void ddsSetFreq(uint32_t freq)
1010 {
1011     uint8_t i;
1012     uint32_t ftw;
1013     
1014     // To avoid rounding errors with floating point math, we do a long multiply on the data.
1015     ftw = freq * DDS_MULT[0];
1016     
1017     for (i = 0; i < DDS_FREQ_TO_FTW_DIGITS - 1; ++i)
1018         ftw += (freq * DDS_MULT[i+1]) / DDS_DIVISOR[i];
1019     
1020     ddsSetFTW (ftw);
1021 }
1022
1023 /**
1024  *  Set DDS frequency tuning word for the FSK 0 and 1 values.  The output frequency is equal 
1025  *  to RefClock * (ftw / 2 ^ 32).
1026  *
1027  *  @param ftw0 frequency tuning word for the FSK 0 value
1028  *  @param ftw1 frequency tuning word for the FSK 1 value
1029  */
1030 void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1)
1031 {
1032     // Set FTW0 (Frequency Tuning Word 0)
1033     output_low (IO_CS);
1034     spi_write (DDS_AD9954_FTW0);
1035
1036     spi_write ((ftw0 >> 24) & 0xff);
1037     spi_write ((ftw0 >> 16) & 0xff);
1038     spi_write ((ftw0 >> 8) & 0xff);
1039     spi_write (ftw0 & 0xff);
1040
1041     output_high (IO_CS);
1042     
1043     // Set FTW0 (Frequency Tuning Word 1)
1044     output_low (IO_CS);
1045     spi_write (DDS_AD9954_FTW1);
1046
1047     spi_write ((ftw1 >> 24) & 0xff);
1048     spi_write ((ftw1 >> 16) & 0xff);
1049     spi_write ((ftw1 >> 8) & 0xff);
1050     spi_write (ftw1 & 0xff);
1051
1052     output_high (IO_CS);
1053     
1054     // Strobe the DDS to set the frequency.
1055     output_high (IO_UPDATE);
1056     output_low (IO_UPDATE);     
1057 }
1058
1059 /** 
1060  *   Set the DDS to run in A-FSK, FSK, or PSK31 mode
1061  *
1062  *   @param mode DDS_MODE_APRS, DDS_MODE_PSK31, or DDS_MODE_HF_APRS constant
1063  */
1064 void ddsSetMode (DDS_MODE mode)
1065 {
1066     // Save the current mode.
1067     ddsMode = mode;
1068
1069     switch (mode) 
1070     {
1071         case DDS_MODE_POWERDOWN:
1072             // CFR1 (Control Function Register No. 1)
1073             output_low (IO_CS);
1074             spi_write (DDS_AD9954_CFR1);
1075         
1076             spi_write (0x00);
1077             spi_write (0x00);
1078             spi_write (0x00);
1079             spi_write (0xf0);  // Power down all subsystems.
1080             output_high (IO_CS);
1081             break;
1082
1083         case DDS_MODE_AFSK:
1084             // CFR1 (Control Function Register No. 1)
1085             output_low (IO_CS);
1086             spi_write (DDS_AD9954_CFR1);
1087         
1088             spi_write (0x03);  // OSK Enable and Auto OSK keying
1089             spi_write (0x00);
1090             spi_write (0x00);
1091             spi_write (0x40);  // Power down comparator circuit
1092             output_high (IO_CS);
1093             break;
1094
1095         case DDS_MODE_FSK:
1096             // CFR1 (Control Function Register No. 1)
1097             output_low (IO_CS);
1098             spi_write (DDS_AD9954_CFR1);
1099
1100             spi_write (0x03);  // Clear RAM Enable, OSK Enable, Auto OSK keying
1101             spi_write (0x00);
1102             spi_write (0x00);
1103             spi_write (0x40);  // Power down comparator circuit
1104             output_high (IO_CS);
1105
1106             // NOTE: The sweep rate requires 1/4 of a bit time (26uS) to transition.
1107             // 6KHz delta = 70641 counts = (6KHz / 364.8MHz) * 2 ^ 32
1108             // SYNC_CLK = 91.2MHz  1/91.2MHz * 70641 * 1/29 = 26.7uS
1109
1110             // NLSCW (Negative Linear Sweep Control Word)
1111             output_low (IO_CS);
1112             spi_write (DDS_AD9954_NLSCW);
1113
1114             spi_write (1);     // Falling sweep ramp rate word
1115             spi_write (0x00);  // Delta frequency tuning word
1116             spi_write (0x00);
1117             spi_write (0x00);
1118             spi_write (250); 
1119             output_high (IO_CS);
1120
1121             // PLSCW (Positive Linear Sweep Control Word)
1122             output_low (IO_CS);
1123             spi_write (DDS_AD9954_PLSCW);
1124
1125             spi_write (1);     // Rising sweep ramp rate word
1126             spi_write (0x00);  // Delta frequency tuning word
1127             spi_write (0x00);
1128             spi_write (0x00);
1129             spi_write (250); 
1130             output_high (IO_CS);
1131             break;
1132     } // END switch
1133     
1134     // Strobe the DDS to change the mode.
1135     output_high (IO_UPDATE);
1136     output_low (IO_UPDATE);      
1137 }
1138
1139 /** @} */
1140
1141 /**
1142  *  @defgroup flash Flash Manager
1143  *
1144  *  Functions to control the ST MP25P80 serial flash device.
1145  *
1146  *  @{
1147  */
1148
1149 /// Flash Chip Select - Port B3
1150 #define FLASH_CS PIN_B3
1151
1152 /// Flash Clock - Port B5
1153 #define FLASH_CLK PIN_B5
1154
1155 /// Flash Data Input - Port B4
1156 #define FLASH_D PIN_B4
1157
1158 /// Flash Data Output - Port B2
1159 #define FLASH_Q PIN_B2
1160
1161 /** 
1162  *   Determine if a flash write or erase operation is currently in progress.
1163  *
1164  *   @return true if write/erase in progress
1165  */
1166 bool_t flashIsWriteInProgress()
1167 {
1168     uint8_t status;
1169
1170     output_low (FLASH_CS);
1171
1172     // Read Status Register (RDSR) flash command.
1173     flashSendByte (0x05);
1174
1175     status = flashGetByte();
1176
1177     output_high (FLASH_CS);
1178
1179     return (((status & 0x01) == 0x01) ? true : false);
1180 }
1181
1182 /**
1183  *   Read a block of memory from the flash device.
1184  *
1185  *   @param address of desired location in the range 0x00000 to 0xFFFFF (1MB)
1186  *   @param block pointer to locate of data block
1187  *   @param length number of bytes to read
1188  */
1189 void flashReadBlock(uint32_t address, uint8_t *block, uint16_t length)
1190 {
1191     uint16_t i;
1192     
1193     output_low (FLASH_CS);
1194
1195     // Read Data Byte(s) (READ) flash command.
1196     flashSendByte (0x03);
1197     flashSendAddress (address);
1198     
1199     for (i = 0; i < length; ++i)
1200         *block++ = flashGetByte();
1201     
1202     output_high (FLASH_CS);
1203 }
1204
1205 /**
1206  *   Write a block of memory to the flash device.
1207  *
1208  *   @param address of desired location in the range 0x00000 to 0xFFFFF (1MB)
1209  *   @param block pointer data block to write
1210  *   @param length number of bytes to write
1211  */
1212 void flashWriteBlock(uint32_t address, uint8_t *block, uint8_t length)
1213 {
1214     uint8_t i;
1215
1216     output_low (FLASH_CS);
1217     // Write Enable (WREN) flash command.
1218     flashSendByte (0x06);
1219     output_high (FLASH_CS);
1220     
1221     output_low (FLASH_CS);
1222     // Page Program (PP) flash command.
1223     flashSendByte (0x02);
1224     flashSendAddress (address);
1225     
1226     for (i = 0; i < length; ++i) 
1227     {
1228         // Send each byte in the data block.
1229         flashSendByte (*block++);
1230
1231         // Track the address in the flash device.
1232         ++address;
1233
1234         // If we cross a page boundary (a page is 256 bytes) we need to stop and send the address again.
1235         if ((address & 0xff) == 0x00) 
1236         {
1237             output_high (FLASH_CS);
1238
1239             // Write this block of data.
1240             while (flashIsWriteInProgress());
1241
1242             output_low (FLASH_CS);
1243             // Write Enable (WREN) flash command.
1244             flashSendByte (0x06);
1245             output_high (FLASH_CS);
1246
1247             output_low (FLASH_CS);
1248             // Page Program (PP) flash command.
1249             flashSendByte (0x02);
1250             flashSendAddress (address);
1251         } // END if
1252     } // END for    
1253
1254     output_high (FLASH_CS);
1255
1256     // Wait for the final write operation to complete.
1257     while (flashIsWriteInProgress());
1258 }
1259
1260 /** 
1261  *   Erase the entire flash device (all locations set to 0xff).
1262  */
1263 void flashErase()
1264 {
1265     output_low (FLASH_CS);
1266     // Write Enable (WREN) flash command.
1267     flashSendByte (0x06);
1268     output_high (FLASH_CS);
1269     
1270     output_low (FLASH_CS);
1271     // Bulk Erase (BE) flash command.
1272     flashSendByte (0xc7);
1273     output_high (FLASH_CS);
1274
1275     while (flashIsWriteInProgress());
1276 }
1277
1278 /**
1279  *   Read a single byte from the flash device through the serial interface.  This function
1280  *   only controls the clock line.  The chip select must be configured before calling
1281  *   this function.
1282  *
1283  *   @return byte read from device
1284  */
1285 uint8_t flashGetByte()
1286 {
1287     uint8_t i, value;
1288     
1289     value = 0;      
1290     
1291     // Bit bang the 8-bits.
1292     for (i = 0; i < 8; ++i) 
1293     {
1294         // Data is ready on the rising edge of the clock.
1295         output_high (FLASH_CLK);
1296
1297         // MSB is first, so shift left.
1298         value = value << 1;
1299     
1300         if (input (FLASH_Q))
1301             value = value | 0x01;
1302     
1303         output_low (FLASH_CLK);
1304     } // END for
1305
1306     return value;
1307 }
1308
1309 /**
1310  *   Initialize the flash memory subsystem.
1311  */
1312 void flashInit()
1313 {
1314     // I/O lines to control flash.
1315     output_high (FLASH_CS);
1316     output_low (FLASH_CLK);
1317     output_low (FLASH_D);
1318 }
1319
1320 /**
1321  *   Write a single byte to the flash device through the serial interface.  This function
1322  *   only controls the clock line.  The chip select must be configured before calling
1323  *   this function.
1324  *
1325  *   @param value byte to write to device
1326  */
1327 void flashSendByte(uint8_t value)
1328 {
1329     uint8_t i;
1330     
1331     // Bit bang the 8-bits.
1332     for (i = 0; i < 8; ++i) 
1333     {
1334         // Drive the data input pin.
1335         if ((value & 0x80) == 0x80)
1336             output_high (FLASH_D);
1337         else
1338             output_low (FLASH_D);
1339         
1340         // MSB is first, so shift leeft.
1341         value = value << 1;
1342         
1343         // Data is accepted on the rising edge of the clock.
1344         output_high (FLASH_CLK);
1345         output_low (FLASH_CLK);
1346     } // END for
1347 }
1348
1349 /**
1350  *    Write the 24-bit address to the flash device through the serial interface.  This function
1351  *   only controls the clock line.  The chip select must be configured before calling
1352  *   this function.
1353  *
1354  *   @param address 24-bit flash device address
1355  */
1356 void flashSendAddress(uint32_t address)
1357 {
1358     uint8_t i;
1359     
1360     // Bit bang the 24-bits.
1361     for (i = 0; i < 24; ++i) 
1362     {
1363         // Drive the data input pin.
1364         if ((address & 0x800000) == 0x800000)
1365             output_high (FLASH_D);
1366         else
1367             output_low (FLASH_D);
1368         
1369         // MSB is first, so shift left.
1370         address = address << 1;
1371
1372         // Data is accepted on the rising edge of the clock.
1373         output_high (FLASH_CLK);
1374         output_low (FLASH_CLK);
1375     } // END for
1376 }
1377
1378 /** @} */
1379
1380 /**
1381  *  @defgroup GPS Motorola M12+ GPS Engine
1382  *
1383  *  Functions to control the Motorola M12+ GPS engine in native binary protocol mode.
1384  *
1385  *  @{
1386  */
1387
1388 /// The maximum length of a binary GPS engine message.
1389 #define GPS_BUFFER_SIZE 50
1390
1391 /// GPS parse engine state machine values.
1392 enum GPS_PARSE_STATE_MACHINE 
1393 {
1394     /// 1st start character '@'
1395     GPS_START1,
1396
1397     /// 2nd start character '@'
1398     GPS_START2,
1399
1400     /// Upper case 'A' - 'Z' message type
1401     GPS_COMMAND1,
1402
1403     /// Lower case 'a' - 'z' message type
1404     GPS_COMMAND2,
1405
1406     /// 0 - xx bytes based on message type 'Aa'
1407     GPS_READMESSAGE,
1408
1409     /// 8-bit checksum
1410     GPS_CHECKSUMMESSAGE,
1411
1412     /// End of message - Carriage Return
1413     GPS_EOMCR,
1414
1415     /// End of message - Line Feed
1416     GPS_EOMLF
1417 };
1418
1419 /// Index into gpsBuffer used to store message data.
1420 uint8_t gpsIndex;
1421
1422 /// State machine used to parse the GPS message stream.
1423 GPS_PARSE_STATE_MACHINE gpsParseState;
1424
1425 /// Buffer to store data as it is read from the GPS engine.
1426 uint8_t gpsBuffer[GPS_BUFFER_SIZE]; 
1427
1428 /// Peak altitude detected while GPS is in 3D fix mode.
1429 int32_t gpsPeakAltitude;
1430
1431 /// Checksum used to verify binary message from GPS engine.
1432 uint8_t gpsChecksum;
1433
1434 /// Last verified GPS message received.
1435 GPSPOSITION_STRUCT gpsPosition;
1436
1437 /**
1438  *   Get the type of fix.
1439  *
1440  *   @return gps fix type enumeration
1441  */
1442 GPS_FIX_TYPE gpsGetFixType()
1443 {
1444     // The upper 3-bits determine the fix type.
1445     switch (gpsPosition.status & 0xe000) 
1446     {
1447         case 0xe000:
1448             return GPS_3D_FIX;
1449
1450         case 0xc000:
1451             return GPS_2D_FIX;
1452     
1453         default:
1454             return GPS_NO_FIX;
1455     } // END switch
1456 }
1457
1458 /**
1459  *  Peak altitude detected while GPS is in 3D fix mode since the system was booted.
1460  *
1461  *  @return altitude in feet
1462  */
1463 int32_t gpsGetPeakAltitude()
1464 {
1465     return gpsPeakAltitude;
1466 }
1467
1468 /** 
1469  *    Initialize the GPS subsystem.
1470  */
1471 void gpsInit()
1472 {
1473     // Initial parse state.
1474     gpsParseState = GPS_START1;
1475
1476     // Assume we start at sea level.
1477     gpsPeakAltitude = 0;
1478
1479     // Clear the structure that stores the position message.
1480     memset (&gpsPosition, 0, sizeof(GPSPOSITION_STRUCT));
1481
1482     // Setup the timers used to measure the 1-PPS time period.
1483     setup_timer_3(T3_INTERNAL | T3_DIV_BY_1);
1484     setup_ccp2 (CCP_CAPTURE_RE | CCP_USE_TIMER3);
1485 }
1486
1487 /**
1488  *   Determine if new GPS message is ready to process.  This function is a one shot and
1489  *   typically returns true once a second for each GPS position fix.
1490  *
1491  *   @return true if new message available; otherwise false
1492  */
1493 bool_t gpsIsReady()
1494 {
1495     if (gpsPosition.updateFlag) 
1496     {
1497         gpsPosition.updateFlag = false;
1498         return true;
1499     } // END if
1500
1501     return false;
1502 }
1503
1504 /**
1505  *   Calculate NMEA-0183 message checksum of buffer that is length bytes long.
1506  *
1507  *   @param buffer pointer to data buffer.
1508  *   @param length number of bytes in buffer.
1509  *
1510  *   @return checksum of buffer
1511  */
1512 uint8_t gpsNMEAChecksum (uint8_t *buffer, uint8_t length)
1513 {
1514     uint8_t i, checksum;
1515
1516     checksum = 0;
1517
1518     for (i = 0; i < length; ++i)
1519         checksum ^= buffer[i];
1520
1521     return checksum;
1522 }
1523
1524 /**
1525  *   Verify the GPS engine is sending the @@Hb position report message.  If not,
1526  *   configure the GPS engine to send the desired report.
1527  *
1528  *   @return true if GPS engine operation; otherwise false
1529  */
1530 bool_t gpsSetup()
1531 {
1532     uint8_t startTime, retryCount;
1533
1534     // We wait 10 seconds for the GPS engine to respond to our message request.
1535     startTime = timeGetTicks();
1536     retryCount = 0;
1537
1538     while (++retryCount < 10) 
1539     {
1540         // Read the serial FIFO and process the GPS messages.
1541         gpsUpdate();
1542
1543         // If a GPS data set is available, then GPS is operational.
1544         if (gpsIsReady()) 
1545         {
1546             timeSetDutyCycle (TIME_DUTYCYCLE_10);
1547             return true;
1548         }
1549
1550         if (timeGetTicks() > startTime) 
1551         {
1552             puts ("@@Hb\001\053\015\012");
1553             startTime += 10;
1554         } // END if
1555             
1556     } // END while
1557
1558     return false;
1559 }
1560
1561 /**
1562  *   Parse the Motorola @@Hb (Short position/message) report.
1563  */
1564 void gpsParsePositionMessage()
1565 {
1566     // Convert the binary stream into data elements.  We will scale to the desired units
1567     // as the values are used.
1568     gpsPosition.updateFlag = true;
1569
1570     gpsPosition.month = gpsBuffer[0];
1571     gpsPosition.day = gpsBuffer[1];
1572     gpsPosition.year = ((uint16_t) gpsBuffer[2] << 8) | gpsBuffer[3];
1573     gpsPosition.hours = gpsBuffer[4];
1574     gpsPosition.minutes = gpsBuffer[5];
1575     gpsPosition.seconds = gpsBuffer[6];
1576     gpsPosition.latitude = ((int32) gpsBuffer[11] << 24) | ((int32) gpsBuffer[12] << 16) | ((int32) gpsBuffer[13] << 8) | (int32) gpsBuffer[14];
1577     gpsPosition.longitude = ((int32) gpsBuffer[15] << 24) | ((int32) gpsBuffer[16] << 16) | ((int32) gpsBuffer[17] << 8) | gpsBuffer[18];
1578     gpsPosition.altitudeCM = ((int32) gpsBuffer[19] << 24) | ((int32) gpsBuffer[20] << 16) | ((int32) gpsBuffer[21] << 8) | gpsBuffer[22];
1579     gpsPosition.altitudeFeet = gpsPosition.altitudeCM * 100l / 3048l;
1580     gpsPosition.vSpeed = ((uint16_t) gpsBuffer[27] << 8) | gpsBuffer[28];
1581     gpsPosition.hSpeed = ((uint16_t) gpsBuffer[29] << 8) | gpsBuffer[30];
1582     gpsPosition.heading = ((uint16_t) gpsBuffer[31] << 8) | gpsBuffer[32];
1583     gpsPosition.dop = ((uint16_t) gpsBuffer[33] << 8) | gpsBuffer[34];
1584     gpsPosition.visibleSats = gpsBuffer[35];
1585     gpsPosition.trackedSats = gpsBuffer[36];
1586     gpsPosition.status = ((uint16_t) gpsBuffer[37] << 8) | gpsBuffer[38];
1587
1588     // Update the peak altitude if we have a valid 3D fix.
1589     if (gpsGetFixType() == GPS_3D_FIX)
1590         if (gpsPosition.altitudeFeet > gpsPeakAltitude)
1591             gpsPeakAltitude = gpsPosition.altitudeFeet;
1592 }
1593
1594 /**
1595  *  Turn on the GPS engine power and serial interface.
1596  */
1597 void gpsPowerOn()
1598 {
1599     // 3.0 VDC LDO control line.
1600     output_high (IO_GPS_PWR);
1601
1602     // Enable the UART and the transmit line.
1603 #asm
1604     bsf 0xFAB.7
1605 #endasm
1606 }
1607
1608 /**
1609  *   Turn off the GPS engine power and serial interface.
1610  */
1611 void gpsPowerOff()
1612 {
1613     // Disable the UART and the transmit line.
1614 #asm
1615     bcf 0xFAB.7
1616 #endasm
1617
1618     // 3.0 VDC LDO control line.
1619     output_low (IO_GPS_PWR);
1620 }
1621
1622 /**
1623  *   Read the serial FIFO and process complete GPS messages.
1624  */
1625 void gpsUpdate() 
1626 {
1627     uint8_t value;
1628
1629     // This state machine handles each characters as it is read from the GPS serial port.
1630     // We are looking for the GPS mesage @@Hb ... C
1631     while (serialHasData()) 
1632     {
1633         // Get the character value.
1634         value = serialRead();
1635
1636         // Process based on the state machine.
1637         switch (gpsParseState) 
1638         {
1639             case GPS_START1:
1640                 if (value == '@')
1641                     gpsParseState = GPS_START2;
1642                 break;
1643
1644             case GPS_START2:
1645                 if (value == '@')
1646                     gpsParseState = GPS_COMMAND1;
1647                 else
1648                     gpsParseState = GPS_START1;
1649                 break;
1650
1651             case GPS_COMMAND1:
1652                 if (value == 'H')
1653                     gpsParseState = GPS_COMMAND2;
1654                 else
1655                     gpsParseState = GPS_START1;
1656                 break;
1657
1658             case GPS_COMMAND2:
1659                 if (value == 'b') 
1660                 {
1661                     gpsParseState = GPS_READMESSAGE;
1662                     gpsIndex = 0;
1663                     gpsChecksum = 0;
1664                     gpsChecksum ^= 'H';
1665                     gpsChecksum ^= 'b';
1666                 } else
1667                     gpsParseState = GPS_START1;
1668                 break;
1669
1670             case GPS_READMESSAGE:
1671                 gpsChecksum ^= value;
1672                 gpsBuffer[gpsIndex++] = value;
1673
1674                 if (gpsIndex == 47)
1675                     gpsParseState = GPS_CHECKSUMMESSAGE;
1676
1677                 break;
1678
1679             case GPS_CHECKSUMMESSAGE:
1680                 if (gpsChecksum == value)
1681                     gpsParseState = GPS_EOMCR;
1682                 else
1683                     gpsParseState = GPS_START1;
1684                 break;
1685
1686             case GPS_EOMCR:
1687                 if (value == 13)
1688                     gpsParseState = GPS_EOMLF;
1689                 else
1690                     gpsParseState = GPS_START1;
1691                 break;
1692
1693             case GPS_EOMLF:
1694                 // Once we have the last character, convert the binary message to something usable.
1695                 if (value == 10)
1696                     gpsParsePositionMessage();
1697
1698                 gpsParseState = GPS_START1;
1699                 break;
1700         } // END switch
1701     } // END while
1702 }
1703
1704 /** @} */
1705
1706
1707 /**
1708  *  @defgroup log Flight Data Recorder
1709  *
1710  *  Functions to manage and control the flight data recorder
1711  *
1712  *  @{
1713  */
1714
1715 /// Number of bytes to buffer before writing to flash memory.
1716 #define LOG_WRITE_BUFFER_SIZE 40
1717
1718 /// Last used address in flash memory.
1719 uint32_t logAddress;
1720
1721 /// Temporary buffer that holds data before it is written to flash device.
1722 uint8_t logBuffer[LOG_WRITE_BUFFER_SIZE];
1723
1724 /// Current index into log buffer.
1725 uint8_t logIndex;
1726
1727 /** 
1728  *    Last used address in flash memory.  This location is where the next log data will
1729  *    be written.
1730  *
1731  *    @return 24-bit flash memory address
1732  */
1733 uint32_t logGetAddress()
1734 {
1735     return logAddress;
1736 }
1737
1738 /**
1739  *   Write the contents of the temporary log buffer to the flash device.  If the buffer
1740  *   is empty, nothing is done.
1741  */
1742 void logFlush()
1743 {
1744     // We only need to write if there is data.
1745     if (logIndex != 0) 
1746     {
1747         flashWriteBlock (logAddress, logBuffer, logIndex);
1748         logAddress += logIndex;
1749         logIndex = 0;
1750     } // END if
1751 }
1752
1753 /** 
1754  *   Prepare the flight data recorder for logging.
1755  */
1756 void logInit()
1757 {
1758     uint8_t buffer[8];
1759     bool_t endFound;
1760
1761     fprintf (PC_HOST, "Searching for end of flash log...");
1762
1763     logAddress = 0x0000;
1764     endFound = false;
1765
1766     // Read each logged data block from flash to determine how long it is.
1767     do 
1768     {
1769         // Read the data log entry type.
1770         flashReadBlock (logAddress, buffer, 1);
1771
1772         // Based on the log entry type, we'll skip over the data contained in the entry.
1773         switch (buffer[0]) 
1774         {
1775             case LOG_BOOTED:
1776                 logAddress += 7;
1777                 break;
1778
1779             case LOG_COORD:
1780                 logAddress += 26;
1781                 break;
1782
1783             case LOG_TEMPERATURE:
1784                 logAddress += 3;
1785                 break;
1786
1787             case LOG_VOLTAGE:
1788                 logAddress += 5;
1789                 break;
1790
1791             case 0xff:
1792                 endFound = true;
1793                 break;
1794
1795             default:
1796                 ++logAddress;
1797         } // END switch
1798     } while (logAddress < 0x100000 && !endFound);
1799
1800     fprintf (PC_HOST, "done.  Log contains %ld bytes.\n\r", logAddress);
1801
1802     logIndex = 0;
1803 }
1804
1805 /**
1806  *   Start a entry in the data log.
1807  *
1808  *   @param type of log entry, i.e. LOG_BOOTED, LOG_COORD, etc.
1809  */
1810 void logType (LOG_TYPE type)
1811 {
1812     // Only add the new entry if there is space.
1813     if (logAddress >= 0x100000)
1814         return;
1815
1816     // Write the old entry first.
1817     logFlush();
1818
1819     // Save the type and set the log buffer pointer.
1820     logBuffer[0] = type;
1821     logIndex = 1;
1822 }
1823
1824 /**
1825  *  Save an unsigned, 8-bit value in the log.
1826  *
1827  *  @param value unsigned, 8-bit value
1828  */
1829 void logUint8 (uint8_t value)
1830 {
1831     logBuffer[logIndex++] = value;
1832 }
1833
1834 /**
1835  *  Save a signed, 16-bit value in the log.
1836  *
1837  *  @param value signed, 16-bit value
1838  */
1839 void logInt16 (int16_t value)
1840 {
1841     logBuffer[logIndex++] = (value >> 8) & 0xff;
1842     logBuffer[logIndex++] = value & 0xff;
1843 }
1844
1845 /**
1846  *  Save an unsigned, 16-bit value in the log.
1847  *
1848  *  @param value unsigned, 16-bit value
1849  */
1850 void logUint16 (uint16_t value)
1851 {
1852     logBuffer[logIndex++] = (value >> 8) & 0xff;
1853     logBuffer[logIndex++] = value & 0xff;
1854 }
1855
1856 /**
1857  *  Save a signed, 32-bit value in the log.
1858  *
1859  *  @param value signed, 32-bit value
1860  */
1861 void logInt32 (int32_t value)
1862 {
1863     logBuffer[logIndex++] = (value >> 24) & 0xff;
1864     logBuffer[logIndex++] = (value >> 16) & 0xff;
1865     logBuffer[logIndex++] = (value >> 8) & 0xff;
1866     logBuffer[logIndex++] = value & 0xff;
1867 }
1868
1869 /** @} */
1870
1871 /**
1872  *  @defgroup LM92 LM92 temperature sensor
1873  *
1874  *  Read and control the National Semiconductor LM92 I2C temperature sensor
1875  *
1876  *  @{
1877  */
1878
1879 /** 
1880  *   Read the LM92 temperature value in 0.1 degrees F.
1881  *
1882  *   @return 0.1 degrees F
1883  */
1884 int16_t lm92GetTemp()
1885 {
1886     int16_t value;
1887     int32_t temp;
1888
1889     // Set the SDA and SCL to input pins to control the LM92.
1890     set_tris_c (0x9a);
1891
1892     // Read the temperature register value.
1893     i2c_start();
1894     i2c_write(0x97);
1895     value = ((int16_t) i2c_read() << 8);
1896     value = value | ((int16_t) i2c_read() & 0x00f8);
1897     i2c_stop();
1898
1899     // Set the SDA and SCL back to outputs for use with the AD9954 because we share common clock pins.
1900     set_tris_c (0x82);
1901
1902     //  LM92 register   0.0625degC/bit   9   10     9
1903     //  ------------- * -------------- * - * -- =  -- + 320
1904     //        8                          5         64
1905
1906     // Convert to degrees F.
1907     temp = (int32_t) value;
1908     temp = ((temp * 9l) / 64l) + 320;
1909     
1910     return (int16_t) temp;
1911 }
1912
1913 /** @} */
1914
1915
1916 /**
1917  *  @defgroup serial Serial Port FIFO
1918  *
1919  *  FIFO for the built-in serial port.
1920  *
1921  *  @{
1922  */
1923
1924 /// Size of serial port FIFO in bytes.  It must be a power of 2, i.e. 2, 4, 8, 16, etc.
1925 #define SERIAL_BUFFER_SIZE 64
1926
1927 /// Mask to wrap around at end of circular buffer.  (SERIAL_BUFFER_SIZE - 1)
1928 #define SERIAL_BUFFER_MASK 0x3f
1929
1930 /// Index to the next free location in the buffer.
1931 uint8_t serialHead;
1932
1933 /// Index to the next oldest data in the buffer.
1934 uint8_t serialTail;
1935
1936 /// Circular buffer (FIFO) to hold serial data.
1937 uint8_t serialBuffer[SERIAL_BUFFER_SIZE];
1938
1939 /**
1940  *   Determine if the FIFO contains data.
1941  *
1942  *   @return true if data present; otherwise false
1943  */
1944 bool_t serialHasData()
1945 {
1946     if (serialHead == serialTail)
1947         return false;
1948
1949     return true;
1950 }
1951
1952 /** 
1953  *   Initialize the serial processor.
1954  */
1955 void serialInit()
1956 {
1957     serialHead = 0;
1958     serialTail = 0;
1959 }
1960
1961 /**
1962  *   Get the oldest character from the FIFO.
1963  *
1964  *   @return oldest character; 0 if FIFO is empty
1965  */
1966 uint8_t serialRead()
1967 {
1968     uint8_t value;
1969
1970     // Make sure we have something to return.
1971     if (serialHead == serialTail)
1972         return 0;
1973
1974     // Save the value.
1975     value = serialBuffer[serialTail];
1976
1977     // Update the pointer.
1978     serialTail = (serialTail + 1) & SERIAL_BUFFER_MASK;
1979
1980     return value;
1981 }
1982
1983 /**
1984  *   Read and store any characters in the PIC serial port in a FIFO.
1985  */
1986 void serialUpdate()
1987 {
1988     // If there isn't a character in the PIC buffer, just leave.
1989     while (kbhit()) 
1990     {
1991         // Save the value in the FIFO.
1992         serialBuffer[serialHead] = getc();
1993
1994         // Move the pointer to the next open space.
1995         serialHead = (serialHead + 1) & SERIAL_BUFFER_MASK;
1996     }
1997 }
1998
1999 /** @} */
2000
2001 /**
2002  *  @defgroup sys System Library Functions
2003  *
2004  *  Generic system functions similiar to the run-time C library.
2005  *
2006  *  @{
2007  */
2008
2009 /**
2010  *    Calculate the CRC-16 CCITT of buffer that is length bytes long.
2011  *    The crc parameter allow the calculation on the CRC on multiple buffers.
2012  *
2013  *    @param buffer Pointer to data buffer.
2014  *    @param length number of bytes in data buffer
2015  *    @param crc starting value
2016  *
2017  *    @return CRC-16 of buffer[0 .. length]
2018  */
2019 uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc)
2020 {
2021     uint8_t i, bit, value;
2022
2023     for (i = 0; i < length; ++i) 
2024     {
2025         value = buffer[i];
2026
2027         for (bit = 0; bit < 8; ++bit) 
2028         {
2029             crc ^= (value & 0x01);
2030             crc = ( crc & 0x01 ) ? ( crc >> 1 ) ^ 0x8408 : ( crc >> 1 );
2031             value = value >> 1;
2032         } // END for
2033     } // END for
2034
2035     return crc ^ 0xffff;
2036 }
2037
2038 /**
2039  *   Initialize the system library and global resources.
2040  */
2041 void sysInit()
2042 {
2043     gpsPowerOff ();
2044     output_high (IO_LED);
2045
2046     output_high (IO_CS);
2047     output_low (IO_PS1);
2048     output_low (IO_PS0);
2049     output_low (IO_OSK);
2050     output_low (IO_UPDATE);
2051     output_low (IO_PTT);
2052     output_low (IO_GPS_TXD);
2053
2054     // Configure the port direction (input/output).
2055     set_tris_a (0xc3);
2056     set_tris_b (0x44);
2057     set_tris_c (0x82);
2058
2059     // Display a startup message during boot.
2060     fprintf (PC_HOST, "System booted.\n\r");
2061 }
2062
2063 /**
2064  *   Log the current GPS position.
2065  */
2066 void sysLogGPSData()
2067 {
2068     // Log the data.
2069     logType (LOG_COORD);
2070     logUint8 (gpsPosition.hours);
2071     logUint8 (gpsPosition.minutes);
2072     logUint8 (gpsPosition.seconds);
2073     logInt32 (gpsPosition.latitude);
2074     logInt32 (gpsPosition.longitude);
2075     logInt32 (gpsPosition.altitudeCM);
2076
2077     logUint16 (gpsPosition.vSpeed);
2078     logUint16 (gpsPosition.hSpeed);
2079     logUint16 (gpsPosition.heading);
2080
2081     logUint16 (gpsPosition.status);
2082
2083     logUint8 ((uint8_t) (gpsPosition.dop & 0xff));
2084     logUint8 ((uint8_t) ((gpsPosition.visibleSats << 4) | gpsPosition.trackedSats));
2085 }
2086
2087 /**
2088  *   Log the ADC values of the bus and reference voltage values.
2089  */
2090 void sysLogVoltage()
2091 {
2092     logType (LOG_VOLTAGE);
2093     logUint16 (adcRawBusVolt());
2094     logUint16 (adcRawRefVolt());
2095 }
2096
2097 /** @} */
2098
2099 /**
2100  *  @defgroup rtc Real Time Interrupt tick
2101  *
2102  *  Manage the built-in real time interrupt.  The interrupt clock PRI is 104uS (9600 bps).
2103  *
2104  *  @{
2105  */
2106
2107 /// A counter that ticks every 100mS.
2108 uint8_t timeTicks;
2109
2110 /// Counts the number of 104uS interrupts for a 100mS time period.
2111 uint16_t timeInterruptCount;
2112
2113 /// Counts the number of 100mS time periods in 1 second.
2114 uint8_t time100ms;
2115
2116 /// System time in seconds.
2117 uint8_t timeSeconds;
2118
2119 /// System time in minutes.
2120 uint8_t timeMinutes;
2121
2122 /// System time in hours.
2123 uint8_t timeHours;
2124
2125 /// Desired LED duty cycle 0 to 9 where 0 = 0% and 9 = 90%.
2126 uint8_t timeDutyCycle;
2127
2128 /// Current value of the timer 1 compare register used to generate 104uS interrupt rate (9600bps).
2129 uint16_t timeCompare;
2130
2131 /// 16-bit NCO where the upper 8-bits are used to index into the frequency generation table.
2132 uint16_t timeNCO;
2133
2134 /// Audio tone NCO update step (phase).
2135 uint16_t timeNCOFreq;
2136
2137 /// Counter used to deciminate down from the 104uS to 833uS interrupt rate.  (9600 to 1200 baud) 
2138 uint8_t timeLowRateCount;
2139
2140 /// Current TNC mode (standby, 1200bps A-FSK, or 9600bps FSK)
2141 TNC_DATA_MODE tncDataMode;
2142
2143 /// Flag set true once per second.
2144 bool_t timeUpdateFlag;
2145
2146 /// Flag that indicate the flight time should run.
2147 bool_t timeRunFlag;
2148
2149 /// The change in the CCP_1 register for each 104uS (9600bps) interrupt period.
2150 #define TIME_RATE 125
2151
2152 /**
2153  *   Running 8-bit counter that ticks every 100mS.
2154  *
2155  *   @return 100mS time tick
2156  */
2157 uint8_t timeGetTicks()
2158 {
2159     return timeTicks;
2160 }
2161
2162 /**
2163  *   Initialize the real-time clock.
2164  */
2165 void timeInit()
2166 {
2167     timeTicks = 0;
2168     timeInterruptCount = 0;
2169     time100mS = 0;
2170     timeSeconds = 0;
2171     timeMinutes = 0;
2172     timeHours = 0;
2173     timeDutyCycle = TIME_DUTYCYCLE_70;
2174     timeCompare = TIME_RATE;
2175     timeUpdateFlag = false;
2176     timeNCO = 0x00;
2177     timeLowRateCount = 0;
2178     timeNCOFreq = 0x2000;
2179     tncDataMode = TNC_MODE_STANDBY;  
2180     timeRunFlag = false;
2181     
2182     // Configure CCP1 to interrupt at 1mS for PSK31 or 833uS for 1200 baud APRS
2183     CCP_1 = TIME_RATE;
2184     set_timer1(timeCompare);
2185     setup_ccp1( CCP_COMPARE_INT );
2186     setup_timer_1( T1_INTERNAL | T1_DIV_BY_4 );
2187 }
2188
2189 /**
2190  *   Function return true once a second based on real-time clock.
2191  *
2192  *   @return true on one second tick; otherwise false
2193  */
2194 bool_t timeIsUpdate()
2195 {
2196     if (timeUpdateFlag) 
2197     {
2198         timeUpdateFlag = false;
2199         return true;
2200     } // END if
2201
2202     return false;
2203 }
2204
2205 /**
2206  *   Set the blink duty cycle of the heartbeat LED.  The LED blinks at a 1Hz rate.
2207  *
2208  *   @param dutyCycle TIME_DUTYCYCLE_xx constant
2209  */
2210 void timeSetDutyCycle (uint8_t dutyCycle)
2211 {
2212     timeDutyCycle = dutyCycle;
2213 }
2214
2215 /**
2216  *   Set a flag to indicate the flight time should run.  This flag is typically set when the payload
2217  *   lifts off.
2218  */
2219 void timeSetRunFlag()
2220 {
2221     timeRunFlag = true;
2222 }
2223
2224 #INT_CCP1
2225 /**
2226  *   Timer interrupt handler called every 104uS (9600 times/second).
2227  */
2228 void timeUpdate()
2229 {
2230     // Setup the next interrupt for the operational mode.
2231     timeCompare += TIME_RATE;
2232     CCP_1 = timeCompare;
2233
2234     switch (tncDataMode) 
2235     {
2236         case TNC_MODE_STANDBY:
2237             break;
2238
2239         case TNC_MODE_1200_AFSK:
2240             ddsSetFTW (freqTable[timeNCO >> 8]);
2241
2242             timeNCO += timeNCOFreq;
2243
2244             if (++timeLowRateCount == 8) 
2245             {
2246                 timeLowRateCount = 0;
2247                 tnc1200TimerTick();
2248             } // END if
2249             break;
2250
2251         case TNC_MODE_9600_FSK:
2252             tnc9600TimerTick();
2253             break;
2254     } // END switch
2255
2256     // Read the GPS serial port and save any incoming characters.
2257     serialUpdate();
2258
2259     // Count the number of milliseconds required for the tenth second counter.
2260     if (++timeInterruptCount == 960) 
2261     {
2262         timeInterruptCount = 0;
2263
2264         // This timer just ticks every 100mS and is used for general timing.
2265         ++timeTicks;
2266
2267         // Roll the counter over every second.
2268         if (++time100mS == 10) 
2269         {
2270             time100mS = 0;
2271
2272             // We set this flag true every second.
2273             timeUpdateFlag = true;
2274
2275             // Maintain a Real Time Clock.
2276             if (timeRunFlag)
2277                 if (++timeSeconds == 60) 
2278                 {
2279                     timeSeconds = 0;
2280     
2281                     if (++timeMinutes == 60) 
2282                     {
2283                         timeMinutes = 0;
2284                         ++timeHours;
2285                     } // END if timeMinutes
2286                 } // END if timeSeconds
2287         } // END if time100mS
2288
2289         // Flash the status LED at timeDutyCycle % per second.  We use the duty cycle for mode feedback.
2290         if (time100mS >= timeDutyCycle)
2291             output_low (IO_LED);
2292         else
2293             output_high (IO_LED);
2294     } // END if
2295 }
2296
2297 /** @} */
2298
2299 /**
2300  *  @defgroup tnc TNC (Terminal Node Controller)
2301  *
2302  *  Functions that provide a subset of the TNC functions.
2303  *
2304  *  @{
2305  */
2306
2307 /// The number of start flag bytes to send before the packet message.  (360bits * 1200bps = 300mS)
2308 #define TNC_TX_DELAY 45
2309
2310 /// The size of the TNC output buffer.
2311 #define TNC_BUFFER_SIZE 80
2312
2313 /// States that define the current mode of the 1200 bps (A-FSK) state machine.
2314 enum TNC_TX_1200BPS_STATE 
2315 {
2316     /// Stand by state ready to accept new message.
2317     TNC_TX_READY,
2318
2319     /// 0x7E bit stream pattern used to define start of APRS message.
2320     TNC_TX_SYNC,
2321
2322     /// Transmit the AX.25 header that contains the source/destination call signs, APRS path, and flags.
2323     TNC_TX_HEADER,
2324
2325     /// Transmit the message data.
2326     TNC_TX_DATA,
2327
2328     /// Transmit the end flag sequence.
2329     TNC_TX_END
2330 };
2331
2332 /// Enumeration of the messages we can transmit. 
2333 enum TNC_MESSAGE_TYPE 
2334 {
2335     /// Startup message that contains software version information.
2336     TNC_BOOT_MESSAGE,
2337
2338     /// Plain text status message.
2339     TNC_STATUS,
2340
2341     /// Message that contains GPS NMEA-0183 $GPGGA message.
2342     TNC_GGA,
2343
2344     /// Message that contains GPS NMEA-0183 $GPRMC message.
2345     TNC_RMC
2346 };
2347
2348 /// AX.25 compliant packet header that contains destination, station call sign, and path.
2349 /// 0x76 for SSID-11, 0x78 for SSID-12
2350 uint8_t TNC_AX25_HEADER[30] = { 
2351     'A' << 1, 'P' << 1, 'R' << 1, 'S' << 1, ' ' << 1, ' ' << 1, 0x60, \
2352     'K' << 1, 'D' << 1, '7' << 1, 'L' << 1, 'M' << 1, 'O' << 1, 0x76, \
2353     'G' << 1, 'A' << 1, 'T' << 1, 'E' << 1, ' ' << 1, ' ' << 1, 0x60, \
2354     'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '3' << 1, ' ' << 1, 0x67, \
2355     0x03, 0xf0 };
2356
2357
2358 /// The next bit to transmit.
2359 uint8_t tncTxBit;
2360
2361 /// Current mode of the 1200 bps state machine.
2362 TNC_TX_1200BPS_STATE tncMode;
2363
2364 /// Counter for each bit (0 - 7) that we are going to transmit.
2365 uint8_t tncBitCount;
2366
2367 /// A shift register that holds the data byte as we bit shift it for transmit.
2368 uint8_t tncShift;
2369
2370 /// Index into the APRS header and data array for each byte as we transmit it.
2371 uint8_t tncIndex;
2372
2373 /// The number of bytes in the message portion of the AX.25 message.
2374 uint8_t tncLength;
2375
2376 /// A copy of the last 5 bits we've transmitted to determine if we need to bit stuff on the next bit.
2377 uint8_t tncBitStuff;
2378
2379 /// Pointer to TNC buffer as we save each byte during message preparation.
2380 uint8_t *tncBufferPnt;
2381
2382 /// The type of message to tranmit in the next packet.
2383 TNC_MESSAGE_TYPE tncPacketType;
2384
2385 /// Buffer to hold the message portion of the AX.25 packet as we prepare it.
2386 uint8_t tncBuffer[TNC_BUFFER_SIZE];
2387
2388 /// Flag that indicates we want to transmit every 5 seconds.
2389 bool_t tncHighRateFlag;
2390
2391 /** 
2392  *   Initialize the TNC internal variables.
2393  */
2394 void tncInit()
2395 {
2396     tncTxBit = 0;
2397     tncMode = TNC_TX_READY;
2398     tncPacketType = TNC_BOOT_MESSAGE;
2399     tncHighRateFlag = false;
2400 }
2401
2402 /**
2403  *  Determine if the hardware if ready to transmit a 1200 baud packet.
2404  *
2405  *  @return true if ready; otherwise false
2406  */
2407 bool_t tncIsFree()
2408 {
2409     if (tncMode == TNC_TX_READY)
2410         return true;
2411
2412     return false;
2413 }
2414
2415 void tncHighRate(bool_t state)
2416 {
2417     tncHighRateFlag = state;
2418 }
2419
2420 /**
2421  *   Configure the TNC for the desired data mode.
2422  *
2423  *    @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK
2424  */ 
2425 void tncSetMode(TNC_DATA_MODE dataMode)
2426 {
2427     switch (dataMode) 
2428     {
2429         case TNC_MODE_1200_AFSK:
2430             ddsSetMode (DDS_MODE_AFSK);
2431             break;
2432
2433         case TNC_MODE_9600_FSK:
2434             ddsSetMode (DDS_MODE_FSK);
2435
2436             // FSK tones at 445.947 and 445.953 MHz
2437             ddsSetFSKFreq (955382980, 955453621);
2438             break;
2439     } // END switch
2440
2441     tncDataMode = dataMode; 
2442 }
2443
2444 /**
2445  *  Determine if the seconds value timeSeconds is a valid time slot to transmit
2446  *  a message.  Time seconds is in UTC.
2447  *
2448  *  @param timeSeconds UTC time in seconds
2449  *
2450  *  @return true if valid time slot; otherwise false
2451  */
2452 bool_t tncIsTimeSlot (uint8_t timeSeconds)
2453 {
2454     if (tncHighRateFlag)
2455     {
2456         if ((timeSeconds % 5) == 0)
2457             return true;
2458
2459         return false;
2460     } // END if
2461
2462     switch (timeSeconds) 
2463     {
2464         case 0:
2465         case 15:
2466         case 30:
2467         case 45:
2468             return true;
2469
2470         default:
2471             return false;
2472     } // END switch
2473 }
2474
2475 /**
2476  *   Method that is called every 833uS to transmit the 1200bps A-FSK data stream.
2477  *   The provides the pre and postamble as well as the bit stuffed data stream.
2478  */
2479 void tnc1200TimerTick()
2480 {
2481     // Set the A-FSK frequency.
2482     if (tncTxBit == 0x00)
2483         timeNCOFreq = 0x2000;
2484     else
2485         timeNCOFreq = 0x3aab;
2486
2487     switch (tncMode) 
2488     {
2489         case TNC_TX_READY:
2490             // Generate a test signal alteranting between high and low tones.
2491             tncTxBit = (tncTxBit == 0 ? 1 : 0);
2492             break;
2493
2494         case TNC_TX_SYNC:
2495             // The variable tncShift contains the lastest data byte.
2496             // NRZI enocde the data stream.
2497             if ((tncShift & 0x01) == 0x00)
2498                 if (tncTxBit == 0)
2499                     tncTxBit = 1;
2500                 else
2501                     tncTxBit = 0;
2502                     
2503             // When the flag is done, determine if we need to send more or data.
2504             if (++tncBitCount == 8) 
2505             {
2506                 tncBitCount = 0;
2507                 tncShift = 0x7e;
2508
2509                 // Once we transmit x mS of flags, send the data.
2510                 // txDelay bytes * 8 bits/byte * 833uS/bit = x mS
2511                 if (++tncIndex == TNC_TX_DELAY) 
2512                 {
2513                     tncIndex = 0;
2514                     tncShift = TNC_AX25_HEADER[0];
2515                     tncBitStuff = 0;
2516                     tncMode = TNC_TX_HEADER;
2517                 } // END if
2518             } else
2519                 tncShift = tncShift >> 1;
2520             break;
2521
2522         case TNC_TX_HEADER:
2523             // Determine if we have sent 5 ones in a row, if we have send a zero.
2524             if (tncBitStuff == 0x1f) 
2525             {
2526                 if (tncTxBit == 0)
2527                     tncTxBit = 1;
2528                 else
2529                     tncTxBit = 0;
2530
2531                 tncBitStuff = 0x00;
2532                 return;
2533             }    // END if
2534
2535             // The variable tncShift contains the lastest data byte.
2536             // NRZI enocde the data stream.
2537             if ((tncShift & 0x01) == 0x00)
2538                 if (tncTxBit == 0)
2539                     tncTxBit = 1;
2540                 else
2541                     tncTxBit = 0;
2542
2543             // Save the data stream so we can determine if bit stuffing is 
2544             // required on the next bit time.
2545             tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
2546
2547             // If all the bits were shifted, get the next byte.
2548             if (++tncBitCount == 8) 
2549             {
2550                 tncBitCount = 0;
2551
2552                 // After the header is sent, then send the data.
2553                 if (++tncIndex == sizeof(TNC_AX25_HEADER)) 
2554                 {
2555                     tncIndex = 0;
2556                     tncShift = tncBuffer[0];
2557                     tncMode = TNC_TX_DATA;
2558                 } else
2559                     tncShift = TNC_AX25_HEADER[tncIndex];
2560
2561             } else
2562                 tncShift = tncShift >> 1;
2563
2564             break;
2565
2566         case TNC_TX_DATA:
2567             // Determine if we have sent 5 ones in a row, if we have send a zero.
2568             if (tncBitStuff == 0x1f) 
2569             {
2570                 if (tncTxBit == 0)
2571                     tncTxBit = 1;
2572                 else
2573                     tncTxBit = 0;
2574
2575                 tncBitStuff = 0x00;
2576                 return;
2577             }    // END if
2578
2579             // The variable tncShift contains the lastest data byte.
2580             // NRZI enocde the data stream.
2581             if ((tncShift & 0x01) == 0x00)
2582                 if (tncTxBit == 0)
2583                     tncTxBit = 1;
2584                 else
2585                     tncTxBit = 0;
2586
2587             // Save the data stream so we can determine if bit stuffing is 
2588             // required on the next bit time.
2589             tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
2590
2591             // If all the bits were shifted, get the next byte.
2592             if (++tncBitCount == 8) 
2593             {
2594                 tncBitCount = 0;
2595
2596                 // If everything was sent, transmit closing flags.
2597                 if (++tncIndex == tncLength) 
2598                 {
2599                     tncIndex = 0;
2600                     tncShift = 0x7e;
2601                     tncMode = TNC_TX_END;
2602                 } else
2603                     tncShift = tncBuffer[tncIndex];
2604
2605             } else
2606                 tncShift = tncShift >> 1;
2607
2608             break;
2609
2610         case TNC_TX_END:
2611             // The variable tncShift contains the lastest data byte.
2612             // NRZI enocde the data stream. 
2613             if ((tncShift & 0x01) == 0x00)
2614                 if (tncTxBit == 0)
2615                     tncTxBit = 1;
2616                 else
2617                     tncTxBit = 0;
2618
2619             // If all the bits were shifted, get the next one.
2620             if (++tncBitCount == 8) 
2621             {
2622                 tncBitCount = 0;
2623                 tncShift = 0x7e;
2624     
2625                 // Transmit two closing flags.
2626                 if (++tncIndex == 2) 
2627                 {
2628                     tncMode = TNC_TX_READY;
2629
2630                     // Tell the TNC time interrupt to stop generating the frequency words.
2631                     tncDataMode = TNC_MODE_STANDBY;
2632
2633                     // Key off the DDS.
2634                     output_low (IO_OSK);
2635                     output_low (IO_PTT);
2636                     ddsSetMode (DDS_MODE_POWERDOWN);
2637
2638                     return;
2639                 } // END if
2640             } else
2641                 tncShift = tncShift >> 1;
2642
2643             break;
2644     } // END switch
2645 }
2646
2647 /**
2648  *   Method that is called every 104uS to transmit the 9600bps FSK data stream.
2649  */
2650 void tnc9600TimerTick()
2651 {
2652
2653 }
2654
2655 /**
2656  *    Write character to the TNC buffer.  Maintain the pointer
2657  *    and length to the buffer.  The pointer tncBufferPnt and tncLength
2658  *    must be set before calling this function for the first time.
2659  * 
2660  *    @param character to save to telemetry buffer
2661  */
2662 void tncTxByte (uint8_t character)
2663 {
2664     *tncBufferPnt++ = character;
2665     ++tncLength;
2666 }
2667
2668 /**
2669  *   Generate the GPS NMEA standard UTC time stamp.  Data is written through the tncTxByte
2670  *   callback function.
2671  */
2672 void tncNMEATime()
2673 {
2674     // UTC of position fix.
2675     printf (tncTxByte, "%02d%02d%02d,", gpsPosition.hours, gpsPosition.minutes, gpsPosition.seconds);
2676 }
2677
2678 /**
2679  *   Generate the GPS NMEA standard latitude/longitude fix.  Data is written through the tncTxByte
2680  *   callback function.
2681  */
2682 void tncNMEAFix()
2683 {
2684     uint8_t dirChar;
2685     uint32_t coord, coordMin;
2686
2687     // Latitude value.
2688     coord = gpsPosition.latitude;
2689
2690     if (gpsPosition.latitude < 0) 
2691     {
2692         coord = gpsPosition.latitude * -1;
2693         dirChar = 'S';
2694     } else {
2695         coord = gpsPosition.latitude;
2696         dirChar = 'N';
2697     }
2698
2699     coordMin = (coord % 3600000) / 6;
2700     printf (tncTxByte, "%02ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar);
2701
2702
2703     // Longitude value.
2704     if (gpsPosition.longitude < 0) 
2705     {
2706         coord = gpsPosition.longitude * - 1;
2707         dirChar = 'W';
2708     } else {
2709         coord = gpsPosition.longitude;
2710         dirChar = 'E';
2711     }
2712
2713     coordMin = (coord % 3600000) / 6;
2714     printf (tncTxByte, "%03ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar);
2715
2716 }
2717
2718 /**
2719  *   Generate the GPS NMEA-0183 $GPGGA packet.  Data is written through the tncTxByte
2720  *   callback function.
2721  */
2722 void tncGPGGAPacket()
2723 {
2724     // Generate the GPGGA message.
2725     printf (tncTxByte, "$GPGGA,");
2726
2727     // Standard NMEA time.
2728     tncNMEATime();
2729
2730     // Standard NMEA-0183 latitude/longitude.
2731     tncNMEAFix();
2732
2733     // GPS status where 0: not available, 1: available
2734     if (gpsGetFixType() != GPS_NO_FIX)
2735         printf (tncTxByte, "1,");
2736     else
2737         printf (tncTxByte, "0,");
2738
2739     // Number of visible birds.
2740     printf (tncTxByte, "%02d,", gpsPosition.trackedSats);
2741
2742     // DOP
2743     printf (tncTxByte, "%ld.%01ld,", gpsPosition.dop / 10, gpsPosition.dop % 10);
2744
2745     // Altitude in meters.
2746     printf (tncTxByte, "%ld.%02ld,M,,M,,", (int32_t) (gpsPosition.altitudeCM / 100l), (int32_t) (gpsPosition.altitudeCM % 100));
2747
2748     // Checksum, we add 1 to skip over the $ character.
2749     printf (tncTxByte, "*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1));
2750 }
2751
2752 /**
2753  *   Generate the GPS NMEA-0183 $GPRMC packet.  Data is written through the tncTxByte
2754  *   callback function.
2755  */
2756 void tncGPRMCPacket()
2757 {
2758     uint32_t temp;
2759
2760     // Generate the GPRMC message.
2761     printf (tncTxByte, "$GPRMC,");
2762
2763     // Standard NMEA time.
2764     tncNMEATime();
2765
2766     // GPS status.
2767     if (gpsGetFixType() != GPS_NO_FIX)
2768         printf (tncTxByte, "A,");
2769     else
2770         printf (tncTxByte, "V,");
2771
2772     // Standard NMEA-0183 latitude/longitude.
2773     tncNMEAFix();
2774
2775     // Speed knots and heading.
2776     temp = (int32_t) gpsPosition.hSpeed * 75000 / 385826;
2777     printf (tncTxByte, "%ld.%ld,%ld.%ld,", (int16_t) (temp / 10), (int16_t) (temp % 10), gpsPosition.heading / 10, gpsPosition.heading % 10);
2778
2779     // Date
2780     printf (tncTxByte, "%02d%02d%02ld,,", gpsPosition.day, gpsPosition.month, gpsPosition.year % 100);
2781
2782     // Checksum, skip over the $ character.
2783     printf (tncTxByte, "*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1));
2784 }
2785
2786 /**
2787  *   Generate the plain text status packet.  Data is written through the tncTxByte
2788  *   callback function.
2789  */
2790 void tncStatusPacket(int16_t temperature)
2791 {
2792     uint16_t voltage;
2793
2794     // Plain text telemetry.
2795     printf (tncTxByte, ">ANSR ");
2796     
2797     // Display the flight time.
2798     printf (tncTxByte, "%02U:%02U:%02U ", timeHours, timeMinutes, timeSeconds);
2799     
2800     // Altitude in feet.
2801     printf (tncTxByte, "%ld' ", gpsPosition.altitudeFeet);
2802     
2803     // Peak altitude in feet.
2804     printf (tncTxByte, "%ld'pk ", gpsGetPeakAltitude());
2805     
2806     // GPS hdop or pdop
2807     printf (tncTxByte, "%lu.%lu", gpsPosition.dop / 10, gpsPosition.dop % 10);
2808
2809     // The text 'pdop' for a 3D fix, 'hdop' for a 2D fix, and 'dop' for no fix.
2810     switch (gpsGetFixType()) 
2811     {
2812         case GPS_NO_FIX:
2813             printf (tncTxByte, "dop ");
2814             break;
2815
2816         case GPS_2D_FIX:
2817             printf (tncTxByte, "hdop ");
2818             break;
2819
2820
2821         case GPS_3D_FIX:
2822             printf (tncTxByte, "pdop ");
2823             break;
2824     } // END switch
2825
2826     // Number of satellites in the solution.
2827     printf (tncTxByte, "%utrk ", gpsPosition.trackedSats);
2828     
2829     // Display main bus voltage.
2830     voltage = adcGetMainBusVolt();
2831     printf (tncTxByte, "%lu.%02luvdc ", voltage / 100, voltage % 100);
2832     
2833     // Display internal temperature.
2834     printf (tncTxByte, "%ld.%01ldF ", temperature / 10, abs(temperature % 10));
2835     
2836     // Print web address link.
2837     printf (tncTxByte, "www.kd7lmo.net");
2838 }  
2839
2840 /** 
2841  *    Prepare an AX.25 data packet.  Each time this method is called, it automatically
2842  *    rotates through 1 of 3 messages.
2843  *
2844  *    @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK
2845  */
2846 void tncTxPacket(TNC_DATA_MODE dataMode)
2847 {
2848     int16_t temperature;
2849     uint16_t crc;
2850
2851     // Only transmit if there is not another message in progress.
2852     if (tncMode != TNC_TX_READY)
2853         return;
2854
2855     // Log the battery and reference voltage before we start the RF chain.
2856     sysLogVoltage();
2857
2858     // We need to read the temperature sensor before we setup the DDS since they share a common clock pin.
2859     temperature = lm92GetTemp();
2860
2861     // Log the system temperature every time we transmit a packet.
2862     logType (LOG_TEMPERATURE);
2863     logInt16 (temperature);
2864
2865     // Configure the DDS for the desired operational.
2866     tncSetMode (dataMode);
2867
2868     // Set a pointer to our TNC output buffer.
2869     tncBufferPnt = tncBuffer;
2870
2871     // Set the message length counter.
2872     tncLength = 0;
2873
2874     // Determine the contents of the packet.
2875     switch (tncPacketType) 
2876     {
2877         case TNC_BOOT_MESSAGE:
2878             printf (tncTxByte, ">ANSR Pico Beacon - V3.05");
2879
2880             // Select the next packet we will generate.
2881             tncPacketType = TNC_STATUS;
2882             break;
2883
2884         case TNC_STATUS:
2885             tncStatusPacket(temperature);
2886
2887             // Select the next packet we will generate.
2888             tncPacketType = TNC_GGA;
2889             break;
2890
2891         case TNC_GGA:
2892             tncGPGGAPacket();
2893
2894             // Select the next packet we will generate.
2895             tncPacketType = TNC_RMC;
2896             break;
2897
2898         case TNC_RMC:
2899             tncGPRMCPacket();
2900
2901             // Select the next packet we will generate.
2902             tncPacketType = TNC_STATUS;
2903             break;
2904     }
2905
2906     // Add the end of message character.
2907     printf (tncTxByte, "\015");
2908
2909     // Calculate the CRC for the header and message.
2910     crc = sysCRC16(TNC_AX25_HEADER, sizeof(TNC_AX25_HEADER), 0xffff);
2911     crc = sysCRC16(tncBuffer, tncLength, crc ^ 0xffff);
2912
2913     // Save the CRC in the message.
2914     *tncBufferPnt++ = crc & 0xff;
2915     *tncBufferPnt = (crc >> 8) & 0xff;
2916
2917     // Update the length to include the CRC bytes.
2918     tncLength += 2;
2919
2920     // Prepare the variables that are used in the real-time clock interrupt.
2921     tncBitCount = 0;
2922     tncShift = 0x7e;
2923     tncTxBit = 0;
2924     tncIndex = 0;
2925     tncMode = TNC_TX_SYNC;
2926
2927     // Turn on the PA chain.
2928     output_high (IO_PTT);
2929
2930     // Wait for the PA chain to power up.
2931     delay_ms (10);
2932
2933     // Key the DDS.
2934     output_high (IO_OSK);
2935
2936     // Log the battery and reference voltage just after we key the transmitter.
2937     sysLogVoltage();
2938 }
2939
2940 /** @} */
2941
2942 uint32_t counter;
2943
2944 uint8_t bitIndex;
2945 uint8_t streamIndex;
2946 uint8_t value;
2947
2948 uint8_t bitStream[] = { 0x10, 0x20, 0x30 };
2949
2950 void init()
2951 {
2952     counter = 0;
2953     bitIndex = 0;
2954     streamIndex = 0;
2955     value = bitStream[0];
2956 }
2957
2958 void test()
2959 {
2960     counter += 0x10622d;
2961
2962     CCP_1 = (uint16_t) ((counter >> 16) & 0xffff);
2963
2964     if ((value & 0x80) == 0x80)
2965         setup_ccp1 (CCP_COMPARE_SET_ON_MATCH);
2966     else
2967         setup_ccp1 (CCP_COMPARE_CLR_ON_MATCH);
2968
2969     if (++bitIndex == 8)
2970     {
2971         bitIndex = 0;
2972         
2973         if (++streamIndex == sizeof(bitStream))
2974         {
2975             streamIndex = 0;
2976         }
2977
2978         value = bitStream[streamIndex];
2979     } else
2980         value = value << 1;
2981 }
2982
2983 // This is where we go after reset.
2984 void main()
2985 {
2986     uint8_t i, utcSeconds, lockLostCounter;
2987
2988 test();
2989
2990     // Configure the basic systems.
2991     sysInit();
2992
2993     // Wait for the power converter chains to stabilize.
2994     delay_ms (100);
2995
2996     // Setup the subsystems.
2997     adcInit();
2998     flashInit();
2999     gpsInit();
3000     logInit();
3001     timeInit();
3002     serialInit();
3003     tncInit();
3004
3005     // Program the DDS.
3006     ddsInit();
3007
3008     // Turn off the LED after everything is configured.
3009     output_low (IO_LED);
3010
3011     // Check for the diagnostics plug, otherwise we'll continue to boot.
3012     diagPort();
3013
3014     // Setup our interrupts.
3015     enable_interrupts(GLOBAL);
3016     enable_interrupts(INT_CCP1);
3017
3018     // Turn on the GPS engine.
3019     gpsPowerOn();
3020
3021     // Allow the GPS engine to boot.
3022     delay_ms (250);
3023
3024     // Initialize the GPS engine.
3025     while (!gpsSetup());
3026
3027     // Charge the ADC filters.
3028     for (i = 0; i < 32; ++i)
3029         adcUpdate();
3030
3031     // Log startup event.
3032     logType (LOG_BOOTED);
3033     logUint8 (gpsPosition.month);
3034     logUint8 (gpsPosition.day);
3035     logUint8 (gpsPosition.year & 0xff);
3036
3037     logUint8 (gpsPosition.hours);
3038     logUint8 (gpsPosition.minutes);
3039     logUint8 (gpsPosition.seconds);
3040
3041     // Transmit software version packet on start up.
3042     tncTxPacket(TNC_MODE_1200_AFSK);
3043
3044     // Counters to send packets if the GPS time stamp is not available.
3045     lockLostCounter = 5;
3046     utcSeconds = 55;
3047   
3048     // This is the main loop that process GPS data and waits for the once per second timer tick.
3049     for (;;) 
3050     {
3051         // Read the GPS engine serial port FIFO and process the GPS data.
3052         gpsUpdate();
3053
3054         if (gpsIsReady()) 
3055         {
3056             // Start the flight timer when we get a valid 3D fix.
3057             if (gpsGetFixType() == GPS_3D_FIX)
3058                 timeSetRunFlag();
3059
3060             // Generate our packets based on the GPS time.
3061             if (tncIsTimeSlot(gpsPosition.seconds))
3062                  tncTxPacket(TNC_MODE_1200_AFSK);
3063
3064             // Sync the internal clock to GPS UTC time.
3065             utcSeconds = gpsPosition.seconds;
3066
3067             // This counter is reset every time we receive the GPS message.
3068             lockLostCounter = 0;
3069
3070             // Log the data to flash.
3071             sysLogGPSData();            
3072         } // END if gpsIsReady   
3073
3074         // Processing that occurs once a second.
3075         if (timeIsUpdate()) 
3076         {
3077             // We maintain the UTC time in seconds if we shut off the GPS engine or it fails.
3078             if (++utcSeconds == 60)
3079                 utcSeconds = 0;
3080
3081             // If we loose information for more than 5 seconds, 
3082             // we will determine when to send a packet based on internal time.
3083             if (lockLostCounter == 5) 
3084             {
3085                 if (tncIsTimeSlot(utcSeconds))
3086                     tncTxPacket(TNC_MODE_1200_AFSK);
3087             } else
3088                 ++lockLostCounter;
3089
3090             // Update the ADC filters.
3091             adcUpdate();
3092
3093             if (timeHours == 5 && timeMinutes == 0 && timeSeconds == 0)
3094                 gpsPowerOff();
3095
3096         } // END if timeIsUpdate
3097
3098     } // END for
3099 }
3100
3101
3102