├── PecanPico.ino ├── README.md ├── aprs.cpp ├── aprs.h ├── ax25.cpp ├── ax25.h ├── config.h ├── debug.cpp ├── debug.h ├── gps.cpp ├── gps.h ├── modem.cpp ├── modem.h ├── radio.cpp ├── radio.h ├── radio_adf7012.cpp ├── radio_adf7012.h ├── radio_hx1.cpp ├── radio_hx1.h ├── radio_mx146.cpp ├── radio_mx146.h ├── radio_si446x.cpp ├── radio_si446x.h ├── sensors.cpp ├── sensors.h └── trackuino.h /PecanPico.ino: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | // Refuse to compile on arduino version 21 or lower. 22 includes an 19 | // optimization of the USART code that is critical for real-time operation. 20 | #if ARDUINO < 22 21 | #error "Oops! We need Arduino 22 or later" 22 | #endif 23 | 24 | // Trackuino custom libs 25 | #include "aprs.h" 26 | #include "ax25.h" 27 | //#include "buzzer.h" 28 | #include "config.h" 29 | #include "debug.h" 30 | #include "gps.h" 31 | #include "modem.h" 32 | #include "radio.h" 33 | 34 | // only include the selected radio 35 | #if RADIO_CLASS == RadioHx1 36 | #include "radio_hx1.h" 37 | #endif 38 | #if RADIO_CLASS == RadioMx146 39 | #include "radio_mx146.h" 40 | #endif 41 | #if RADIO_CLASS == RadioAdf7012 42 | #include "radio_adf7012.h" 43 | #endif 44 | #if RADIO_CLASS == RadioSi446x 45 | #include "radio_si446x.h" 46 | #endif 47 | 48 | #include "sensors.h" 49 | #include 50 | 51 | 52 | // Arduino/AVR libs 53 | #include 54 | #include 55 | 56 | #include 57 | #include 58 | #include 59 | 60 | //unsigned long next_tx_millis; 61 | volatile int wd_counter; 62 | bool newPositionStillUnknown; 63 | //volatile boolean f_wdt=1; 64 | bool gpsIsOn; 65 | //unsigned int powerlevel; 66 | 67 | 68 | void disable_bod_and_sleep() 69 | { 70 | /* This will turn off brown-out detection while 71 | * sleeping. Unfortunately this won't work in IDLE mode. 72 | * Relevant info about BOD disabling: datasheet p.44 73 | * 74 | * Procedure to disable the BOD: 75 | * 76 | * 1. BODSE and BODS must be set to 1 77 | * 2. Turn BODSE to 0 78 | * 3. BODS will automatically turn 0 after 4 cycles 79 | * 80 | * The catch is that we *must* go to sleep between 2 81 | * and 3, ie. just before BODS turns 0. 82 | */ 83 | unsigned char mcucr; 84 | 85 | cli(); 86 | mcucr = MCUCR | (_BV(BODS) | _BV(BODSE)); 87 | MCUCR = mcucr; 88 | MCUCR = mcucr & (~_BV(BODSE)); 89 | sei(); 90 | sleep_mode(); // Go to sleep 91 | } 92 | 93 | void power_save() 94 | { 95 | /* Enter power saving mode. SLEEP_MODE_IDLE is the least saving 96 | * mode, but it's the only one that will keep the UART running. 97 | * In addition, we need timer0 to keep track of time, timer 1 98 | * to drive the buzzer and timer2 to keep pwm output at its rest 99 | * voltage. TK: changed to conditional SLEEP_MODE_PWR_DOWN 100 | */ 101 | if (! modem_busy()) { // Don't sleep if we're txing. 102 | 103 | if (newPositionStillUnknown == true) 104 | { 105 | 106 | // Here we still need the serial port to 107 | // capture GPS NMEA data 108 | set_sleep_mode(SLEEP_MODE_IDLE); 109 | sleep_enable(); 110 | power_adc_disable(); 111 | power_spi_disable(); 112 | power_twi_disable(); 113 | power_timer1_disable(); 114 | digitalWrite(LED_PIN, LOW); 115 | sleep_mode(); // Go to sleep 116 | 117 | } 118 | else 119 | { 120 | //Serial.end(); 121 | // Now that we know the position, the serial 122 | // port is no longer needed 123 | // and we can go to full SLEEP_MODE_PWR_DOWN. 124 | // Only the watchdog interrupt will wake us up. 125 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); 126 | 127 | // Shut down GPS hardware 128 | 129 | digitalWrite(GPS_POWER_PIN, LOW); 130 | delay(500); // Give time to discharge power capacitor of GPS board 131 | gpsIsOn = false; 132 | sleep_enable(); 133 | power_adc_disable(); 134 | power_spi_disable(); 135 | power_twi_disable(); 136 | power_timer1_disable(); 137 | 138 | digitalWrite(LED_PIN, LOW); 139 | disable_bod_and_sleep(); // Go to sleep 140 | //sleep_mode(); // Go to sleep 141 | } 142 | 143 | 144 | digitalWrite(LED_PIN, HIGH); 145 | 146 | sleep_disable(); // Resume after wake up 147 | power_all_enable(); 148 | 149 | } 150 | } 151 | 152 | //**************************************************************** 153 | // 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms 154 | // 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec 155 | void setup_watchdog(int ii) 156 | { 157 | byte bb; 158 | int ww; 159 | if (ii > 9 ) ii=9; 160 | bb=ii & 7; 161 | if (ii > 7) bb|= (1<<5); 162 | bb|= (1<= (int)(APRS_PERIOD_SECONDS / 8)) 223 | // if (millis() >= next_tx_millis) 224 | { 225 | // make sure the serial port is set properly after sleep period 226 | //Serial.begin(GPS_BAUDRATE); 227 | 228 | // Show modem ISR stats from the previous transmission 229 | #ifdef DEBUG_MODEM 230 | modem_debug(); 231 | #endif 232 | aprs_send(); 233 | 234 | // next_tx_millis = millis() + APRS_PERIOD; 235 | wd_counter = 0; 236 | newPositionStillUnknown = true; 237 | // Now we need the GPS again to find the new position. 238 | if(gpsIsOn == false) 239 | { 240 | pinMode(GPS_POWER_PIN, OUTPUT); // In case this config was forgotten in a brown out 241 | //digitalWrite(GPS_POWER_PIN, HIGH); // turn GPS properly off 242 | //delay(500); 243 | digitalWrite(GPS_POWER_PIN, HIGH); // turn GPS on 244 | delay(500); 245 | gps_setup(); 246 | // if (Serial.available()) // check if Venus GPS is really on and delivers NEMA 247 | // { 248 | gpsIsOn = true; 249 | // } 250 | // else 251 | // { 252 | // pinMode(GPS_RESET_PIN, OUTPUT); // In case this config was forgotten in a brown out 253 | // digitalWrite(GPS_RESET_PIN, LOW); // Reset the Venus GPS to make 100% sure it starts properly 254 | // delay(100); 255 | // digitalWrite(GPS_RESET_PIN, HIGH); 256 | // delay(100); 257 | // pinMode(GPS_RESET_PIN, INPUT); 258 | // } 259 | 260 | } 261 | } 262 | 263 | if (Serial.available() && newPositionStillUnknown) 264 | // if (Serial.available()) 265 | { 266 | c = Serial.read(); 267 | if (gps_decode(c)) 268 | { 269 | // We have received and decoded our location 270 | newPositionStillUnknown = false; 271 | 272 | blink3(); // Show the user that we have a valid GPS lease 273 | if(gps_check_satellite()) 274 | { 275 | blink3(); // show that ISS is in view 276 | // upset the watchdog so that we permanently keep transmitting while ISS is in view 277 | wd_counter = (int)(APRS_PERIOD_SECONDS / 8) + 20; 278 | } 279 | 280 | // if (gps_altitude > BUZZER_ALTITUDE) 281 | // buzzer_off(); // In space, no one can hear you buzz 282 | // else 283 | // buzzer_on(); 284 | } 285 | 286 | } 287 | else 288 | { 289 | power_save(); 290 | } 291 | } 292 | 293 | 294 | void blink3() // for debug 295 | { 296 | 297 | digitalWrite(LED_PIN, LOW); 298 | delay(100); 299 | digitalWrite(LED_PIN, HIGH); 300 | delay(100); 301 | digitalWrite(LED_PIN, LOW); 302 | delay(100); 303 | digitalWrite(LED_PIN, HIGH); 304 | delay(100); 305 | digitalWrite(LED_PIN, LOW); 306 | delay(100); 307 | digitalWrite(LED_PIN, HIGH); 308 | delay(100); 309 | 310 | } 311 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PecanPico 2 | ========= 3 | 4 | APRS Tracker for balloons based on a very old version of Trackuino with a lot of modifications by KT5TK 5 | -------------------------------------------------------------------------------- /aprs.cpp: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #include "config.h" 19 | #include "ax25.h" 20 | #include "gps.h" 21 | #include "aprs.h" 22 | #include "sensors.h" 23 | #include "modem.h" 24 | #include 25 | #include 26 | #if defined(ARDUINO) && ARDUINO >= 100 27 | #include 28 | #else 29 | #include 30 | #endif 31 | 32 | //extern void *__bss_end; 33 | //extern void *__brkval; 34 | 35 | 36 | // Module globals 37 | static unsigned int telemetry_counter = 0; 38 | static unsigned int loss_of_gps_counter = 0; 39 | static unsigned int ariss_counter = 0; 40 | 41 | char gps_aprs_lat_old[10]; 42 | char gps_aprs_lon_old[10]; 43 | char gps_time_old[7]; 44 | 45 | /* 46 | struct s_address { 47 | {const char[7] *d_call, const int *d_id}, 48 | {const char[7] *s_call, const int *s_id}, 49 | #ifdef DIGI_PATH1 50 | {const char[7] *d1_call, const int *d1_id}, 51 | #endif 52 | #ifdef DIGI_PATH2 53 | {const char[7] *d2_call, const int *d2_id} 54 | #endif 55 | } ; 56 | 57 | 58 | 59 | //s_address *addresses; 60 | 61 | */ 62 | 63 | 64 | 65 | 66 | // changed from const 67 | struct s_address addresses[] = 68 | { 69 | {D_CALLSIGN, D_CALLSIGN_ID}, // Destination callsign 70 | {S_CALLSIGN, S_CALLSIGN_ID}, // Source callsign (-11 = balloon, -9 = car) 71 | #ifdef DIGI_PATH1 72 | {DIGI_PATH1, DIGI_PATH1_TTL}, // Digi1 (first digi in the chain) 73 | #endif 74 | #ifdef DIGI_PATH2 75 | {DIGI_PATH2, DIGI_PATH2_TTL}, // Digi2 (second digi in the chain) 76 | #endif 77 | }; 78 | 79 | 80 | // Module functions 81 | float meters_to_feet(float m) 82 | { 83 | // 10000 ft = 3048 m 84 | return m / 0.3048; 85 | } 86 | 87 | long addtime( unsigned long original, unsigned long seconds2add) 88 | { 89 | 90 | 91 | unsigned int hours = original / 10000UL; 92 | unsigned int minutes = (original % 10000UL) / 100UL; 93 | unsigned int seconds = (original % 10000UL) % 100UL; 94 | 95 | seconds += seconds2add; 96 | 97 | minutes += seconds / 60UL; 98 | seconds = seconds % 60UL; 99 | 100 | hours += minutes / 60UL; 101 | minutes = minutes % 60UL; 102 | hours = hours % 24UL; 103 | 104 | return 10000UL * (unsigned long)hours + 100UL * (unsigned long)minutes + (unsigned long)seconds; 105 | 106 | } 107 | 108 | // Exported functions 109 | void aprs_send() 110 | { 111 | char temp[12]; // Temperature (int/ext) 112 | unsigned long frequency; 113 | float altitude = 0; 114 | int value; 115 | short bmp085temp; 116 | long bmp085pressure; 117 | 118 | 119 | bmp085temp = bmp085GetTemperature(bmp085ReadUT()) / 10; // Must read temperature from BMP081 before you can read pressure! 120 | bmp085pressure = (float)bmp085GetPressure(bmp085ReadUP()) ; 121 | 122 | 123 | 124 | // Send a telemetry package 125 | ax25_send_header(addresses, sizeof(addresses)/sizeof(s_address)); 126 | ax25_send_string("T#"); // Telemetry Report 127 | telemetry_counter++; 128 | if (telemetry_counter > 999) { 129 | telemetry_counter = 0; 130 | } 131 | snprintf(temp, 4, "%03d", telemetry_counter); 132 | ax25_send_string(temp); // sequential id 133 | ax25_send_byte(','); 134 | 135 | analogReference(DEFAULT); 136 | 137 | // adc0 = Battery Voltage ; Write to T# pos 1 138 | analogRead(A0); // Disregard the 1st conversion after changing ref 139 | value = analogRead(A0); // read ADC 140 | snprintf(temp, 4, "%03d", value/4L); // Arduino has 1023 max.; APRS has 255 max.; So divide by 4 141 | ax25_send_string(temp); // write 8 bit ADC value 142 | ax25_send_byte(','); 143 | 144 | // adc7 = uBlox Voltage ; Write to T# pos 2 145 | analogRead(A7); // Disregard the 1st conversion after changing ref 146 | value = analogRead(A7); // read ADC 147 | snprintf(temp, 4, "%03d", value/4L); // Arduino has 1023 max.; APRS has 255 max.; So divide by 4 148 | ax25_send_string(temp); // write 8 bit ADC value 149 | ax25_send_byte(','); 150 | 151 | 152 | // fill the 3rd telemetry value with a number that's proportional to the altitude: 153 | snprintf(temp, 4, "%03d", (long)(meters_to_feet(gps_altitude) + 0.5)/1000L); // Altitude in kfeet 154 | ax25_send_string(temp); // write 8 bit value 155 | ax25_send_byte(','); 156 | 157 | 158 | // adc3 = PLL MUX, (skip) 159 | 160 | 161 | // adc6 = ADF7012 MUXOUT; Write to T# pos 4 162 | snprintf(temp, 4, "%03d", modem_get_powerlevel()); 163 | ax25_send_string(temp); // write power level from last tx cycle 164 | ax25_send_byte(','); 165 | 166 | 167 | 168 | // adc3 = Attach a LM50 temp sensor here ; Write to T# pos 5 169 | analogRead(A3); // Disregard the 1st conversion after changing ref 170 | value = analogRead(A3); // read ADC 171 | snprintf(temp, 4, "%03d", value/4L); // Arduino has 1023 max.; APRS has 255 max.; So divide by 4 172 | ax25_send_string(temp); // write 8 bit ADC value 173 | ax25_send_byte(','); 174 | 175 | 176 | 177 | /* 178 | 179 | // fill the fourth telemetry value with the loss_of_gps_counter 180 | snprintf(temp, 4, "%03d", loss_of_gps_counter); 181 | ax25_send_string(temp); // write 8 bit value 182 | ax25_send_byte(','); 183 | 184 | // fill the fifth telemetry value with the number of ARISS packets transmitted 185 | snprintf(temp, 4, "%03d", ariss_counter); 186 | ax25_send_string(temp); // write 8 bit value 187 | ax25_send_byte(','); 188 | 189 | */ 190 | 191 | 192 | ax25_send_string("0000000"); // More room to send binary info. Still unused bits... 193 | //Todo: PLL MUX info 194 | if(satInView) 195 | { 196 | ax25_send_byte('1'); 197 | 198 | frequency = RADIO_FREQUENCY_ARISS; 199 | ariss_counter++; 200 | if(ariss_counter > 199) // prevent overflow but still produce interpretable graphs 201 | { 202 | ariss_counter = 100; // this will likely never happen :) However just in case... 203 | } 204 | 205 | s_address addresses[] = { 206 | {D_CALLSIGN, D_CALLSIGN_ID}, // Destination callsign still APRS 207 | {S_CALLSIGN, S_CALLSIGN_ID}, // Source callsign (-11 = balloon, -9 = car) 208 | #ifdef DIGI_PATH1 209 | {DIGI_PATH1_SAT, DIGI_PATH1_TTL_SAT}, // Digi1 = ARISS 210 | #endif 211 | #ifdef DIGI_PATH2 212 | {DIGI_PATH2_SAT, DIGI_PATH2_TTL_SAT}, // Digi2 = SGATE 213 | #endif 214 | }; 215 | 216 | } 217 | else 218 | { 219 | ax25_send_byte('0'); 220 | 221 | frequency = gps_get_region_frequency(); 222 | 223 | s_address addresses[] = { 224 | {D_CALLSIGN, D_CALLSIGN_ID}, // Destination callsign changed back to normal 225 | {S_CALLSIGN, S_CALLSIGN_ID}, // Source callsign (-11 = balloon, -9 = car) 226 | #ifdef DIGI_PATH1 227 | {DIGI_PATH1, DIGI_PATH1_TTL}, // Digi1 (first digi in the chain) 228 | #endif 229 | #ifdef DIGI_PATH2 230 | {DIGI_PATH2, DIGI_PATH2_TTL}, // Digi2 (second digi in the chain) 231 | #endif 232 | }; 233 | 234 | } 235 | 236 | 237 | // Announce the next transmission 238 | // ax25_send_byte(' '); 239 | // snprintf(temp, 4, "%lu", frequency/1000000UL); 240 | // ax25_send_string(temp); 241 | // ax25_send_byte('.'); 242 | // snprintf(temp, 7, "%03lu", (frequency%1000000UL)/1000UL); 243 | // ax25_send_string(temp); 244 | // ax25_send_string("MHz"); // Comment 245 | // ax25_send_string(" next ISS:"); 246 | // snprintf(temp, 7, "%d", iss_lat); 247 | // ax25_send_string(temp); // ISS lat 248 | // ax25_send_byte('/'); 249 | // snprintf(temp, 7, "%d", iss_lon); 250 | // ax25_send_string(temp); // ISS lon 251 | // ax25_send_byte('/'); 252 | // snprintf(temp, 7, "%u", iss_datapoint); 253 | // ax25_send_string(temp); 254 | 255 | // if(newPositionStillUnknown) // append the last known position in case we lost GPS 256 | // { 257 | // ax25_send_string(" old:"); 258 | // ax25_send_string(old_lease); 259 | // } 260 | 261 | // some output for debugging via packet: 262 | // ax25_send_string(gps_date); // 241211 = Christmas 2011 263 | 264 | // ax25_send_string(dtostrf(gps_get_variable(),7,4,temp)); 265 | // ax25_send_byte('/'); 266 | // ax25_send_string(dtostrf(gps_get_lat(),7,4,temp)); 267 | // ax25_send_byte('/'); 268 | // ax25_send_string(dtostrf(gps_get_lon(),7,4,temp)); 269 | // ax25_send_byte('/'); 270 | // snprintf(temp, 7, "%lu", gps_get_time()); 271 | // ax25_send_string(temp); 272 | // ax25_send_byte('/'); 273 | // snprintf(temp, 7, "%lu", gps_get_date()); 274 | // ax25_send_string(temp); 275 | // snprintf(temp, 7, "%du", p13.Sx); 276 | // ax25_send_string(temp); 277 | 278 | ax25_send_footer(); 279 | ax25_flush_frame(); // Tell the modem to go 280 | 281 | 282 | 283 | // Wait a few seconds (Else aprs.fi reports "[Rate limited (< 5 sec)]") 284 | delay(6000); 285 | 286 | 287 | ax25_send_header(addresses, sizeof(addresses)/sizeof(s_address)); 288 | ax25_send_byte('/'); // Report w/ timestamp, no APRS messaging. $ = NMEA raw data 289 | 290 | altitude = gps_get_altitude(); 291 | 292 | if(newPositionStillUnknown) 293 | { 294 | //use the old position 295 | strcpy(gps_aprs_lat, gps_aprs_lat_old); 296 | strcpy(gps_aprs_lon, gps_aprs_lon_old); 297 | 298 | snprintf(gps_time, 7, "%06ld", addtime( atol(gps_time_old), APRS_PERIOD_SECONDS ) ); 299 | 300 | 301 | //calculate altitude from pressure 302 | altitude = (float)44330 * (1 - pow((float)bmp085pressure / P0, 0.190295)); 303 | // altitude = 0.0; 304 | 305 | } 306 | // else 307 | // { 308 | // altitude = gps_get_altitude(); 309 | // } 310 | if (altitude < 1000.0) // At low altitudes pressure altitude is more precise than GPS 311 | { 312 | altitude = (float)44330 * (1 - pow((float)bmp085pressure / P0, 0.190295)); 313 | } 314 | // ax25_send_string("021709z"); // 021709z = 2nd day of the month, 17:09 zulu (UTC/GMT) 315 | ax25_send_string(gps_time); // 170915 = 17h:09m:15s zulu (not allowed in Status Reports) 316 | ax25_send_byte('h'); 317 | ax25_send_string(gps_aprs_lat); // Lat: 38deg and 22.20 min (.20 are NOT seconds, but 1/100th of minutes) 318 | ax25_send_byte(APRS_SYMBOL_TABLE); // Symbol table 319 | ax25_send_string(gps_aprs_lon); // Lon: 000deg and 25.80 min 320 | ax25_send_byte(APRS_SYMBOL_ID); // Symbol: /O=balloon, /-=QTH, \N=buoy 321 | snprintf(temp, 4, "%03d", (int)(gps_course + 0.5)); 322 | ax25_send_string(temp); // Course (degrees) 323 | ax25_send_byte('/'); // and 324 | snprintf(temp, 4, "%03d", (int)(gps_speed + 0.5)); 325 | ax25_send_string(temp); // speed (knots) 326 | ax25_send_string("/A="); // Altitude (feet). Goes anywhere in the comment area 327 | snprintf(temp, 7, "%06ld", (long)(meters_to_feet(altitude) + 0.5)); 328 | ax25_send_string(temp); 329 | ax25_send_string(" "); 330 | 331 | ax25_send_string(dtostrf((getUBatt()/1000.0),4,1,temp)); 332 | ax25_send_string("V "); 333 | // ax25_send_string(itoa(sensors_lm335(), temp, 10)); 334 | // ax25_send_string(itoa(sensors_lm50(), temp, 10)); 335 | 336 | ax25_send_string(itoa(bmp085temp, temp, 10)); 337 | ax25_send_string("C "); 338 | 339 | ax25_send_string(ltoa(bmp085pressure, temp, 10)); 340 | ax25_send_string("Pa "); 341 | 342 | 343 | ax25_send_string(APRS_COMMENT); // Comment 344 | 345 | if(newPositionStillUnknown) 346 | { 347 | 348 | loss_of_gps_counter++; 349 | if(loss_of_gps_counter >= 1000) // make sure we don't get above 1000 350 | { 351 | loss_of_gps_counter = 100; // will show a sawtooth pattern 100 -> 1000 at permanent gps loss. 352 | } 353 | ax25_send_string(" GPS loss "); 354 | snprintf(temp, 4, "%3d", loss_of_gps_counter); 355 | ax25_send_string(temp); // write 8 bit value 356 | 357 | } 358 | else 359 | { 360 | loss_of_gps_counter = 0; 361 | } 362 | 363 | ax25_send_footer(); 364 | ax25_flush_frame(); // Tell the modem to go 365 | strcpy(gps_aprs_lat_old, gps_aprs_lat); 366 | strcpy(gps_aprs_lon_old, gps_aprs_lon); 367 | strcpy(gps_time_old, gps_time); 368 | 369 | 370 | 371 | 372 | modem_set_tx_freq(frequency); // actually do the QSY 373 | 374 | } 375 | 376 | 377 | -------------------------------------------------------------------------------- /aprs.h: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #ifndef __APRS_H__ 19 | #define __APRS_H__ 20 | 21 | #include "gps.h" 22 | #include "trackuino.h" 23 | 24 | void aprs_send(); 25 | long addtime( unsigned long original, unsigned long seconds2add); 26 | #endif 27 | -------------------------------------------------------------------------------- /ax25.cpp: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #include "ax25.h" 19 | #include "config.h" 20 | #include "modem.h" 21 | #if defined(ARDUINO) && ARDUINO >= 100 22 | #include 23 | #else 24 | #include 25 | #endif 26 | // Module globals 27 | unsigned short int crc; 28 | int ones_in_a_row; 29 | 30 | // Module functions 31 | static void 32 | update_crc(unsigned char bit) 33 | { 34 | crc ^= bit; 35 | if (crc & 1) 36 | crc = (crc >> 1) ^ 0x8408; // X-modem CRC poly 37 | else 38 | crc = crc >> 1; 39 | } 40 | 41 | static void 42 | send_byte(unsigned char byte) 43 | { 44 | int i; 45 | for (i = 0; i < 8; i++) { 46 | update_crc((byte >> i) & 1); 47 | if ((byte >> i) & 1) { 48 | // Next bit is a '1' 49 | if (modem_packet_size >= MODEM_MAX_PACKET * 8) // Prevent buffer overrun 50 | return; 51 | modem_packet[modem_packet_size >> 3] |= (1 << (modem_packet_size & 7)); 52 | modem_packet_size++; 53 | if (++ones_in_a_row < 5) continue; 54 | } 55 | // Next bit is a '0' or a zero padding after 5 ones in a row 56 | if (modem_packet_size >= MODEM_MAX_PACKET * 8) // Prevent buffer overrun 57 | return; 58 | modem_packet[modem_packet_size >> 3] &= ~(1 << (modem_packet_size & 7)); 59 | modem_packet_size++; 60 | ones_in_a_row = 0; 61 | } 62 | } 63 | 64 | // Exported functions 65 | void 66 | ax25_send_byte(unsigned char byte) 67 | { 68 | // Wrap around send_byte, but prints debug info 69 | send_byte(byte); 70 | #ifdef DEBUG_AX25 71 | Serial.print((char)byte); 72 | #endif 73 | } 74 | 75 | void 76 | ax25_send_sync() 77 | { 78 | unsigned char byte = 0x00; 79 | int i; 80 | for (i = 0; i < 8; i++, modem_packet_size++) { 81 | if (modem_packet_size >= MODEM_MAX_PACKET * 8) // Prevent buffer overrun 82 | return; 83 | if ((byte >> i) & 1) 84 | modem_packet[modem_packet_size >> 3] |= (1 << (modem_packet_size & 7)); 85 | else 86 | modem_packet[modem_packet_size >> 3] &= ~(1 << (modem_packet_size & 7)); 87 | } 88 | } 89 | 90 | void 91 | ax25_send_flag() 92 | { 93 | unsigned char byte = 0x7e; 94 | int i; 95 | for (i = 0; i < 8; i++, modem_packet_size++) { 96 | if (modem_packet_size >= MODEM_MAX_PACKET * 8) // Prevent buffer overrun 97 | return; 98 | if ((byte >> i) & 1) 99 | modem_packet[modem_packet_size >> 3] |= (1 << (modem_packet_size & 7)); 100 | else 101 | modem_packet[modem_packet_size >> 3] &= ~(1 << (modem_packet_size & 7)); 102 | } 103 | } 104 | 105 | void 106 | ax25_send_string(const char *string) 107 | { 108 | int i; 109 | for (i = 0; string[i]; i++) { 110 | ax25_send_byte(string[i]); 111 | } 112 | } 113 | 114 | void 115 | ax25_send_header(s_address addresses[], int num_addresses) 116 | { 117 | int i, j; 118 | modem_packet_size = 0; 119 | ones_in_a_row = 0; 120 | crc = 0xffff; 121 | 122 | // Send sync ("a bunch of 0s") 123 | for (i = 0; i < 60; i++) 124 | { 125 | ax25_send_sync(); 126 | } 127 | 128 | //start the actual frame. Send 3 of them (one empty frame and the real start) 129 | for (i = 0; i < 3; i++) 130 | { 131 | ax25_send_flag(); 132 | } 133 | 134 | for (i = 0; i < num_addresses; i++) { 135 | // Transmit callsign 136 | for (j = 0; addresses[i].callsign[j]; j++) 137 | send_byte(addresses[i].callsign[j] << 1); 138 | // Transmit pad 139 | for ( ; j < 6; j++) 140 | send_byte(' ' << 1); 141 | // Transmit SSID. Termination signaled with last bit = 1 142 | if (i == num_addresses - 1) 143 | send_byte(('0' + addresses[i].ssid) << 1 | 1); 144 | else 145 | send_byte(('0' + addresses[i].ssid) << 1); 146 | } 147 | 148 | // Control field: 3 = APRS-UI frame 149 | send_byte(0x03); 150 | 151 | // Protocol ID: 0xf0 = no layer 3 data 152 | send_byte(0xf0); 153 | 154 | #ifdef DEBUG_AX25 155 | // Print source callsign 156 | Serial.println(); 157 | Serial.print(addresses[1].callsign); 158 | if (addresses[1].ssid) { 159 | Serial.print('-'); 160 | Serial.print((unsigned int)addresses[1].ssid); 161 | } 162 | Serial.print('>'); 163 | // Destination callsign 164 | Serial.print(addresses[0].callsign); 165 | if (addresses[0].ssid) { 166 | Serial.print('-'); 167 | Serial.print((unsigned int)addresses[0].ssid); 168 | } 169 | for (i = 2; i < num_addresses; i++) { 170 | Serial.print(','); 171 | Serial.print(addresses[i].callsign); 172 | if (addresses[i].ssid) { 173 | Serial.print('-'); 174 | Serial.print((unsigned int)addresses[i].ssid); 175 | } 176 | } 177 | Serial.print(':'); 178 | #endif 179 | } 180 | 181 | void 182 | ax25_send_footer() 183 | { 184 | // Save the crc so that it can be treated it atomically 185 | unsigned short int final_crc = crc; 186 | 187 | // Send the CRC 188 | send_byte(~(final_crc & 0xff)); 189 | final_crc >>= 8; 190 | send_byte(~(final_crc & 0xff)); 191 | 192 | // Signal the end of frame 193 | ax25_send_flag(); 194 | 195 | // Send sync ("a bunch of 0s") 196 | // for (int i = 0; i < 30; i++) 197 | // { 198 | // ax25_send_sync(); 199 | // } 200 | 201 | 202 | #ifdef DEBUG_AX25 203 | Serial.println(); 204 | #endif 205 | } 206 | 207 | void 208 | ax25_flush_frame() 209 | { 210 | // Key the transmitter and send the frame 211 | modem_flush_frame(); 212 | } 213 | 214 | 215 | -------------------------------------------------------------------------------- /ax25.h: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #ifndef __AX25_H__ 19 | #define __AX25_H__ 20 | 21 | struct s_address { 22 | char callsign[7]; 23 | unsigned char ssid; 24 | }; 25 | 26 | //void ax25_send_header(struct s_address *addresses, int num_addresses); 27 | void ax25_send_header(s_address addresses[], int num_addresses); 28 | 29 | void ax25_send_byte(unsigned char byte); 30 | void ax25_send_string(const char *string); 31 | void ax25_send_footer(); 32 | void ax25_flush_frame(); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #ifndef __CONFIG_H__ 19 | #define __CONFIG_H__ 20 | 21 | 22 | // -------------------------------------------------------------------------- 23 | // THIS IS THE TRACKUINO FIRMWARE CONFIGURATION FILE. YOUR CALLSIGN AND 24 | // OTHER SETTINGS GO HERE. 25 | // 26 | // NOTE: all pins are Arduino based, not the Atmega chip. Mapping: 27 | // http://www.arduino.cc/en/Hacking/PinMapping 28 | // -------------------------------------------------------------------------- 29 | 30 | 31 | // -------------------------------------------------------------------------- 32 | // APRS config (aprs.c) 33 | // -------------------------------------------------------------------------- 34 | 35 | // Set your callsign and SSID here. Common values for the SSID are 36 | // (from http://zlhams.wikidot.com/aprs-ssidguide): 37 | // 38 | // - Balloons: 11 39 | // - Cars: 9 40 | // - Buoy: 8 41 | // - Home: 0 42 | // - IGate: 5 43 | // 44 | #define S_CALLSIGN "KT5TK" 45 | #define S_CALLSIGN_ID 2 46 | 47 | // Destination callsign: APRS (with SSID=0) is usually okay. 48 | //#define D_CALLSIGN "APZ" // APZ = Experimental 49 | #define D_CALLSIGN "APECAN" // APExxx = Telemetry devices (that's what Pecan actually is) 50 | 51 | #define D_CALLSIGN_ID 0 52 | 53 | 54 | // APRS Symbol 55 | // Consists of Symbol table (/ or \ or a dew overlay symbols) and the symbol ID 56 | #define APRS_SYMBOL_TABLE '/' // Default table 57 | //#define APRS_SYMBOL_TABLE '\\' // Secondary table (needs to be escaped => two \) 58 | 59 | //#define APRS_SYMBOL_ID '>' // /O = Car 60 | #define APRS_SYMBOL_ID 'O' // /O = Balloon 61 | //#define APRS_SYMBOL_ID 'N' // \N = Buoy 62 | 63 | // Digipeating paths: 64 | // (read more about digipeating paths here: http://wa8lmf.net/DigiPaths/ ) 65 | // The recommended digi path for a balloon is WIDE2-1 or pathless. The default 66 | // is to use WIDE2-1. Comment out the following two lines for pathless: 67 | #define DIGI_PATH1 "WIDE2" 68 | #define DIGI_PATH1_TTL 1 69 | //#define DIGI_PATH2 "WIDE2" 70 | //#define DIGI_PATH2_TTL 1 71 | 72 | // If we want to pass selected packets through the International Space Station 73 | #define DIGI_PATH1_SAT "ARISS" 74 | #define DIGI_PATH1_TTL_SAT 0 75 | //#define DIGI_PATH2_SAT "SGATE" 76 | //#define DIGI_PATH2_TTL_SAT 0 77 | 78 | 79 | // APRS comment: this goes in the comment portion of the APRS message. You 80 | // might want to keep this short. The longer the packet, the more vulnerable 81 | // it is to noise. 82 | //#define APRS_COMMENT "http://w5acm.net" 83 | #define APRS_COMMENT "http://tkrahn.net" 84 | //#define APRS_COMMENT "Testing Pecan" 85 | 86 | 87 | 88 | 89 | // Pressure in Pascal at sea level. 90 | // Can be adjusted at launch date to get exact altitude from BMP085 91 | #define P0 101325.0 92 | 93 | 94 | // -------------------------------------------------------------------------- 95 | // AX.25 config (ax25.cpp) 96 | // -------------------------------------------------------------------------- 97 | 98 | // TX delay in milliseconds 99 | #define TX_DELAY 300 // default was 300 100 | 101 | // -------------------------------------------------------------------------- 102 | // Tracker config (trackuino.cpp) 103 | // -------------------------------------------------------------------------- 104 | 105 | // APRS_PERIOD is the period between transmissions. Since we're not listening 106 | // before transmitting, it may be wise to choose a "random" value here JUST 107 | // in case someone else is transmitting at fixed intervals like us. 61000 ms 108 | // is the default (1 minute and 1 second). 109 | // 110 | // Low-power transmissions on occasional events (such as a balloon launch) 111 | // might be okay at lower-than-standard APRS periods (< 10m). Check with/ask 112 | // permision to local digipeaters beforehand. 113 | // #define APRS_PERIOD 25000UL 114 | 115 | // APRS_PERIOD is here replaced with APRS_PERIOD_SECONDS because we use the 116 | // watchdog timer to save more power. 117 | #define APRS_PERIOD_SECONDS 120 118 | 119 | // Set any value here (in ms) if you want to delay the first transmission 120 | // after resetting the device. 121 | #define APRS_DELAY 0UL 122 | 123 | // GPS baud rate (in bits per second) 124 | #define GPS_BAUDRATE 9600 //4800 for Argentdata High Altitude GPS, 9600 for Venus or uBlox MAX-6Q 125 | 126 | // GPS Power Pin 127 | // GPS receivers are real power hogs. To save power we might want to switch the 128 | // GPS module completely off when it's no longer needed (when we already have 129 | // a valid GPS position for this cycle). 130 | #define GPS_POWER_PIN 5 131 | 132 | // Note that the Venus board has a GPIO_24 jumper. By default it is set to GND 133 | // which means that the receiver is running full power in high resolution 134 | // mode. Unless you use the trackuino on a rocket, you should change this 135 | // jumper with a fine solder iron to the other side (+3.3 V). Then the Venus 136 | // will only draw enough power to supply valid NMEA data to the serial port. 137 | // This is about 70 mA before it gets a lease, but it comes quickly down to 138 | // ~ 40 mA. The board has also a BATT pin. If you permanently power this up 139 | // with 3.3V then the main power can be switched on and off from the Arduino 140 | // with a transistor through the GPS_POWER_PIN. The GPS fix then only takes a 141 | // second. 142 | 143 | #define GPS_RESET_PIN 9 144 | // The Venus auto reset usually doesn't work. We use this line to force a reset 145 | // pulse from the Arduino to the Venus module each time it should restart. 146 | 147 | // -------------------------------------------------------------------------- 148 | // Modem config (modem.cpp) 149 | // -------------------------------------------------------------------------- 150 | 151 | // AUDIO_PIN is the audio-out pin. The audio is generated by timer 2 using 152 | // PWM, so the only two options are pins 3 and 11. 153 | // Pin 11 doubles as MOSI, so I suggest using pin 3 for PWM and leave 11 free 154 | // in case you ever want to interface with an SPI device. 155 | #define AUDIO_PIN 3 156 | 157 | // Radio: I've tested trackuino with two different radios: 158 | // Radiometrix HX1 and SRB MX146. The interface with these devices 159 | // is implemented in their respective radio_*.cpp files, and here 160 | // you can choose which one will be hooked up to the tracker. 161 | // The tracker will behave differently depending on the radio used: 162 | // 163 | // RadioHx1 (Radiometrix HX1): 164 | // - Time from PTT-on to transmit: 5ms (per datasheet) 165 | // - PTT is TTL-level driven (on=high) and audio input is 5v pkpk 166 | // filtered and internally DC-coupled by the HX1, so both PTT 167 | // and audio can be wired directly. Very few external components 168 | // are needed for this radio, indeed. 169 | // 170 | // RadioMx146 (SRB MX146): 171 | // - Time from PTT-on to transmit: signaled by MX146 (pin RDY) 172 | // - Uses I2C to set freq (144.8 MHz) on start 173 | // - I2C requires wiring analog pins 4/5 (SDA/SCL) via two level 174 | // converters (one for each, SDA and SCL). DO NOT WIRE A 5V ARDUINO 175 | // DIRECTLY TO THE 3.3V MX146, YOU WILL DESTROY IT!!! 176 | // 177 | // I2C 5-3.3v LEVEL TRANSLATOR: 178 | // 179 | // +3.3v o--------+-----+ +---------o +5v 180 | // / | / 181 | // R \ | \ R 182 | // / G| / 183 | // 3K3 \ _ L _ \ 4K7 184 | // | T T T | 185 | // 3.3v device o---+--+|_| |+---+---o 5v device 186 | // S| |D 187 | // +-|>|-+ 188 | // N-channel MOSFET 189 | // (BS170, for instance) 190 | // 191 | // (Explanation of the lever translator: 192 | // http://www.neoteo.com/adaptador-de-niveles-para-bus-i2c-3-3v-5v.neo) 193 | // 194 | // - Audio needs a low-pass filter (R=8k2 C=0.1u) plus DC coupling 195 | // (Cc=10u). This also lowers audio to 500mV peak-peak required by 196 | // the MX146. 197 | // 198 | // 8k2 10uF 199 | // Arduino out o--/\/\/\---+---||---o 200 | // R | Cc 201 | // === 202 | // 0.1uF | C 203 | // v 204 | // 205 | // - PTT is pulled internally to 3.3v (off) or shorted (on). Use 206 | // an open-collector BJT to drive it: 207 | // 208 | // o MX146 PTT 209 | // | 210 | // 4k7 b |c 211 | // Arduino PTT o--/\/\/\----K (Any NPN will do) 212 | // R |e 213 | // | 214 | // v GND 215 | // 216 | // - Beware of keying the MX146 for too long, you will BURN it. 217 | // 218 | // So, summing up. Options are: 219 | // 220 | // - RadioMx146 221 | // - RadioHx1 222 | // - RadioAdf7012 223 | // - RadioSi446x 224 | #define RADIO_CLASS RadioSi446x 225 | 226 | // -------------------------------------------------------------------------- 227 | // Radio config (radio_*.cpp) 228 | // -------------------------------------------------------------------------- 229 | 230 | // This is the PTT pin to enable the transmitter and the VCXO; HIGH = on 231 | #define PTT_PIN 8 232 | 233 | // The ADL5531 PA is switched on separately with the TPS77650 Enable pin; LOW = on 234 | //#define TX_PA_PIN 6 // Not available at PecanPico 235 | 236 | // This is the pin used by the MX146 radio to signal full RF 237 | //#define MX146_READY_PIN 2 // Not available at PecanPico 238 | 239 | #define VCXO_FREQ 27000000L 240 | 241 | //#define ADF7012_CRYSTAL_FREQ 19440000L 242 | //#define ADF7012_CRYSTAL_FREQ 10000000L 243 | 244 | #define ADF7012_CRYSTAL_DIVIDER 15 // [1..15] 245 | 246 | #define ADF7012_TX_DATA_PIN 7 247 | 248 | // Some radios are frequency agile. Therefore we can set the (default) frequency here: 249 | #define RADIO_FREQUENCY 144390000UL 250 | //#define RADIO_FREQUENCY 432900000UL 251 | //#define RADIO_FREQUENCY 223390000UL 252 | 253 | // In other regions the APRS frequencies are different. Our balloon may travel 254 | // from one region to another, so we may QSY according to GPS defined geographical regions 255 | // Here we set some regional frequencies: 256 | 257 | #define RADIO_FREQUENCY_REGION1 144800000UL // Europe & Africa 258 | #define RADIO_FREQUENCY_REGION2 144390000UL // North and south America (Brazil is different) 259 | //#define RADIO_FREQUENCY_REGION2 432900000UL 260 | //#define RADIO_FREQUENCY_REGION2 223390000UL 261 | 262 | #define RADIO_FREQUENCY_JAPAN 144660000UL 263 | #define RADIO_FREQUENCY_CHINA 144640000UL 264 | #define RADIO_FREQUENCY_BRAZIL 145570000UL 265 | #define RADIO_FREQUENCY_AUSTRALIA 145175000UL 266 | #define RADIO_FREQUENCY_NEWZEALAND 144575000UL 267 | #define RADIO_FREQUENCY_THAILAND 145525000UL 268 | 269 | 270 | #define RADIO_FREQUENCY_ARISS 145825000UL // For remote locations we may get lucky with ISS flying by? 271 | //#define RADIO_FREQUENCY_CW 144050000UL 272 | //#define RADIO_FREQUENCY_TESTING 147435000UL // test area to see if it works 273 | 274 | 275 | /* 276 | 144.390 MHz - Chile, Indonesia, North America 277 | 144.575 MHz - New Zealand 278 | 144.660 MHz - Japan 279 | 144.640 MHz - China 280 | 144.800 MHz - South Africa, Europe, Russia 281 | 144.930 MHz - Argentina, Uruguay (no confirmation found. Hope this is not a typo?) 282 | 145.175 MHz - Australia 283 | 145.570 MHz - Brazil 284 | 145.525 MHz - Thailand 285 | 286 | */ 287 | 288 | // -------------------------------------------------------------------------- 289 | // Sensors config (sensors.cpp) 290 | // -------------------------------------------------------------------------- 291 | 292 | // Most of the sensors.cpp functions use internal reference voltages (either 293 | // AVCC or 1.1V). If you want to use an external reference, you should 294 | // uncomment the following line: 295 | // 296 | // #define USE_AREF 297 | // 298 | // BEWARE! If you hook up an external voltage to the AREF pin and 299 | // accidentally set the ADC to any of the internal references, YOU WILL 300 | // FRY YOUR AVR. 301 | // 302 | // It is always advised to connect the AREF pin through a pull-up resistor, 303 | // whose value is defined here in ohms (set to 0 if no pull-up): 304 | // 305 | #define AREF_PULLUP 4700 306 | // 307 | // Since there is already a 32K resistor at the ADC pin, the actual 308 | // voltage read will be VREF * 32 / (32 + AREF_PULLUP) 309 | // 310 | // Read more in the Arduino reference docs: 311 | // http://arduino.cc/en/Reference/AnalogReference?from=Reference.AREF 312 | 313 | // Pin mappings for the internal / external temperature sensors. VS refers 314 | // to (arduino) digital pins, whereas VOUT refers to (arduino) analog pins. 315 | 316 | //#define LM50_VS_PIN #N/A 317 | #define LM50_VOUT_PIN 2 318 | 319 | 320 | //#define INTERNAL_LM60_VS_PIN 6 321 | //#define INTERNAL_LM60_VOUT_PIN 0 322 | //#define EXTERNAL_LM60_VS_PIN 7 323 | //#define EXTERNAL_LM60_VOUT_PIN 1 324 | 325 | //#define LM335_VS_PIN 8 326 | //#define LM335_VOUT_PIN 2 // adc2 327 | 328 | 329 | 330 | // ADC inputs 331 | #define ADC0_PIN A0 // Battery 332 | #define ADC1_PIN A1 // Custom 333 | #define ADC2_PIN A2 // Custom 334 | #define ADC3_PIN A3 // Custom 335 | #define ADC6_PIN A6 // ADF7012 MUXOUT 336 | #define ADC7_PIN A7 // uBlox Voltage 337 | 338 | 339 | // Units for temperature sensors (Added by: Kyle Crockett) 340 | // 1 = Celsius, 2 = Kelvin, 3 = Fahrenheit 341 | #define TEMP_UNIT 1 342 | 343 | // Calibration value in the units selected. Use integer only. 344 | #define CALIBRATION_VAL 0 345 | 346 | // -------------------------------------------------------------------------- 347 | // Buzzer config (buzzer.cpp) 348 | // -------------------------------------------------------------------------- 349 | 350 | // This is the buzzer frequency. Choose one that maximizes the output volume 351 | // according to your buzzer's datasheet. It must be between L and 65535, 352 | // where L = F_CPU / 65535 and F_CPU is the clock frequency in hertzs. For 353 | // Arduinos (16 MHz), that gives a low limit of 245 Hz. 354 | //#define BUZZER_FREQ 895 // Hz 355 | 356 | // These are the number of seconds the buzzer will stay on/off alternately 357 | //#define BUZZER_ON_TIME 2 // secs 358 | //#define BUZZER_OFF_TIME 2 // secs 359 | 360 | // This option disables the buzzer above BUZZER_ALTITUDE meters. This is a 361 | // float value, so make it really high (eg. 1000000.0 = 1 million meters) 362 | // if you want it to never stop buzzing. 363 | //#define BUZZER_ALTITUDE 3000.0 // meters (divide by 0.3048 for ft.) 364 | 365 | // The buzzer is driven by timer 1, so the options here are pin 9 or 10 366 | //#define BUZZER_PIN 9 367 | 368 | // -------------------------------------------------------------------------- 369 | // Debug 370 | // -------------------------------------------------------------------------- 371 | 372 | // This is the LED pin (13 on Arduinos). The LED will be on while the AVR is 373 | // running and off while it's sleeping, so its brightness gives an indication 374 | // of the activity. 375 | #define LED_PIN 13 376 | 377 | 378 | 379 | // Debug info includes printouts from different modules to aid in testing and 380 | // debugging. 381 | // 382 | // 1. To properly receive debug information, only connect the Arduino RX pin 383 | // to the GPS TX pin, and leave the Arduino TX pin disconnected. 384 | // 385 | // 2. On the serial monitor, set the baudrate to GPS_BAUDRATE (above), 386 | // usually 9600. 387 | // 388 | // 3. When flashing the firmware, disconnect the GPS from the RX pin or you 389 | // will get errors. 390 | 391 | // #define DEBUG_GPS // GPS sentence dump and checksum validation 392 | // #define DEBUG_AX25 // AX.25 frame dump 393 | // #define DEBUG_MODEM // Modem ISR overrun and profiling 394 | // #define DEBUG_RESET // AVR reset 395 | 396 | 397 | #endif 398 | -------------------------------------------------------------------------------- /debug.cpp: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #include "debug.h" 19 | #include "config.h" 20 | 21 | #if 0 22 | /* Snippet to read from serial buffer. Since gps.encode() may take 23 | * a while to process, we buffer enough data ahead to avoid serial 24 | * data loss */ 25 | 26 | const int GPS_SW_RX = 7; // software-serial read from pin 7 27 | const int GPS_SW_TX = 8; // software-serial write to pin 8 28 | SoftwareSerial sw_serial(GPS_SW_RX, GPS_SW_TX); 29 | 30 | // define pin modes for tx, rx 31 | pinMode(GPS_SW_RX, INPUT); 32 | pinMode(GPS_SW_TX, OUTPUT); 33 | 34 | sw_serial.begin(GPS_BAUDRATE); 35 | 36 | int i; 37 | unsigned char buffer[256]; 38 | 39 | valid_gps_data = 0; 40 | while (! valid_gps_data) { 41 | for (i = 0; i < 256; i++) { 42 | buffer[i] = sw_serial.read(); 43 | } 44 | for (i = 0; i < 256; i++) { 45 | if (gps.encode(buffer[i])) { 46 | valid_gps_data = 1; 47 | break; 48 | } 49 | } 50 | } 51 | 52 | #endif 53 | 54 | 55 | #if 0 56 | /* Another snippet to output the aprs frame to the serial port */ 57 | Serial.write("/"); // Report w/ timestamp, no APRS messaging. $ = NMEA raw data 58 | Serial.write(gps.get_time()); // 170915h = 17h:09m:15s zulu (not allowed in Status Reports) 59 | Serial.write(gps.get_lat()); // Lat: 38deg and 22.20 min (.20 are NOT seconds, but 1/100th of minutes) 60 | Serial.write("/"); // Symbol table 61 | Serial.write(gps.get_lon()); // Lon: 000deg and 25.80 min 62 | Serial.write("O"); // Symbol: O=balloon, -=QTH 63 | Serial.write(gps.get_course()); 64 | Serial.write("/"); 65 | Serial.write(gps.get_speed()); // Course and speed (degrees/knots) 66 | Serial.write( // Comment 67 | "Prueba Trackuino 1200"); 68 | Serial.write("/A="); 69 | Serial.write(gps.get_altitude()); // Altitude (feet). Goes anywhere in the comment area 70 | Serial.println(""); 71 | #endif 72 | 73 | #if 0 74 | // Another snippet to test the modem 75 | modem_test(); 76 | while (modem_busy()) { } 77 | delay(200); 78 | #endif 79 | 80 | -------------------------------------------------------------------------------- /debug.h: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #ifndef __DEBUG_H__ 19 | #define __DEBUG_H__ 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /gps.cpp: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | 19 | #include "config.h" 20 | #include "gps.h" 21 | 22 | 23 | #if defined(ARDUINO) && ARDUINO >= 100 24 | #include 25 | #else 26 | #include 27 | #endif 28 | #include 29 | #include 30 | #include 31 | 32 | 33 | PROGMEM const prog_uchar ubx_setNav[] = { 34 | 0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 35 | 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 36 | 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 37 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 38 | 0x00, 0x00, 0x16, 0xDC 39 | }; 40 | 41 | // The ISS Kepplerian Elements calculated dataset (starting at Apr. 8th 23:00:00 UTC) 42 | PROGMEM const prog_uint16_t ISSData[] = { 43 | 26392, 24859, 23325, 21792, 20258, 18725, 17192, 16171, 44 | 14638, 13618, 12085, 11065, 10046, 9026, 8519, 8013, 45 | 7506, 7000, 6494, 6500, 6146, 6663, 7181, 7699, 46 | 8216, 9245, 10273, 11301, 12329, 13357, 14896, 16436, 47 | 17463, 19001, 20540, 22079, 23617, 25156, 26694, 28232, 48 | 29770, 31309, 32847, 34385, 35923, 37461, 38999, 40538, 49 | 42076, 43615, 45153, 46692, 47719, 49258, 50797, 51824, 50 | 53364, 54392, 55420, 56449, 56965, 57995, 58512, 59030, 51 | 59548, 59554, 59560, 59053, 59059, 58553, 57534, 57027, 52 | 56008, 54988, 53968, 52436, 51416, 49883, 48862, 47329, 53 | 45796, 44262, 42729, 41195, 39662, 38128, 36594, 35060, 54 | 33526, 31993, 30459, 28925, 27391, 25857, 24324, 22790, 55 | 21257, 19723, 18190, 17169, 15636, 14616, 13083, 12063, 56 | 11043, 10023, 9004, 8497, 7479, 6972, 6978, 6472, 57 | 6478, 6996, 7002, 7519, 8037, 8706, 9222, 10251, 58 | 11279, 12819, 13846, 15386, 16413, 17952, 19491, 21029, 59 | 22056, 23594, 25133, 26671, 28209, 29747, 31286, 33336, 60 | 34874, 36412, 37950, 39489, 41027, 42565, 44104, 45642, 61 | 46669, 48208, 49747, 50774, 52314, 53341, 54369, 55398, 62 | 56426, 57455, 57972, 58490, 59008, 59526, 59532, 59538, 63 | 59032, 58525, 58019, 57512, 56493, 55474, 54454, 53434, 64 | 52414, 50881, 49860, 48327, 46794, 45261, 43727, 42194, 65 | 40660, 39127, 37593, 36059, 34525, 32991, 31458, 29924, 66 | 28390, 26856, 25322, 23789, 22255, 20722, 19189, 18167, 67 | 16634, 15102, 14081, 12549, 11529, 10509, 9489, 8982, 68 | 7963, 7457, 6950, 6444, 6450, 6456, 6974, 6980, 69 | 7497, 8526, 9043, 10072, 11100, 12129, 13156, 14696, 70 | 15363, 16902, 18441, 19980, 21007, 22545, 24083, 25622, 71 | 27160, 28698, 30236, 31775, 33313, 34851, 36901, 38439, 72 | 39978, 41516, 43054, 44081, 45619, 47158, 48697, 50236, 73 | 51264, 52803, 53831, 54859, 55887, 56916, 57433, 58462, 74 | 58980, 58986, 59504, 59510, 59516, 59010, 58503, 57997, 75 | 56978, 56471, 55451, 54431, 52899, 51879, 50346, 49326, 76 | 47793, 46259, 44726, 43705, 42171, 40637, 39104, 37570, 77 | 35524, 33990, 32456, 30923, 29389, 27855, 26321, 24788, 78 | 23254, 21720, 20187, 19166, 17633, 16100, 15079, 13546, 79 | 12526, 11506, 10486, 9467, 8448, 7941, 7435, 6928, 80 | 6422, 6428, 6434, 6952, 7470, 7987, 8504, 9533, 81 | 10562, 11590, 12618, 13646, 15185, 16212, 17752, 19290, 82 | 20317, 21856, 23394, 24933, 26471, 27649, 29187, 30726, 83 | 32264, 33802, 35340, 36878, 38416, 39955, 41493, 43031, 84 | 44570, 46109, 47647, 49186, 50214, 51753, 52781, 53809, 85 | 55349, 56377, 56894, 57923, 58440, 58958, 58964, 59482, 86 | 59488, 58982, 58988, 58481, 57463, 56956, 55936, 54917, 87 | 53897, 52877, 51344, 50324, 48791, 47258, 45725, 44703, 88 | 43170, 41636, 40102, 38569, 37035, 35501, 33967, 31922, 89 | 30388, 28854, 27320, 25786, 24253, 22719, 21186, 20164, 90 | 18631, 17098, 15565, 14544, 13012, 11992, 10972, 9952, 91 | 8933, 8426, 7407, 6901, 6906, 6400, 6406, 6412, 92 | 6930, 7448, 7965, 8994, 9511, 10539, 11568, 12596, 93 | 14135, 15163, 16702, 18241, 19268, 20806, 22345, 23883, 94 | 25422, 26960, 28498, 30036, 31575, 33113, 34651, 36189, 95 | 37727, 39265, 40804, 42342, 43521, 45059, 46598, 48137, 96 | 49164, 50703, 51730, 53270, 54298, 55326, 56355, 57384, 97 | 57901, 58418, 58936, 59454, 59460, 59466, 58960, 58454, 98 | 57947, 57441, 56422, 55914, 54895, 53363, 52342, 51322, 99 | 49789, 48256, 47235, 45702, 44168, 42635, 41101, 39568, 100 | 38034, 36500, 34966, 33432, 31899, 30365, 28319, 26785, 101 | 25251, 23718, 22696, 21163, 19629, 18096, 16563, 15542, 102 | 14010, 12989, 11969, 10437, 9930, 8911, 7892, 7385, 103 | 6879, 6884, 6378, 6384, 6902, 6908, 7426, 7943, 104 | 8972, 10001, 11029, 12057, 13085, 14113, 15652, 17191, 105 | 18218, 19757, 21295, 22834, 24372, 25911, 27449, 28987, 106 | 30525, 32064, 33602, 35140, 36678, 38216, 39755, 41293, 107 | 42831, 44370, 45908, 47447, 48474, 50013, 51552, 52580, 108 | 54120, 54788, 55816, 56845, 57362, 57879, 58397, 58914, 109 | 59432, 59438, 59444, 58938, 58432, 57925, 57418, 56399, 110 | 55380, 54360, 53340, 51808, 50787, 49254, 47721, 46700, 111 | 45167, 43634, 42100, 40566, 39033, 37499, 35965, 34431, 112 | 32897, 31364, 29830, 28296, 26762, 25229, 23695, 22161, 113 | 20628, 19095, 17562, 16541, 15008, 13475, 12455, 11435, 114 | 10415, 9396, 8376, 7870, 7363, 6857, 6351, 6357, 115 | 6362, 6880, 7398, 7916, 8433, 8950, 9978, 11007, 116 | 12035, 13575, 14602, 16141, 17168, 18707, 20246, 21785, 117 | 23323, 24862, 26400, 27938, 29476, 31014, 32553, 34091, 118 | 35629, 37167, 38705, 40244, 41782, 43320, 44859, 46398, 119 | 47936, 48963, 50502, 51530, 53069, 54097, 55125, 56154, 120 | 57183, 57700, 58369, 58887, 58892, 59410, 59416, 58910, 121 | 58916, 58410, 57903, 56884, 55865, 54846, 53826, 52806, 122 | 51785, 50253, 48720, 47699, 46165, 44632, 43099, 41565, 123 | 40031, 38498, 36964, 35430, 33896, 32363, 30829, 29295, 124 | 27761, 26227, 24694, 23160, 21627, 20093, 18560, 17027, 125 | 16006, 14473, 13453, 11920, 10900, 9881, 9373, 8354, 126 | 7848, 7341, 6835, 6329, 6335, 6341, 6858, 7376, 127 | 7894, 8411, 9440, 10468, 11496, 12524, 13552, 15091, 128 | 16119, 17658, 19197, 20735, 22274, 23812, 24839, 26377, 129 | 28427, 29965, 31504, 33042, 34580, 36118, 37656, 39194, 130 | 40733, 42271, 43810, 45348, 46887, 47914, 49453, 50992, 131 | 52019, 53559, 54587, 55615, 56644, 57160, 58190, 58707, 132 | 59225, 59743, 59749, 59395, 58888, 58894, 58388, 57369, 133 | 56862, 55843, 54823, 53803, 52271, 51251, 49718, 48697, 134 | 47164, 45631, 44097, 42564, 41030, 39497, 37963, 36429, 135 | 34895, 33361, 31828, 30294, 28760, 27226, 25692, 24159, 136 | 22625, 21092, 19558, 18025, 17004, 15471, 14451, 12918, 137 | 11898, 10878, 9858, 8839, 8332, 7314, 6807, 6813, 138 | 6307, 6313, 6831, 6837, 7354, 7872, 8901, 9417, 139 | 10446, 11474, 13014, 14041, 15581, 16608, 18147, 19686, 140 | 21224, 22251, 23789, 25328, 26866, 28404, 29942, 31481, 141 | 33019, 35069, 36607, 38145, 39684, 41222, 42760, 44299, 142 | 45325, 46864, 48403, 49942, 50969, 52509, 53536, 54564, 143 | 55593, 56621, 57650, 58167, 58685, 59203, 59721, 59727, 144 | 59733, 59226, 58720, 58214, 57347, 56328, 55308, 54289, 145 | 53269, 52248, 50716, 49695, 48162, 46629, 45096, 43562, 146 | 42029, 41007, 39474, 37428, 35894, 34360, 32826, 31293, 147 | 29759, 28225, 26691, 25157, 23624, 22090, 20557, 19024, 148 | 18002, 16469, 14937, 13916, 12384, 11363, 10344, 9324, 149 | 8817, 7798, 7291, 6785, 6279, 6285, 6291, 6809, 150 | 6815, 7332, 8361, 8878, 9907, 10935, 11963, 12991, 151 | 14531, 15558, 17097, 18636, 19663, 21202, 22740, 24278, 152 | 25817, 27355, 28893, 30431, 31970, 33508, 35046, 36584, 153 | 38122, 40173, 41711, 42737, 44276, 45814, 47353, 48892, 154 | 49919, 51459, 52486, 54026, 55054, 56082, 57111, 57628, 155 | 58657, 59175, 59181, 59699, 59705, 59711, 59205, 58698, 156 | 58192, 57173, 56666, 55646, 54626, 53094, 51714, 50181, 157 | 49161, 47628, 46094, 45073, 43540, 42006, 40472, 38939, 158 | 37405, 35871, 33825, 32291, 30758, 29224, 27690, 26156, 159 | 24623, 23089, 21555, 20022, 19001, 17468, 15935, 14914, 160 | 13381, 12361, 11341, 10321, 9302, 8283, 7776, 7269, 161 | 6763, 6257, 6263, 6269, 6787, 7305, 7822, 8339, 162 | 9368, 10397, 11425, 12453, 13481, 14508, 16047, 17587, 163 | 18613, 20152, 21691, 23229, 24768, 26306, 27844, 29382, 164 | 30921, 32459, 33997, 35535, 37073, 38611, 40150, 41688, 165 | 43226, 44765, 46304, 47842, 49381, 50409, 51948, 52976, 166 | 54003, 55544, 56060, 57089, 58118, 58635, 59153, 59159, 167 | 59677, 59683, 59177, 59183, 58676, 57658, 57151, 56131, 168 | 55112, 54092, 53072, 51539, 50519, 48986, 47453, 46432, 169 | 44898, 43365, 41831, 39938, 38404, 36870, 35336, 33802, 170 | 32269, 30223, 28689, 27155, 25621, 24088, 22554, 21533, 171 | 19999, 18466, 16933, 15400, 14379, 12847, 11827, 10807, 172 | 9787, 8768, 8261, 7242, 6736, 6741, 6235, 6241, 173 | 6247, 6765, 7283, 7800, 8829, 9346, 10374, 11403, 174 | 12431, 13970, 14998, 16537, 17564, 19103, 20641, 22180, 175 | 23718, 25257, 26795, 28333, 29871, 31410, 32948, 34486, 176 | 36024, 37562, 39100, 40639, 42177, 43716, 45254, 46793, 177 | 48332, 49359, 50898, 51925, 53465, 54493, 55521, 56550, 178 | 57579, 58096, 58613, 59131, 59649, 59655, 59661, 59155, 179 | 58649, 58142, 57636, 56617, 56109, 55089, 53558, 52537, 180 | 51517, 49984, 48451, 47430, 45897, 44363, 42830, 41296, 181 | 39763, 38229, 36695, 35161, 33627, 32094, 30560, 29026, 182 | 27492, 25958, 24065, 22531, 20998, 19464, 17931, 16398, 183 | 15377, 13845, 12824, 11804, 10272, 9765, 8746, 7727, 184 | 7220, 6714, 6719, 6213, 6219, 6737, 6743, 7261, 185 | 7778, 8807, 9836, 10864, 11892, 12920, 13948, 15487, 186 | 17026, 18053, 19592, 21130, 22669, 24207, 25746, 27284, 187 | 28822, 30360, 31899, 33437, 34975, 36513, 38051, 39590, 188 | 41128, 42666, 44205, 45743, 47282, 48309, 49848, 51387, 189 | 52415, 53443, 54983, 56011, 56528, 57557, 58074, 58591, 190 | 59109, 59627, 59633, 59639, 59133, 58627, 58120, 57613, 191 | 56594, 55575, 54555, 53535, 52003, 50982, 49449, 48428, 192 | 46895, 45362, 43829, 42295, 40761, 39228, 37694, 36160, 193 | 34626, 33092, 31559, 30025, 28491, 26957, 25424, 23890, 194 | 22356, 20823, 19290, 17757, 16736, 15203, 14182, 12290, 195 | 11270, 10250, 9231, 8211, 7705, 7198, 6692, 6186, 196 | 6191, 6197, 6715, 6721, 7751, 8268, 8785, 9813, 197 | 10842, 11870, 13410, 14437, 15976, 17003, 18542, 20081, 198 | 21620, 23158, 24697, 26235, 27773, 29311, 30850, 32388, 199 | 33926, 35464, 37002, 38540, 40079, 41617, 43155, 44694, 200 | 46233, 47259, 48798, 50337, 51365, 52904, 53932, 54960, 201 | 55989, 57017, 57535, 58564, 59082, 59087, 59605, 59611, 202 | 59105, 59111, 58605, 58098, 57079, 56060, 55041, 54021, 203 | 53001, 51980, 50448, 48915, 47894, 46360, 44827, 43294, 204 | 41760, 40226, 38693, 37159, 35625, 34091, 32558, 31024, 205 | 29490, 27956, 26422, 24889, 23355, 21822, 20288, 18755, 206 | 17734, 16201, 14668, 13648, 12627, 11095, 10076, 9568, 207 | 8549, 7683, 7176, 6670, 6164, 6170, 6176, 6693, 208 | 7211, 7729, 8246, 9275, 10303, 11331, 12359, 13387, 209 | 14926, 15954, 17493, 19032, 20570, 22109, 23135, 24674, 210 | 26212, 27750, 29288, 31339, 32877, 34415, 35953, 37491, 211 | 39029, 40568, 42106, 43645, 45183, 46210, 47749, 49288, 212 | 50827, 51854, 52882, 54422, 55450, 56479, 56995, 58024, 213 | 58542, 59060, 59577, 59583, 59589, 59083, 59089, 58583, 214 | 57564, 57057, 56038, 55018, 53998, 52466, 51446, 49913, 215 | 48892, 47359, 45826, 44292, 42759, 41225, 39692, 38158, 216 | 36624, 35090, 33556, 32023, 30489, 28955, 27421, 25887, 217 | 24354, 22820, 21287, 19753, 18220, 17199, 15666, 14646, 218 | 13113, 12093, 11073, 10053, 9034, 8527, 7508, 7002, 219 | 7008, 6502, 6148, 6666, 6671, 7189, 7706, 8736, 220 | 9252, 10281, 11309, 12849, 13876, 15416, 16443, 17982, 221 | 19521, 20547, 22086, 23624, 25163, 26701, 28239, 29777, 222 | 31316, 32854, 34904, 36442, 37980, 39519, 41057, 42595, 223 | 44134, 45160, 46699, 48238, 49777, 50804, 52344, 53371, 224 | 54399, 55428, 56456, 57485, 58002, 58520, 59038, 59556, 225 | 59562, 59568, 59062, 58555, 58049, 57542, 56523, 55504, 226 | 54484, 53464, 52444, 50911, 49890, 48357, 46824, 45291, 227 | 43757, 42736, 41202, 39669, 37623, 36089, 34555, 33021, 228 | 31488, 29954, 28420, 26886, 25353, 23819, 22285, 20752, 229 | 19219, 18197, 16664, 15132, 14111, 12579, 11559, 10539, 230 | 9519, 9012, 7993, 7486, 6980, 6474, 6480, 6486, 231 | 7004, 7010, 7527, 8196, 8713, 9742, 10770, 11799, 232 | 12826, 14366, 15393, 16932, 18471, 19498, 21037, 22575, 233 | 24114, 25652, 27190, 28728, 30267, 31805, 33343, 34881, 234 | 36419, 37957, 40008, 41034, 42572, 44111, 45650, 47188, 235 | 48727, 49754, 51294, 52321, 53861, 54889, 55917, 56946, 236 | 57463, 58492, 59010, 59016, 59534, 59540, 59546, 59040, 237 | 58533, 58027, 57008, 56501, 55481, 54461, 52929, 51909, 238 | 50376, 49356, 47823, 46289, 45268, 43735, 42201, 40667, 239 | 39134, 37600, 36066, 34532, 32487, 30953, 29419, 27885, 240 | 26351, 24818, 23284, 21751, 20217, 19196, 17663, 16130, 241 | 15109, 13576, 12556, 11536, 10516, 9497, 8478, 7971, 242 | 7465, 6958, 6452, 6458, 6464, 6982, 7500, 8017, 243 | 8534, 9563, 10080, 11620, 12288, 13316, 14343, 15883, 244 | 17422, 18448, 19987, 21526, 23064, 24603, 26141, 27679, 245 | 29217, 30756, 32294, 33832, 35370, 36908, 38446, 39985, 246 | 41523, 43062, 44600, 46139, 47678, 49217, 50244, 51783, 247 | 52811, 53839, 55379, 55895, 56924, 57953, 58470, 58988, 248 | 58994, 59512, 59518, 59012, 59018, 58511, 57493, 56986, 249 | 55966, 54947, 53927, 52907, 51374, 50354, 48821, 47288, 250 | 46267, 44733, 43200, 41666, 40133, 38599, 37065, 35531, 251 | 33997, 32464, 30930, 28884, 27350, 25816, 24283, 22749, 252 | 21728, 20194, 18661, 17128, 15595, 14574, 13554, 12022, 253 | 11002, 9982, 8963, 8456, 7437, 6931, 6936, 6430, 254 | 6436, 6442, 6960, 7478, 7995, 9024, 9541, 10569, 255 | 11598, 12626, 14165, 15193, 16732, 17759, 19298, 20836, 256 | 22375, 23553, 25092, 26630, 28168, 29706, 31245, 32783, 257 | 34321, 35859, 37397, 38936, 40474, 42012, 43551, 45089, 258 | 46628, 48167, 49194, 50733, 51760, 53300, 54328, 55356, 259 | 56385, 57414, 57931, 58448, 58966, 59484, 59490, 59496, 260 | 58990, 58484, 57977, 57471, 56452, 55944, 54925, 53393, 261 | 52372, 51352, 49819, 48286, 47265, 45732, 44198, 42665, 262 | 41131, 39598, 38064, 36530, 34996, 33462, 31929, 30395, 263 | 28861, 27327, 25794, 24260, 22726, 21193, 19660, 18126, 264 | 16593, 15572, 14040, 13019, 11999, 10467, 9960, 8941, 265 | 7922, 7415, 6909, 6914, 6408, 6414, 6932, 6938, 266 | 7456, 7973, 9002, 10031, 11059, 12087, 13115, 14143, 267 | 15682, 17221, 18248, 19787, 21326, 22864, 24402, 25941, 268 | 27479, 29017, 30556, 32094, 33632, 35170, 36708, 38246, 269 | 39425, 40963, 42501, 44040, 45578, 47117, 48144, 49683, 270 | 51222, 52250, 53278, 54818, 55846, 56363, 57392, 57909, 271 | 58426, 58944, 59462, 59468, 59474, 58968, 58462, 57955, 272 | 57448, 56429, 55410, 54390, 53370, 51838, 50817, 49284, 273 | 48263, 46730, 45197, 43664, 42130, 40596, 39063, 37529, 274 | 35995, 34461, 32928, 31394, 29860, 28326, 26792, 25259, 275 | 23725, 22191, 20658, 19125, 17592, 16571, 15038, 14017, 276 | 12485, 11465, 10445, 9426, 8406, 7900, 7393, 6887, 277 | 6381, 6387, 6392, 6910, 6916, 7946, 8463, 8980, 278 | 10008, 11037, 12065, 13605, 14632, 16171, 17198, 18737, 279 | 20276, 21815, 23353, 24892, 26430, 27968, 29506, 31045, 280 | 32583, 34121, 35659, 37197, 38735, 40274, 41812, 43350, 281 | 44889, 46428, 47454, 48993, 50533, 51560, 52739, 53767, 282 | 54795, 55824, 56853, 57370, 58399, 58917, 58922, 59440, 283 | 59446, 58940, 58946, 58440, 57933, 56914, 55895, 54876, 284 | 53856, 52836, 51815, 50283, 48750, 47729, 46196, 44662, 285 | 43129, 41595, 40062, 38528, 36994, 35460, 33926, 32393, 286 | 30859, 29325, 27791, 26257, 24724, 23190, 21657, 20123, 287 | 18590, 17569, 16036, 14503, 13483, 12462, 10930, 9911, 288 | 9403, 8384, 7878, 7371, 6865, 6359, 6365, 6371, 289 | 6888, 7406, 7924, 8441, 9470, 10498, 11526, 12554, 290 | 13582, 15121, 16149, 17688, 19227, 20765, 22304, 23330, 291 | 24869, 26407, 27945, 29483, 31534, 33072, 34610, 36148, 292 | 37686, 39225, 40763, 42301, 43840, 45378, 46405, 47944, 293 | 49483, 51022, 52049, 53077, 54617, 55645, 56674, 57190, 294 | 57860, 58377, 58895, 59413, 59419, 59425, 58918, 58924, 295 | 58418, 57399, 56892, 55873, 54853, 53833, 52301, 51281, 296 | 49748, 48727, 47194, 45661, 44127, 42594, 41060, 39527, 297 | 37993, 36459, 34925, 33391, 31858, 30324, 28790, 27256, 298 | 25722, 24189, 22655, 21122, 19588, 18055, 17034, 15501, 299 | 14481, 12948, 11928, 10908, 9889, 8869, 8362, 7344, 300 | 6837, 6843, 6337, 6343, 6861, 6867, 7384, 7902, 301 | 8931, 9447, 10476, 11504, 13044, 14072, 15611, 16638, 302 | 18177, 19716, 20742, 22281, 23819, 25358, 26896, 28434, 303 | 29973, 31511, 33049, 35099, 36637, 38175, 39714, 41252, 304 | 42790, 44329, 45355, 46894, 48433, 49972, 50999, 52539, 305 | 53566, 54594, 55623, 56651, 57680, 58197, 58715, 59233, 306 | 59751, 59397, 59403, 58897, 58390, 57884, 57377, 56358, 307 | 55339, 54319, 53299, 52279, 50746, 49725, 48192, 46659, 308 | 45126, 43593, 42571, 41037, 39504, 37458, 35924, 34390, 309 | 32857, 31323, 29789, 28255, 26721, 25188, 23654, 22120, 310 | 20587, 19054, 18032, 16499, 14967, 13946, 12414, 11394, 311 | 10374, 9354, 8847, 7828, 7322, 6815, 6309, 6315, 312 | 6321, 6839, 6845, 7362, 8391, 8908, 9937, 10966, 313 | 11994, 13021, 14561, 15588, 17127, 18666, 19693, 21232, 314 | 22770, 24309, 25847, 27385, 28923, 30462, 32000, 33538, 315 | 35076, 36614, 38152, 39691, 41229, 42767, 44306, 45845, 316 | 47383, 48922, 49949, 51489, 52516, 54056, 55084, 56112, 317 | 57141, 57658, 58687, 59205, 59211, 59729, 59735, 59741, 318 | 59235, 58368, 57862, 56843, 56336, 55316, 54296, 52764, 319 | 51744, 50723, 49191, 47658, 46124, 45103, 43570, 42036, 320 | 40503, 38969, 37435, 35901, 34367, 32322, 30788, 29254, 321 | 27720, 26186, 24653, 23119, 21586, 20052, 19031, 17498, 322 | 15965, 14944, 13411, 12391, 11371, 10351, 9332, 8313, 323 | 7806, 7300, 6793, 6287, 6293, 6299, 6817, 7335, 324 | 7852, 8369, 9398, 9915, 11455, 12483, 13511, 14538, 325 | 16078, 17617, 18644, 20182, 21721, 23259, 24798, 26336, 326 | 27874, 29412, 30951, 32489, 34027, 35565, 37103, 38641, 327 | 40180, 41718, 43257, 44795, 46334, 47873, 49412, 50439, 328 | 51978, 53006, 54033, 55062, 56090, 57119, 58148, 58665, 329 | 59183, 59189, 59707, 59713, 59207, 59213, 58706, 57688, 330 | 57181, 56161, 55142, 53762, 52742, 51209, 50189, 48656, 331 | 47123, 46102, 44568, 43035, 41501, 39968, 38434, 36900, 332 | 35366, 33832, 32299, 30765, 29231, 27185, 25651, 24118, 333 | 23096, 21563, 20029, 18496, 16963, 15942, 14409, 13389, 334 | 11857, 10837, 9817, 8798, 8291, 7272, 6766, 6771, 335 | 6265, 6271, 6277, 6795, 7313, 7830, 8347, 9376, 336 | 10404, 11433, 12461, 14000, 15028, 16567, 17594, 19133, 337 | 20671, 22210, 23748, 25287, 26825, 28363, 29901, 31440, 338 | 32978, 34516, 36054, 37592, 39131, 40669, 42207, 43746, 339 | 45284, 46823, 48362, 49389, 50928, 51955, 53495, 54523, 340 | 55551, 56580, 57609, 58126, 58643, 59161, 59679, 59685, 341 | 59691, 59185, 58679, 58172, 57666, 56647, 56139, 55120, 342 | 53588, 52567, 51547, 50014, 48481, 47460, 45927, 44033, 343 | 42500, 40966, 39433, 37899, 36365, 34831, 33297, 31764, 344 | 30230, 28696, 27162, 25629, 24095, 22561, 21028, 19495, 345 | 17961, 16428, 15407, 13875, 12854, 11834, 10814, 9795, 346 | 8776, 7757, 7250, 6744, 6749, 6243, 6249, 6767, 347 | 6773, 7291, 7808, 8837, 9866, 10894, 11922, 12950, 348 | 13978, 15517, 17056, 18083, 19622, 21161, 22699, 24237, 349 | 25776, 27314, 28852, 30391, 31929, 33467, 35005, 36543, 350 | 38081, 39620, 41158, 42696, 44235, 45773, 47312, 48339, 351 | 49878, 51417, 52445, 53473, 55013, 56041, 56558, 57587, 352 | 58104, 58621, 59139, 59657, 59663, 59669, 59163, 58657, 353 | 58150, 57643, 56624, 55605, 54585, 53565, 52033, 51012, 354 | 49479, 48458, 46925, 45392, 43859, 42325, 40791, 39258, 355 | 37724, 36190, 34656, 33123, 31589, 30055, 28161, 26627, 356 | 25094, 23560, 22026, 20493, 18960, 17427, 16406, 14873, 357 | 13852, 12320, 11300, 10280, 9261, 8241, 7735, 7228, 358 | 6722, 6215, 6221, 6227, 6745, 6751, 7781, 8298, 359 | 8815, 9843, 10872, 11900, 13440, 14467, 16006, 17033, 360 | 18572, 20111, 21650, 23188, 24727, 26265, 27803, 29341, 361 | 30880, 32418, 33956, 35494, 37032, 38570, 40109, 41647, 362 | 43185, 44724, 46263, 47289, 48828, 50367, 51395, 52934, 363 | 53962, 54990, 56019, 57047, 57565, 58594, 59111, 59117, 364 | 59635, 59641, 59135, 59141, 58635, 58128, 57109, 56090, 365 | 55583, 54051, 53031, 52010, 50478, 49457, 47924, 46391, 366 | 44857, 43324, 41790, 40257, 38723, 37189, 35655, 34121, 367 | 32588, 31054, 29520, 27986, 26452, 24919, 23385, 21852, 368 | 20318, 18785, 17764, 16231, 14338, 13318, 12297, 10765, 369 | 9746, 9238, 8219, 7713, 7206, 6700, 6194, 6200, 370 | 6206, 6723, 7241, 7759, 8276, 9305, 10333, 11361, 371 | 12389, 13417, 14956, 15984, 17523, 19062, 20600, 21627, 372 | 23165, 24704, 26242, 27780, 29318, 30857, 32907, 34445, 373 | 35983, 37521, 39060, 40598, 42136, 43675, 45213, 46240, 374 | 47779, 49318, 50345, 51884, 52912, 54452, 55480, 56509, 375 | 57025, 58054, 58572, 59090, 59607, 59613, 59619, 59113, 376 | 59119, 58613, 57594, 57087, 56068, 55048, 54028, 52496, 377 | 51476, 49943, 48922, 47389, 45856, 44322, 42789, 41255, 378 | 39722, 38188, 36654, 35120, 33586, 32053, 30519, 28985, 379 | 27451, 25918, 24384, 22850, 21317, 19783, 18762, 17229, 380 | 15696, 14676, 13143, 12123, 11103, 10083, 8704, 8197, 381 | 7178, 6672, 6678, 6172, 6178, 6696, 6701, 7219, 382 | 7736, 8766, 9282, 10311, 11339, 12879, 13906, 15446, 383 | 16473, 18012, 19551, 20577, 22116, 23654, 25193, 26731, 384 | 28269, 29808, 31346, 32884, 34422, 36472, 38010, 39549, 385 | 41087, 42625, 43652, 45190, 46729, 48268, 49807, 50834, 386 | 52374, 53401, 54429, 55458, 56486, 57515, 58032, 58550, 387 | 59068, 59586, 59592, 59598, 59092, 58585, 58079, 57572, 388 | 56553, 55534, 54514, 53494, 52474, 50941, 49920, 48387, 389 | 46854, 45321, 44300, 42766, 41232, 39699, 38165, 36631, 390 | 34585, 33052, 31518, 29984, 28450, 26916, 25383, 23849, 391 | 22315, 20782, 19761, 18227, 16694, 15162, 14141, 13121, 392 | 11589, 10569, 9549, 9042, 8023, 7516, 7010, 6504, 393 | 6150, 6156, 6674, 6680, 7197, 8226, 8743, 9772, 394 | 10800, 11829, 12856, 14396, 15423, 16962, 18501, 19528, 395 | 21067, 22605, 24144, 25682, 27220, 28758, 30297, 31835, 396 | 33373, 34911, 36449, 37987, 39526, 41064, 42602, 44141, 397 | 45680, 47218, 48757, 49784, 51324, 52351, 53891, 54919, 398 | 55947, 56976, 57493, 58522, 59040, 59046, 59564, 59570, 399 | 59576, 59070, 58563, 58057, 57038, 56531, 55511, 54491, 400 | 52959, 51939, 50918, 49386, 47853, 46319, 45298, 43765, 401 | 42231, 40698, 39164, 37630, 36096, 34562, 33029, 30983, 402 | 29449, 27915, 26381, 24848, 23314, 21781, 20759, 19226 403 | 404 | }; 405 | 406 | #define ISSData_Length 2880 // 5 days will stay below 32 Mbyte 407 | // Remember to get ISS data: 408 | // Lookup the UNIX epoch time: http://www.epochconverter.com/ 409 | // cd /home/thomas/Documents/hamradio/balloon/ISS/ 410 | // perl iss.pl 1335009600 5 2012-04-21_1200UTC.dat > 2012-04-21_1200UTC.tsv 411 | 412 | 413 | 414 | 415 | #define ISS_CUT_OFF 30 // tollerance in deg (lat + lon) to consider ISS close enough to give it a try 416 | //#define getISSLatitude(x) ((ISSData[x] >> 9) - 64) 417 | //#define getISSLongitude(x) ((ISSData[x] & 511) - 180) 418 | 419 | /* 420 | // GPS PUBX request string 421 | #define PUBX "$PUBX,00*33" 422 | 423 | // NMEA commands 424 | #define GLL_OFF "$PUBX,40,GLL,0,0,0,0*5C" 425 | #define ZDA_OFF "$PUBX,40,ZDA,0,0,0,0*44" 426 | #define GSV_OFF "$PUBX,40,GSV,0,0,0,0*59" 427 | #define GSA_OFF "$PUBX,40,GSA,0,0,0,0*4E" 428 | #define RMC_OFF "$PUBX,40,RMC,0,0,0,0*47" 429 | #define GGA_OFF "$PUBX,40,GGA,0,0,0,0*5A" 430 | #define VTG_OFF "$PUBX,40,VTG,0,0,0,0*5E" 431 | 432 | // configures the GPS to only use the strings we want 433 | void configNMEA() { 434 | Serial.println(GLL_OFF); 435 | Serial.println(ZDA_OFF); 436 | Serial.println(GSV_OFF); 437 | Serial.println(GSA_OFF); 438 | Serial.println(RMC_OFF); 439 | Serial.println(GGA_OFF); 440 | Serial.println(VTG_OFF); 441 | 442 | // Set the navigation mode (Airborne, 1G) 443 | Serial.println("Trying to set the GPS to airborne mode:"); 444 | unsigned int setNav[] = { 445 | 0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC }; 446 | sendUBX(setNav, sizeof(setNav)/sizeof(unsigned int)); 447 | getUBX_ACK(setNav); 448 | } 449 | 450 | // Send a byte array of UBX protocol to the GPS 451 | void sendUBX(unsigned int *MSG, unsigned int len) { 452 | for(int i=0; i 9) { 486 | // All packets in order! 487 | return true; 488 | } 489 | 490 | // Timeout if no valid response in 3 seconds 491 | if (millis() - startTime > 3000) { 492 | return false; 493 | } 494 | 495 | // Make sure data is available to read 496 | if (Serial.available()) { 497 | b = Serial.read(); 498 | 499 | // Check that bytes arrive in sequence as per expected ACK packet 500 | if (b == ackPacket[ackByteID]) { 501 | ackByteID++; 502 | } 503 | else { 504 | ackByteID = 0; // Reset and look again, invalid order 505 | } 506 | 507 | } 508 | } 509 | } 510 | 511 | */ 512 | 513 | // Module declarations 514 | static void parse_sentence_type(const char * token); 515 | static void parse_time(const char *token); 516 | static void parse_date(const char *token); 517 | static void parse_status(const char *token); 518 | static void parse_lat(const char *token); 519 | static void parse_lat_hemi(const char *token); 520 | static void parse_lon(const char *token); 521 | static void parse_lon_hemi(const char *token); 522 | static void parse_speed(const char *token); 523 | static void parse_course(const char *token); 524 | static void parse_altitude(const char *token); 525 | 526 | 527 | 528 | // Module types 529 | typedef void (*t_nmea_parser)(const char *token); 530 | 531 | enum t_sentence_type { 532 | SENTENCE_UNK, 533 | SENTENCE_GGA, 534 | SENTENCE_RMC 535 | }; 536 | 537 | 538 | // Module constants 539 | static const t_nmea_parser unk_parsers[] = { 540 | parse_sentence_type, // $GPxxx 541 | }; 542 | 543 | static const t_nmea_parser gga_parsers[] = { 544 | NULL, // $GPGGA 545 | parse_time, // Time 546 | NULL, // Latitude 547 | NULL, // N/S 548 | NULL, // Longitude 549 | NULL, // E/W 550 | NULL, // Fix quality 551 | NULL, // Number of satellites 552 | NULL, // Horizontal dilution of position 553 | parse_altitude, // Altitude 554 | NULL, // "M" (mean sea level) 555 | NULL, // Height of GEOID (MSL) above WGS84 ellipsoid 556 | NULL, // "M" (mean sea level) 557 | NULL, // Time in seconds since the last DGPS update 558 | NULL // DGPS station ID number 559 | }; 560 | 561 | static const t_nmea_parser rmc_parsers[] = { 562 | NULL, // $GPRMC 563 | parse_time, // Time 564 | parse_status, // A=active, V=void 565 | parse_lat, // Latitude, 566 | parse_lat_hemi, // N/S 567 | parse_lon, // Longitude 568 | parse_lon_hemi, // E/W 569 | parse_speed, // Speed over ground in knots 570 | parse_course, // Track angle in degrees (true) 571 | parse_date, // Date (DDMMYY) 572 | NULL, // Magnetic variation 573 | NULL // E/W 574 | }; 575 | 576 | static const int NUM_OF_UNK_PARSERS = (sizeof(unk_parsers) / sizeof(t_nmea_parser)); 577 | static const int NUM_OF_GGA_PARSERS = (sizeof(gga_parsers) / sizeof(t_nmea_parser)); 578 | static const int NUM_OF_RMC_PARSERS = (sizeof(rmc_parsers) / sizeof(t_nmea_parser)); 579 | 580 | // Module variables 581 | static t_sentence_type sentence_type = SENTENCE_UNK; 582 | static bool at_checksum = false; 583 | static unsigned char our_checksum = '$'; 584 | static unsigned char their_checksum = 0; 585 | static char token[16]; 586 | static int num_tokens = 0; 587 | static unsigned int offset = 0; 588 | static bool active = false; 589 | static char gga_time[7], rmc_time[7], rmc_date[7]; 590 | static char new_time[7], new_date[7]; 591 | static float new_lat; 592 | static float new_lon; 593 | static char new_aprs_lat[9]; 594 | static char new_aprs_lon[10]; 595 | static float new_course; 596 | static float new_speed; 597 | static float new_altitude; 598 | 599 | // Public (extern) variables, readable from other modules 600 | char gps_time[7]; // HHMMSS 601 | char gps_date[7]; // DDMMYY 602 | float gps_lat = 0; 603 | float gps_lon = 0; 604 | char gps_aprs_lat[9]; 605 | char gps_aprs_lon[10]; 606 | float gps_course = 0; 607 | float gps_speed = 0; 608 | float gps_altitude = -1000.0; //The dead sea at -420 m is theoretically the deepest usable spot on earth where you could use a GPS 609 | //Here we define -1000 m as our invalid gps altitude 610 | unsigned long doppler_frequency = 145825000UL; 611 | bool satInView = false; 612 | int iss_lat; 613 | int iss_lon; 614 | unsigned int iss_datapoint; 615 | 616 | // Module functions 617 | unsigned char from_hex(char a) 618 | { 619 | if (a >= 'A' && a <= 'F') 620 | return a - 'A' + 10; 621 | else if (a >= 'a' && a <= 'f') 622 | return a - 'a' + 10; 623 | else if (a >= '0' && a <= '9') 624 | return a - '0'; 625 | else 626 | return 0; 627 | } 628 | 629 | void parse_sentence_type(const char *token) 630 | { 631 | if (strcmp(token, "$GPGGA") == 0) { 632 | sentence_type = SENTENCE_GGA; 633 | } else if (strcmp(token, "$GPRMC") == 0) { 634 | sentence_type = SENTENCE_RMC; 635 | } else { 636 | sentence_type = SENTENCE_UNK; 637 | } 638 | } 639 | 640 | void parse_time(const char *token) 641 | { 642 | // Time can have decimals (fractions of a second), but we only take HHMMSS 643 | strncpy(new_time, token, 6); 644 | } 645 | 646 | void parse_date(const char *token) 647 | { 648 | // Date in DDMMYY 649 | strncpy(new_date, token, 6); 650 | } 651 | 652 | 653 | void parse_status(const char *token) 654 | { 655 | // "A" = active, "V" = void. We shoud disregard void sentences 656 | if (strcmp(token, "A") == 0) 657 | active = true; 658 | else 659 | active = false; 660 | } 661 | 662 | void parse_lat(const char *token) 663 | { 664 | // Parses latitude in the format "DD" + "MM" (+ ".M{...}M") 665 | char degs[3]; 666 | if (strlen(token) >= 4) { 667 | degs[0] = token[0]; 668 | degs[1] = token[1]; 669 | degs[2] = '\0'; 670 | new_lat = atof(degs) + atof(token + 2) / 60; 671 | } 672 | // APRS-ready latitude 673 | strncpy(new_aprs_lat, token, 7); 674 | } 675 | 676 | void parse_lat_hemi(const char *token) 677 | { 678 | if (token[0] == 'S') 679 | new_lat = -new_lat; 680 | new_aprs_lat[7] = token[0]; 681 | new_aprs_lon[8] = '\0'; 682 | } 683 | 684 | void parse_lon(const char *token) 685 | { 686 | // Longitude is in the format "DDD" + "MM" (+ ".M{...}M") 687 | char degs[4]; 688 | if (strlen(token) >= 5) { 689 | degs[0] = token[0]; 690 | degs[1] = token[1]; 691 | degs[2] = token[2]; 692 | degs[3] = '\0'; 693 | new_lon = atof(degs) + atof(token + 3) / 60; 694 | } 695 | // APRS-ready longitude 696 | strncpy(new_aprs_lon, token, 8); 697 | } 698 | 699 | void parse_lon_hemi(const char *token) 700 | { 701 | if (token[0] == 'W') 702 | new_lon = -new_lon; 703 | new_aprs_lon[8] = token[0]; 704 | new_aprs_lon[9] = '\0'; 705 | } 706 | 707 | void parse_speed(const char *token) 708 | { 709 | new_speed = atof(token); 710 | } 711 | 712 | void parse_course(const char *token) 713 | { 714 | new_course = atof(token); 715 | } 716 | 717 | void parse_altitude(const char *token) 718 | { 719 | new_altitude = atof(token); 720 | } 721 | 722 | 723 | // 724 | // Exported functions 725 | // 726 | void gps_setup() { 727 | strcpy(gps_time, "000000"); 728 | strcpy(gps_date, "000000"); 729 | strcpy(gps_aprs_lat, "0000.00N"); 730 | strcpy(gps_aprs_lon, "00000.00E"); 731 | 732 | 733 | // Set uBlox navigation mode 734 | for(int i=0; i<44; i++) { 735 | Serial.write(pgm_read_byte_near(ubx_setNav + i )); 736 | 737 | } 738 | 739 | 740 | 741 | } 742 | 743 | bool gps_decode(char c) 744 | { 745 | int ret = false; 746 | 747 | switch(c) { 748 | case '\r': 749 | case '\n': 750 | // End of sentence 751 | 752 | if (num_tokens && our_checksum == their_checksum) { 753 | #ifdef DEBUG_GPS 754 | Serial.print(" (OK!)"); 755 | #endif 756 | // Return a valid position only when we've got two rmc and gga 757 | // messages with the same timestamp. 758 | switch (sentence_type) { 759 | case SENTENCE_UNK: 760 | break; // Keeps gcc happy 761 | case SENTENCE_GGA: 762 | strcpy(gga_time, new_time); 763 | break; 764 | case SENTENCE_RMC: 765 | strcpy(rmc_time, new_time); 766 | strcpy(rmc_date, new_date); 767 | break; 768 | } 769 | 770 | // Valid position scenario: 771 | // 772 | // 1. The timestamps of the two previous GGA/RMC sentences must match. 773 | // 774 | // 2. We just processed a known (GGA/RMC) sentence. Suppose the 775 | // contrary: after starting up this module, gga_time and rmc_time 776 | // are both equal (they're both initialized to ""), so (1) holds 777 | // and we wrongly report a valid position. 778 | // 779 | // 3. The GPS has a valid fix. For some reason, the Venus 634FLPX 780 | // reports 24 deg N, 121 deg E (the middle of Taiwan) until a valid 781 | // fix is acquired: 782 | // 783 | // $GPGGA,120003.000,2400.0000,N,12100.0000,E,0,00,0.0,0.0,M,0.0,M,,0000**69 (OK!) 784 | // $GPGSA,A,1,,,,,,,,,,,,,0.0,0.0,0.0**30 (OK!) 785 | // $GPRMC,120003.000,V,2400.0000,N,12100.0000,E,000.0,000.0,280606,,,N**78 (OK!) 786 | // $GPVTG,000.0,T,,M,000.0,N,000.0,K,N**02 (OK!) 787 | // 788 | // 4.) We also want a clean altitude being reported. 789 | // When is an altitude clean? Theoretically we could launch a balloon from the dead sea 790 | // 420 m below sea level. Therefore we define -1000 m as our invalid altitude and 791 | // expect to find an altitude above -420 m when we've decoded a valid GGA statement. 792 | 793 | if (sentence_type != SENTENCE_UNK && // Known sentence? 794 | strcmp(gga_time, rmc_time) == 0 && // RMC/GGA times match? 795 | active && // Valid fix? 796 | new_altitude > -1000.0) { // Valid new altitude? 797 | // Atomically merge data from the two sentences 798 | strcpy(gps_time, new_time); 799 | strcpy(gps_date, new_date); 800 | gps_lat = new_lat; 801 | gps_lon = new_lon; 802 | strcpy(gps_aprs_lat, new_aprs_lat); 803 | strcpy(gps_aprs_lon, new_aprs_lon); 804 | gps_course = new_course; 805 | gps_speed = new_speed; 806 | gps_altitude = new_altitude; 807 | new_altitude = -1000.0; // Invalidate new_altitude so that we are sure to get a valid one next time 808 | ret = true; 809 | } 810 | } 811 | #ifdef DEBUG_GPS 812 | if (num_tokens) 813 | Serial.println(); 814 | #endif 815 | at_checksum = false; // CR/LF signals the end of the checksum 816 | our_checksum = '$'; // Reset checksums 817 | their_checksum = 0; 818 | offset = 0; // Prepare for the next incoming sentence 819 | num_tokens = 0; 820 | sentence_type = SENTENCE_UNK; 821 | break; 822 | 823 | case '*': 824 | // Begin of checksum and process token (ie. do not break) 825 | at_checksum = true; 826 | our_checksum ^= c; 827 | #ifdef DEBUG_GPS 828 | Serial.print(c); 829 | #endif 830 | 831 | case ',': 832 | // Process token 833 | token[offset] = '\0'; 834 | our_checksum ^= c; // Checksum the ',', undo the '*' 835 | 836 | // Parse token 837 | switch (sentence_type) { 838 | case SENTENCE_UNK: 839 | if (num_tokens < NUM_OF_UNK_PARSERS && unk_parsers[num_tokens]) 840 | unk_parsers[num_tokens](token); 841 | break; 842 | case SENTENCE_GGA: 843 | if (num_tokens < NUM_OF_GGA_PARSERS && gga_parsers[num_tokens]) 844 | gga_parsers[num_tokens](token); 845 | break; 846 | case SENTENCE_RMC: 847 | if (num_tokens < NUM_OF_RMC_PARSERS && rmc_parsers[num_tokens]) 848 | rmc_parsers[num_tokens](token); 849 | break; 850 | } 851 | 852 | // Prepare for next token 853 | num_tokens++; 854 | offset = 0; 855 | #ifdef DEBUG_GPS 856 | Serial.print(c); 857 | #endif 858 | break; 859 | 860 | default: 861 | // Any other character 862 | if (at_checksum) { 863 | // Checksum value 864 | their_checksum = their_checksum * 16 + from_hex(c); 865 | } else { 866 | // Regular NMEA data 867 | if (offset < 15) { // Avoid buffer overrun (tokens can't be > 15 chars) 868 | token[offset] = c; 869 | offset++; 870 | our_checksum ^= c; 871 | } 872 | } 873 | #ifdef DEBUG_GPS 874 | Serial.print(c); 875 | #endif 876 | } 877 | return ret; 878 | } 879 | 880 | 881 | unsigned long gps_get_region_frequency() 882 | { 883 | unsigned long frequency = RADIO_FREQUENCY; 884 | if(-168 < gps_lon && gps_lon < -25) frequency = RADIO_FREQUENCY_REGION2; 885 | if(-25 < gps_lon && gps_lon < 71) frequency = RADIO_FREQUENCY_REGION1; 886 | if(-34.95 < gps_lat && gps_lat < 7.18 && -73.13 < gps_lon && gps_lon < -26.46) frequency = RADIO_FREQUENCY_BRAZIL; // Brazil 887 | if( 29.38 < gps_lat && gps_lat < 47.10 && 127.16 < gps_lon && gps_lon < 153.61) frequency = RADIO_FREQUENCY_JAPAN; // Japan 888 | if( 19.06 < gps_lat && gps_lat < 53.74 && 72.05 < gps_lon && gps_lon < 127.16) frequency = RADIO_FREQUENCY_CHINA; // China 889 | if( -0.30 < gps_lat && gps_lat < 20.42 && 93.06 < gps_lon && gps_lon < 105.15) frequency = RADIO_FREQUENCY_THAILAND; // Thailand 890 | if(-54.54 < gps_lat && gps_lat <-32.43 && 161.62 < gps_lon && gps_lon < 179.99) frequency = RADIO_FREQUENCY_NEWZEALAND; // New Zealand 891 | if(-50.17 < gps_lat && gps_lat < -8.66 && 105.80 < gps_lon && gps_lon < 161.62) frequency = RADIO_FREQUENCY_AUSTRALIA; // Australia 892 | 893 | // Note: These regions are a super simplified approach to define rectangles on the world map, representing regions where we may consider at least some 894 | // chance that an APRS Digipeter or an Igate is listening. They have absolutely NO political relevance. I was just trying to identify 895 | // regions with APRS activity on APRS.fi and look up the used frequencies on the web. Any corrections or additions are welcome. Please email 896 | // your suggestions to thomas@tkrahn.com 897 | 898 | // use your own coordinates for testing; Comment out when testing is finished! 899 | // if(29.7252 < gps_lat && gps_lat < 29.7261 && -95.5082 < gps_lon && gps_lon < -95.5074) frequency = MX146_FREQUENCY_TESTING; // KT5TK home 900 | //if(29.7353 < gps_lat && gps_lat < 29.7359 && -95.5397 < gps_lon && gps_lon < -95.5392) frequency = MX146_FREQUENCY_TESTING; // Gessner BBQ 901 | 902 | // Note: Never define a region that spans the date line! Use two regions instead. 903 | if(gps_lat == 0 && gps_lon == 0) frequency = RADIO_FREQUENCY; // switch to default when we don't have a GPS lease 904 | 905 | return frequency; 906 | } 907 | 908 | bool gps_check_satellite() 909 | { 910 | int latdiff; 911 | int londiff; 912 | 913 | long igpsdate = atol(gps_date); 914 | long igpstime = atol(gps_time); 915 | 916 | int gpstime_day = igpsdate / 10000; 917 | int gpstime_month = (igpsdate % 10000) / 100; 918 | int gpstime_year = (igpsdate % 10000) % 100 + 2000; 919 | int gpstime_hour = igpstime / 10000; 920 | int gpstime_minute = (igpstime % 10000) / 100; 921 | int gpstime_second = (igpstime % 10000) % 100; 922 | 923 | 924 | // int gpstime_year = 2012; 925 | // int gpstime_month = 2; 926 | // int gpstime_day = 11; 927 | // int gpstime_hour = 21; 928 | // int gpstime_minute = 0; 929 | // int gpstime_second = 0; 930 | 931 | 932 | // The start time must match with the dataset above! 933 | long gpslaunchminutes = (gpstime_day - 8 ) * 1440 + (gpstime_hour - 23) * 60 + gpstime_minute - 0; 934 | 935 | 936 | 937 | // look 2 minutes into the future so that you see the constellation for the next TX cycle 938 | gpslaunchminutes += 2; 939 | 940 | // make sure we're in the bounds of the array. 941 | if ((gpslaunchminutes < 0) || (gpslaunchminutes > ISSData_Length)) // make sure we're in the bounds of the array. 942 | { 943 | gpslaunchminutes = 0; 944 | } 945 | 946 | iss_datapoint = pgm_read_word_near(ISSData + gpslaunchminutes); 947 | 948 | // unmerge the datapoint into its components 949 | 950 | iss_lat = ((iss_datapoint >> 9) - 64); 951 | iss_lon = ((iss_datapoint & 511) - 180); 952 | 953 | latdiff = abs(iss_lat - (int)gps_lat); 954 | londiff = abs(iss_lon - (int)gps_lon); 955 | 956 | // ISS nearby? This doesn't really represent a circle (rather a rhombus or something, but who cares? 957 | if ((latdiff + londiff) < ISS_CUT_OFF) 958 | { 959 | satInView = true; 960 | 961 | } 962 | else 963 | { 964 | satInView = false; 965 | 966 | } 967 | return satInView; 968 | } 969 | 970 | 971 | 972 | 973 | 974 | float gps_get_lat() 975 | { 976 | return gps_lat; 977 | } 978 | 979 | 980 | float gps_get_lon() 981 | { 982 | return gps_lon; 983 | } 984 | 985 | float gps_get_altitude() 986 | { 987 | return gps_altitude; 988 | } 989 | 990 | 991 | long gps_get_time() 992 | { 993 | return atol(gps_time); 994 | } 995 | 996 | long gps_get_date() 997 | { 998 | return atol(gps_date); 999 | } 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | -------------------------------------------------------------------------------- /gps.h: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #ifndef __GPS_H__ 19 | #define __GPS_H__ 20 | 21 | extern char gps_time[7]; // HHMMSS 22 | extern char gps_date[7]; // DDMMYY 23 | extern float gps_lat; 24 | extern float gps_lon; 25 | extern char gps_aprs_lat[9]; 26 | extern char gps_aprs_lon[10]; 27 | extern unsigned long gps_region_frequency; 28 | extern unsigned long doppler_frequency; 29 | extern bool satInView; 30 | extern float gps_course; 31 | extern float gps_speed; 32 | extern float gps_altitude; 33 | extern float gps_get_lat(); 34 | extern float gps_get_lon(); 35 | extern float gps_get_altitude(); 36 | extern long gps_get_time(); 37 | extern long gps_get_date(); 38 | extern int iss_lat; 39 | extern int iss_lon; 40 | extern unsigned int iss_datapoint; 41 | 42 | void gps_setup(); 43 | 44 | /* 45 | void configNMEA(); 46 | void sendUBX(unsigned int *MSG, unsigned int len); 47 | bool getUBX_ACK(unsigned int *MSG); 48 | */ 49 | 50 | bool gps_decode(char c); 51 | unsigned long gps_get_region_frequency(); 52 | bool gps_check_satellite(); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /modem.cpp: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | /* Credit to: 19 | * 20 | * Michael Smith for his Example of Audio generation with two timers and PWM: 21 | * http://www.arduino.cc/playground/Code/PCMAudio 22 | * 23 | * Ken Shirriff for his Great article on PWM: 24 | * http://arcfn.com/2009/07/secrets-of-arduino-pwm.html 25 | * 26 | * The large group of people who created the free AVR tools. 27 | * Documentation on interrupts: 28 | * http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html 29 | */ 30 | 31 | #include "config.h" 32 | #include "modem.h" 33 | 34 | // only include the selected radio 35 | #if RADIO_CLASS == RadioHx1 36 | #include "radio_hx1.h" 37 | #endif 38 | #if RADIO_CLASS == RadioMx146 39 | #include "radio_mx146.h" 40 | #endif 41 | #if RADIO_CLASS == RadioAdf7012 42 | #include "radio_adf7012.h" 43 | #endif 44 | #if RADIO_CLASS == RadioSi446x 45 | #include "radio_si446x.h" 46 | #endif 47 | 48 | #if defined(ARDUINO) && ARDUINO >= 100 49 | #include 50 | #else 51 | #include 52 | #endif 53 | 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #if AUDIO_PIN == 3 60 | # define OCR2 OCR2B 61 | #endif 62 | #if AUDIO_PIN == 11 63 | # define OCR2 OCR2A 64 | #endif 65 | 66 | // Module Constants 67 | 68 | // This procudes a "warning: only initialized variables can be placed into 69 | // program memory area", which can be safely ignored: 70 | // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734 71 | 72 | /* 73 | PROGMEM const prog_uchar sine_table[512] = { 74 | 127, 129, 130, 132, 133, 135, 136, 138, 139, 141, 143, 144, 146, 147, 149, 150, 152, 153, 155, 156, 158, 75 | 159, 161, 163, 164, 166, 167, 168, 170, 171, 173, 174, 176, 177, 179, 180, 182, 183, 184, 186, 187, 188, 76 | 190, 191, 193, 194, 195, 197, 198, 199, 200, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 77 | 216, 217, 218, 219, 220, 221, 223, 224, 225, 226, 227, 228, 228, 229, 230, 231, 232, 233, 234, 235, 236, 78 | 236, 237, 238, 239, 239, 240, 241, 242, 242, 243, 244, 244, 245, 245, 246, 247, 247, 248, 248, 249, 249, 79 | 249, 250, 250, 251, 251, 251, 252, 252, 252, 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 80 | 254, 254, 255, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 253, 253, 253, 253, 252, 252, 252, 251, 81 | 251, 251, 250, 250, 249, 249, 249, 248, 248, 247, 247, 246, 245, 245, 244, 244, 243, 242, 242, 241, 240, 82 | 239, 239, 238, 237, 236, 236, 235, 234, 233, 232, 231, 230, 229, 228, 228, 227, 226, 225, 224, 223, 221, 83 | 220, 219, 218, 217, 216, 215, 214, 213, 211, 210, 209, 208, 207, 205, 204, 203, 202, 200, 199, 198, 197, 84 | 195, 194, 193, 191, 190, 188, 187, 186, 184, 183, 182, 180, 179, 177, 176, 174, 173, 171, 170, 168, 167, 85 | 166, 164, 163, 161, 159, 158, 156, 155, 153, 152, 150, 149, 147, 146, 144, 143, 141, 139, 138, 136, 135, 86 | 133, 132, 130, 129, 127, 125, 124, 122, 121, 119, 118, 116, 115, 113, 111, 110, 108, 107, 105, 104, 102, 87 | 101, 99, 98, 96, 95, 93, 91, 90, 88, 87, 86, 84, 83, 81, 80, 78, 77, 75, 74, 72, 71, 88 | 70, 68, 67, 66, 64, 63, 61, 60, 59, 57, 56, 55, 54, 52, 51, 50, 49, 47, 46, 45, 44, 89 | 43, 41, 40, 39, 38, 37, 36, 35, 34, 33, 31, 30, 29, 28, 27, 26, 26, 25, 24, 23, 22, 90 | 21, 20, 19, 18, 18, 17, 16, 15, 15, 14, 13, 12, 12, 11, 10, 10, 9, 9, 8, 7, 7, 91 | 6, 6, 5, 5, 5, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 92 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 93 | 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, 11, 94 | 12, 12, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 95 | 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 44, 45, 46, 47, 49, 50, 51, 52, 96 | 54, 55, 56, 57, 59, 60, 61, 63, 64, 66, 67, 68, 70, 71, 72, 74, 75, 77, 78, 80, 81, 97 | 83, 84, 86, 87, 88, 90, 91, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 108, 110, 111, 113, 98 | 115, 116, 118, 119, 121, 122, 124, 125 99 | }; 100 | */ 101 | 102 | // This is a 15% scaled down version of the above 103 | // in order to operate the PWM not too close to its extreme limits. 104 | // Trying to prevent distortion this way. 105 | 106 | PROGMEM const prog_uchar sine_table[512] = { 107 | 128,128,128,128,129,129,129,130,130,130,131,131,131,132,132,132,132,133,133,133,134, 108 | 134,134,135,135,135,136,136,136,136,137,137,137,138,138,138,138,139,139,139,140,140, 109 | 140,140,141,141,141,141,142,142,142,142,143,143,143,143,144,144,144,144,145,145,145, 110 | 145,146,146,146,146,146,147,147,147,147,147,148,148,148,148,148,149,149,149,149,149, 111 | 149,150,150,150,150,150,150,151,151,151,151,151,151,151,151,151,152,152,152,152,152, 112 | 152,152,152,152,152,152,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153, 113 | 153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,152, 114 | 152,152,152,152,152,152,152,152,152,152,151,151,151,151,151,151,151,151,151,150,150, 115 | 150,150,150,150,149,149,149,149,149,149,148,148,148,148,148,147,147,147,147,147,146, 116 | 146,146,146,146,145,145,145,145,144,144,144,144,143,143,143,143,142,142,142,142,141, 117 | 141,141,141,140,140,140,140,139,139,139,138,138,138,138,137,137,137,136,136,136,136, 118 | 135,135,135,134,134,134,133,133,133,132,132,132,132,131,131,131,130,130,130,129,129, 119 | 129,128,128,128,128,127,127,127,126,126,126,125,125,125,124,124,124,123,123,123,123, 120 | 122,122,122,121,121,121,120,120,120,119,119,119,119,118,118,118,117,117,117,117,116, 121 | 116,116,115,115,115,115,114,114,114,114,113,113,113,113,112,112,112,112,111,111,111, 122 | 111,110,110,110,110,109,109,109,109,109,108,108,108,108,108,107,107,107,107,107,106, 123 | 106,106,106,106,106,105,105,105,105,105,105,104,104,104,104,104,104,104,104,104,103, 124 | 103,103,103,103,103,103,103,103,103,103,102,102,102,102,102,102,102,102,102,102,102, 125 | 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102, 126 | 102,102,102,103,103,103,103,103,103,103,103,103,103,103,104,104,104,104,104,104,104, 127 | 104,104,105,105,105,105,105,105,106,106,106,106,106,106,107,107,107,107,107,108,108, 128 | 108,108,108,109,109,109,109,109,110,110,110,110,111,111,111,111,112,112,112,112,113, 129 | 113,113,113,114,114,114,114,115,115,115,115,116,116,116,117,117,117,117,118,118,118, 130 | 119,119,119,119,120,120,120,121,121,121,122,122,122,123,123,123,123,124,124,124,125, 131 | 125,125,126,126,126,127,127,127 132 | 133 | 134 | }; 135 | 136 | /* The sine_table is the carrier signal. To achieve phase continuity, each tone 137 | * starts at the index where the previous one left off. By changing the stride of 138 | * the index (phase_delta) we get 1200 or 2200 Hz. The PHASE_DELTA_XXXX values 139 | * can be calculated as: 140 | * 141 | * Fg = frequency of the output tone (1200 or 2200) 142 | * Fm = sampling rate (PLAYBACK_RATE_HZ) 143 | * Tt = sine table size (TABLE_SIZE) 144 | * 145 | * PHASE_DELTA_Fg = Tt*(Fg/Fm) 146 | */ 147 | 148 | static const unsigned char REST_DUTY = 127; 149 | static const int TABLE_SIZE = sizeof(sine_table); 150 | static const unsigned long PLAYBACK_RATE = F_CPU / 256; // 62.5KHz @ F_CPU=16MHz; 31.25kHz @ 8MHz 151 | static const int BAUD_RATE = 1200; 152 | static const unsigned char SAMPLES_PER_BAUD= (PLAYBACK_RATE / BAUD_RATE); // 52.083333333 / 26.041666667 153 | static const unsigned int PHASE_DELTA_1200 = (((TABLE_SIZE * 1200L) << 7) / PLAYBACK_RATE); // Fixed point 9.7 // 1258 / 2516 154 | static const unsigned int PHASE_DELTA_2200 = (((TABLE_SIZE * 2200L) << 7) / PLAYBACK_RATE); // 2306 / 4613 155 | 156 | 157 | // Module globals 158 | static unsigned char current_byte; 159 | static unsigned char current_sample_in_baud; // 1 bit = SAMPLES_PER_BAUD samples 160 | static bool go = false; // Modem is on 161 | static unsigned int phase_delta; // 1200/2200 for standard AX.25 162 | static unsigned int phase; // Fixed point 9.7 (2PI = TABLE_SIZE) 163 | static unsigned int packet_pos; // Next bit to be sent out 164 | #ifdef DEBUG_MODEM 165 | static int overruns = 0; 166 | static unsigned int slow_isr_time; 167 | static unsigned int slow_packet_pos; 168 | static unsigned char slow_sample_in_baud; 169 | #endif 170 | 171 | // The radio (class defined in config.h) 172 | static RADIO_CLASS radio; 173 | 174 | // Exported globals 175 | unsigned int modem_packet_size = 0; 176 | unsigned char modem_packet[MODEM_MAX_PACKET]; 177 | 178 | void modem_setup() 179 | { 180 | // Configure pins 181 | pinMode(PTT_PIN, OUTPUT); 182 | digitalWrite(PTT_PIN, LOW); 183 | pinMode(AUDIO_PIN, OUTPUT); 184 | 185 | // Start radio 186 | radio.setup(); 187 | 188 | // Set up Timer 2 to do pulse width modulation on the speaker 189 | // pin. 190 | 191 | // Source timer2 from clkIO (datasheet p.164) 192 | ASSR &= ~(_BV(EXCLK) | _BV(AS2)); 193 | 194 | // Set fast PWM mode with TOP = 0xff: WGM22:0 = 3 (p.150) 195 | TCCR2A |= _BV(WGM21) | _BV(WGM20); 196 | TCCR2B &= ~_BV(WGM22); 197 | 198 | #if AUDIO_PIN == 11 199 | // Do non-inverting PWM on pin OC2A (arduino pin 11) (p.159) 200 | // OC2B (arduino pin 3) stays in normal port operation: 201 | // COM2A1=1, COM2A0=0, COM2B1=0, COM2B0=0 202 | TCCR2A = (TCCR2A | _BV(COM2A1)) & ~(_BV(COM2A0) | _BV(COM2B1) | _BV(COM2B0)); 203 | #endif 204 | 205 | #if AUDIO_PIN == 3 206 | // Do non-inverting PWM on pin OC2B (arduino pin 3) (p.159). 207 | // OC2A (arduino pin 11) stays in normal port operation: 208 | // COM2B1=1, COM2B0=0, COM2A1=0, COM2A0=0 209 | TCCR2A = (TCCR2A | _BV(COM2B1)) & ~(_BV(COM2B0) | _BV(COM2A1) | _BV(COM2A0)); 210 | #endif 211 | 212 | // No prescaler (p.162) 213 | TCCR2B = (TCCR2B & ~(_BV(CS22) | _BV(CS21))) | _BV(CS20); 214 | 215 | // Set initial pulse width to the rest position (0v after DC decoupling) 216 | OCR2 = REST_DUTY; 217 | } 218 | 219 | int modem_busy() 220 | { 221 | return go; 222 | } 223 | 224 | int modem_get_powerlevel() 225 | { 226 | return radio.get_powerlevel(); 227 | } 228 | 229 | void modem_set_tx_freq(unsigned long freq) 230 | { 231 | radio.set_freq(freq); 232 | } 233 | 234 | void modem_flush_frame() 235 | { 236 | phase_delta = PHASE_DELTA_1200; 237 | phase = 0; 238 | packet_pos = 0; 239 | current_sample_in_baud = 0; 240 | go = true; 241 | 242 | // Key the radio 243 | radio.ptt_on(); 244 | 245 | // Clear the overflow flag, so that the interrupt doesn't go off 246 | // immediately and overrun the next one (p.163). 247 | TIFR2 |= _BV(TOV2); // Yeah, writing a 1 clears the flag. 248 | 249 | // Enable interrupt when TCNT2 reaches TOP (0xFF) (p.151, 163) 250 | TIMSK2 |= _BV(TOIE2); 251 | } 252 | 253 | // This is called at PLAYBACK_RATE Hz to load the next sample. 254 | ISR(TIMER2_OVF_vect) { 255 | 256 | if (go) { 257 | 258 | // If done sending packet 259 | if (packet_pos == modem_packet_size) { 260 | go = false; // End of transmission 261 | OCR2 = REST_DUTY; // Output 0v (after DC coupling) 262 | radio.ptt_off(); // Release PTT 263 | TIMSK2 &= ~_BV(TOIE2); // Disable playback interrupt. 264 | goto end_isr; // Done, gather ISR stats 265 | } 266 | 267 | // If sent SAMPLES_PER_BAUD already, go to the next bit 268 | if (current_sample_in_baud == 0) { // Load up next bit 269 | if ((packet_pos & 7) == 0) // Load up next byte 270 | current_byte = modem_packet[packet_pos >> 3]; 271 | else 272 | current_byte = current_byte / 2; // ">>1" forces int conversion 273 | if ((current_byte & 1) == 0) { 274 | // Toggle tone (1200 <> 2200) 275 | phase_delta ^= (PHASE_DELTA_1200 ^ PHASE_DELTA_2200); 276 | } 277 | } 278 | 279 | phase += phase_delta; 280 | 281 | OCR2 = pgm_read_byte_near(sine_table + ((phase >> 7) & (TABLE_SIZE - 1))); 282 | 283 | if(++current_sample_in_baud == SAMPLES_PER_BAUD) { 284 | current_sample_in_baud = 0; 285 | packet_pos++; 286 | } 287 | } 288 | 289 | end_isr: 290 | #ifdef DEBUG_MODEM 291 | unsigned int t = (unsigned int) TCNT2; 292 | // Signal overrun if we received an interrupt while processing this one 293 | if (TIFR2 & _BV(TOV2)) { 294 | overruns++; 295 | t += 256; 296 | } 297 | // Keep the slowest execution time in slow_isr_time 298 | if (t > slow_isr_time) { 299 | slow_isr_time = t; 300 | slow_packet_pos = packet_pos; 301 | slow_sample_in_baud = current_sample_in_baud; 302 | } 303 | #endif 304 | return; 305 | } 306 | 307 | #ifdef DEBUG_MODEM 308 | void modem_debug() 309 | { 310 | Serial.print("t="); 311 | Serial.print(slow_isr_time); 312 | Serial.print(", pos="); 313 | Serial.print(slow_packet_pos); 314 | Serial.print(", sam="); 315 | Serial.println(slow_sample_in_baud, DEC); 316 | slow_isr_time = 0; 317 | if (overruns) { 318 | Serial.print("MODEM OVERRUNS: "); 319 | Serial.println(overruns); 320 | overruns = 0; 321 | } 322 | } 323 | #endif 324 | -------------------------------------------------------------------------------- /modem.h: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #ifndef __MODEM_H__ 19 | #define __MODEM_H__ 20 | 21 | #define MODEM_MAX_PACKET 512 22 | 23 | extern unsigned char modem_packet[MODEM_MAX_PACKET]; // Upper layer data 24 | extern unsigned int modem_packet_size; // in bits 25 | 26 | void modem_setup(); 27 | void modem_start(); 28 | void modem_flush_frame(); 29 | void modem_set_tx_freq(unsigned long freq); 30 | int modem_get_powerlevel(); 31 | int modem_busy(); 32 | #ifdef DEBUG_MODEM 33 | void modem_debug(); 34 | #endif 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /radio.cpp: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | 19 | #include "radio.h" 20 | -------------------------------------------------------------------------------- /radio.h: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #ifndef __RADIO_H__ 19 | #define __RADIO_H__ 20 | 21 | class Radio { 22 | public: 23 | virtual void setup() = 0; 24 | virtual void ptt_on() = 0; 25 | virtual void ptt_off() = 0; 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /radio_adf7012.cpp: -------------------------------------------------------------------------------- 1 | /* pecan copyright (C) 2012 KT5TK 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #define SPI_BITBANG 19 | #include "config.h" 20 | #include 21 | #include "radio_adf7012.h" 22 | 23 | 24 | #if !defined(SPI_BITBANG) 25 | #include 26 | #endif 27 | 28 | 29 | #if defined(ARDUINO) && ARDUINO >= 100 30 | #include 31 | #else 32 | #include 33 | #endif 34 | 35 | //#include 36 | //SoftwareSerial mySerial(ADC2_PIN, ADC1_PIN); // RX, TX 37 | 38 | 39 | 40 | 41 | const int MAX_RES = 16; 42 | char res_adf7012[MAX_RES]; 43 | unsigned int powerlevel; 44 | 45 | // Configuration storage structs ============================================= 46 | struct { 47 | struct { 48 | unsigned int frequency_error_correction; 49 | unsigned char r_divider; 50 | unsigned char crystal_doubler; 51 | unsigned char crystal_oscillator_disable; 52 | unsigned char clock_out_divider; 53 | unsigned char vco_adjust; 54 | unsigned char output_divider; 55 | } r0; 56 | 57 | struct { 58 | unsigned int fractional_n; 59 | unsigned char integer_n; 60 | unsigned char prescaler; 61 | } r1; 62 | 63 | struct { 64 | unsigned char mod_control; 65 | unsigned char gook; 66 | unsigned char power_amplifier_level; 67 | unsigned int modulation_deviation; 68 | unsigned char gfsk_modulation_control; 69 | unsigned char index_counter; 70 | } r2; 71 | 72 | struct { 73 | unsigned char pll_enable; 74 | unsigned char pa_enable; 75 | unsigned char clkout_enable; 76 | unsigned char data_invert; 77 | unsigned char charge_pump_current; 78 | unsigned char bleed_up; 79 | unsigned char bleed_down; 80 | unsigned char vco_disable; 81 | unsigned char muxout; 82 | unsigned char ld_precision; 83 | unsigned char vco_bias; 84 | unsigned char pa_bias; 85 | unsigned char pll_test_mode; 86 | unsigned char sd_test_mode; 87 | } r3; 88 | } adf_config; 89 | 90 | 91 | 92 | 93 | // Write directly to AVR port in SPIwrite() instead of using digitalWrite() 94 | //#define FAST_IO 95 | 96 | // Configuration functions =================================================== 97 | 98 | // Config resetting functions -------------------------------------------- 99 | void RadioAdf7012::adf_reset_config(void) 100 | { 101 | 102 | adf_reset_register_zero(); 103 | adf_reset_register_one(); 104 | adf_reset_register_two(); 105 | adf_reset_register_three(); 106 | 107 | adf_reset(); 108 | 109 | 110 | // while(!adf_reg_ready()); 111 | 112 | } 113 | 114 | // Power up default settings are defined here: 115 | 116 | void RadioAdf7012::adf_reset_register_zero(void) { 117 | adf_config.r0.frequency_error_correction = 0; // Don't bother for now... 118 | adf_config.r0.r_divider = ADF7012_CRYSTAL_DIVIDER; // Whatever works best for 2m, 1.25m and 70 cm ham bands 119 | adf_config.r0.crystal_doubler = 0; // Who would want that? Lower f_pfd means finer channel steps. 120 | adf_config.r0.crystal_oscillator_disable = 1; // Disable internal crystal oscillator because we have an external VCXO 121 | adf_config.r0.clock_out_divider = 2; // Don't bother for now... 122 | adf_config.r0.vco_adjust = 0; // Don't bother for now... (Will be automatically adjusted until PLL lock is achieved) 123 | adf_config.r0.output_divider = ADF_OUTPUT_DIVIDER_BY_4; // Pre-set div 4 for 2m. Will be changed according tx frequency on the fly 124 | } 125 | 126 | void RadioAdf7012::adf_reset_register_one(void) { 127 | adf_config.r1.integer_n = 111; // Pre-set for 144.390 MHz APRS. Will be changed according tx frequency on the fly 128 | adf_config.r1.fractional_n = 1687; // Pre-set for 144.390 MHz APRS. Will be changed according tx frequency on the fly 129 | adf_config.r1.prescaler = ADF_PRESCALER_8_9; // 8/9 requires an integer_n > 91; 4/5 only requires integer_n > 31 130 | } 131 | 132 | void RadioAdf7012::adf_reset_register_two(void) { 133 | adf_config.r2.mod_control = ADF_MODULATION_ASK; // For AFSK the modulation is done through the external VCXO we don't want any FM generated by the ADF7012 itself 134 | adf_config.r2.gook = 0; // Whatever... This might give us a nicer swing in phase maybe... 135 | adf_config.r2.power_amplifier_level = 16; // 16 is about half maximum power. Output −20dBm at 0x0, and 13 dBm at 0x7E at 868 MHz 136 | 137 | adf_config.r2.modulation_deviation = 16; // 16 is about half maximum amplitude @ ASK. 138 | adf_config.r2.gfsk_modulation_control = 0; // Don't bother for now... 139 | adf_config.r2.index_counter = 0; // Don't bother for now... 140 | } 141 | 142 | void RadioAdf7012::adf_reset_register_three(void) { 143 | adf_config.r3.pll_enable = 0; // Switch off PLL (will be switched on after Ureg is checked and confirmed ok) 144 | adf_config.r3.pa_enable = 0; // Switch off PA (will be switched on when PLL lock is confirmed) 145 | adf_config.r3.clkout_enable = 0; // No clock output needed at the moment 146 | adf_config.r3.data_invert = 1; // Results in a TX signal when TXDATA input is low 147 | adf_config.r3.charge_pump_current = ADF_CP_CURRENT_2_1; // 2.1 mA. This is the maximum 148 | adf_config.r3.bleed_up = 0; // Don't worry, be happy... 149 | adf_config.r3.bleed_down = 0; // Dito 150 | adf_config.r3.vco_disable = 0; // VCO is on 151 | 152 | adf_config.r3.muxout = ADF_MUXOUT_REG_READY; // Lights up the green LED if the ADF7012 is properly powered (changes to lock detection in a later stage) 153 | 154 | adf_config.r3.ld_precision = ADF_LD_PRECISION_3_CYCLES; // What the heck? It is recommended that LDP be set to 1; 0 is more relaxed 155 | adf_config.r3.vco_bias = 6; // In 0.5 mA steps; Default 6 means 3 mA; Maximum (15) is 8 mA 156 | adf_config.r3.pa_bias = 4; // In 1 mA steps; Default 4 means 8 mA; Minimum (0) is 5 mA; Maximum (7) is 12 mA (Datasheet says uA which is bullshit) 157 | adf_config.r3.pll_test_mode = 0; 158 | adf_config.r3.sd_test_mode = 0; 159 | } 160 | 161 | void RadioAdf7012::adf_reset(void) { 162 | // digitalWrite(PTT_PIN, LOW); 163 | digitalWrite(SSpin, HIGH); 164 | digitalWrite(ADF7012_TX_DATA_PIN, HIGH); 165 | digitalWrite(SCKpin, HIGH); 166 | digitalWrite(MOSIpin, HIGH); 167 | 168 | // delay(1); 169 | 170 | // digitalWrite(PTT_PIN, HIGH); 171 | 172 | delay(100); 173 | } 174 | 175 | 176 | 177 | 178 | // Configuration writing functions --------------------------------------- 179 | void RadioAdf7012::adf_write_config(void) { 180 | adf_write_register_zero(); 181 | adf_write_register_one(); 182 | adf_write_register_two(); 183 | adf_write_register_three(); 184 | } 185 | 186 | void RadioAdf7012::adf_write_register_zero(void) { 187 | 188 | unsigned long reg = 189 | (0) | 190 | ((unsigned long)(adf_config.r0.frequency_error_correction & 0x7FF) << 2U) | 191 | ((unsigned long)(adf_config.r0.r_divider & 0xF ) << 13U) | 192 | ((unsigned long)(adf_config.r0.crystal_doubler & 0x1 ) << 17U) | 193 | ((unsigned long)(adf_config.r0.crystal_oscillator_disable & 0x1 ) << 18U) | 194 | ((unsigned long)(adf_config.r0.clock_out_divider & 0xF ) << 19U) | 195 | ((unsigned long)(adf_config.r0.vco_adjust & 0x3 ) << 23U) | 196 | ((unsigned long)(adf_config.r0.output_divider & 0x3 ) << 25U); 197 | 198 | // Serial.print("r0 = "); 199 | // Serial.print(reg, HEX); 200 | // Serial.print(" = "); 201 | // Serial.print(reg, BIN); 202 | // Serial.println(); 203 | 204 | adf_write_register(reg); 205 | } 206 | 207 | void RadioAdf7012::adf_write_register_one(void) { 208 | unsigned long reg = 209 | (1) | 210 | ((unsigned long)(adf_config.r1.fractional_n & 0xFFF) << 2) | 211 | ((unsigned long)(adf_config.r1.integer_n & 0xFF ) << 14) | 212 | ((unsigned long)(adf_config.r1.prescaler & 0x1 ) << 22); 213 | 214 | // Serial.print("r1 = "); 215 | // Serial.print(reg, HEX); 216 | // Serial.print(" = "); 217 | // Serial.print(reg, BIN); 218 | // Serial.println(); 219 | 220 | adf_write_register(reg); 221 | } 222 | 223 | void RadioAdf7012::adf_write_register_two(void) { 224 | unsigned long reg = 225 | (2) | 226 | ((unsigned long)(adf_config.r2.mod_control & 0x3 ) << 2) | 227 | ((unsigned long)(adf_config.r2.gook & 0x1 ) << 4) | 228 | ((unsigned long)(adf_config.r2.power_amplifier_level & 0x3F ) << 5) | 229 | ((unsigned long)(adf_config.r2.modulation_deviation & 0x1FF) << 11) | 230 | ((unsigned long)(adf_config.r2.gfsk_modulation_control & 0x7 ) << 20) | 231 | ((unsigned long)(adf_config.r2.index_counter & 0x3 ) << 23); 232 | 233 | // Serial.print("r2 = "); 234 | // Serial.print(reg, HEX); 235 | // Serial.print(" = "); 236 | // Serial.print(reg, BIN); 237 | // Serial.println(); 238 | 239 | adf_write_register(reg); 240 | } 241 | 242 | void RadioAdf7012::adf_write_register_three(void) { 243 | unsigned long reg = 244 | (3) | 245 | ((unsigned long)(adf_config.r3.pll_enable & 0x1 ) << 2) | 246 | ((unsigned long)(adf_config.r3.pa_enable & 0x1 ) << 3) | 247 | ((unsigned long)(adf_config.r3.clkout_enable & 0x1 ) << 4) | 248 | ((unsigned long)(adf_config.r3.data_invert & 0x1 ) << 5) | 249 | ((unsigned long)(adf_config.r3.charge_pump_current & 0x3 ) << 6) | 250 | ((unsigned long)(adf_config.r3.bleed_up & 0x1 ) << 8) | 251 | ((unsigned long)(adf_config.r3.bleed_down & 0x1 ) << 9) | 252 | ((unsigned long)(adf_config.r3.vco_disable & 0x1 ) << 10) | 253 | ((unsigned long)(adf_config.r3.muxout & 0xF ) << 11) | 254 | ((unsigned long)(adf_config.r3.ld_precision & 0x1 ) << 15) | 255 | ((unsigned long)(adf_config.r3.vco_bias & 0xF ) << 16) | 256 | ((unsigned long)(adf_config.r3.pa_bias & 0x7 ) << 20) | 257 | ((unsigned long)(adf_config.r3.pll_test_mode & 0x1F ) << 23) | 258 | ((unsigned long)(adf_config.r3.sd_test_mode & 0xF ) << 28); 259 | 260 | // Serial.print("r3 = "); 261 | // Serial.print(reg, HEX); 262 | // Serial.print(" = "); 263 | // Serial.print(reg, BIN); 264 | // Serial.println(); 265 | 266 | adf_write_register(reg); 267 | } 268 | 269 | void RadioAdf7012::adf_write_register(unsigned long data) 270 | { 271 | //adf_reset(); 272 | digitalWrite(SSpin, HIGH); 273 | digitalWrite(ADF7012_TX_DATA_PIN, HIGH); 274 | digitalWrite(SCKpin, HIGH); 275 | digitalWrite(MOSIpin, HIGH); 276 | 277 | #if !defined(SPI_BITBANG) // use SPI library 278 | // take the SS pin low to select the ADF7012 chip: 279 | digitalWrite(SSpin,LOW); 280 | 281 | for (int j = 3; j >= 0; j--) // Loop through the 4 bytes of the unsigned long 282 | { 283 | byte wordb = (byte) (data >> (j * 8)); 284 | SPI.transfer(wordb); 285 | //Serial.print(wordb, HEX); 286 | //Serial.print("j"); 287 | // delay(1000); 288 | } 289 | 290 | // take the SS pin high to de-select the ADF7012 chip: 291 | digitalWrite(SSpin,HIGH); 292 | 293 | #else 294 | 295 | // Bit bang SPI to ADF7012 296 | int i; 297 | digitalWrite(SCKpin, LOW ); 298 | delayMicroseconds(2); 299 | digitalWrite(SSpin, LOW); 300 | delayMicroseconds(10); 301 | 302 | for(i=31; i>=0; i--) { 303 | if((data & (unsigned long)(1UL<>i) 304 | digitalWrite(MOSIpin, HIGH); 305 | else 306 | digitalWrite(MOSIpin, LOW); 307 | delayMicroseconds(2); 308 | digitalWrite(SCKpin, HIGH); 309 | delayMicroseconds(10); 310 | digitalWrite(SCKpin, LOW ); 311 | delayMicroseconds(10); 312 | } 313 | 314 | delayMicroseconds(2); 315 | digitalWrite(SSpin, HIGH); 316 | 317 | #endif 318 | 319 | } 320 | 321 | 322 | 323 | 324 | int RadioAdf7012::adf_lock(void) 325 | { 326 | // fiddle around with bias and adjust capacity until the vco locks 327 | 328 | int adj = adf_config.r0.vco_adjust; // use default start values from setup 329 | int bias = adf_config.r3.vco_bias; // or the updated ones that worked last time 330 | 331 | adf_config.r3.pll_enable = 1; 332 | adf_config.r3.muxout = ADF_MUXOUT_DIGITAL_LOCK; 333 | adf_write_config(); 334 | delay(50); 335 | adf_locked(); 336 | 337 | while(!adf_locked()) { 338 | // Serial.print("VCO not in lock. Trying adj: "); 339 | // Serial.print(adj); 340 | // Serial.print(" and bias: "); 341 | // Serial.println(bias); 342 | adf_config.r0.vco_adjust = adj; 343 | adf_config.r3.vco_bias = bias; 344 | adf_config.r3.muxout = ADF_MUXOUT_DIGITAL_LOCK; 345 | adf_write_config(); 346 | delay(50); 347 | if(++bias == 14) { 348 | bias = 1; 349 | if(++adj == 4) { 350 | Serial.println("Couldn't achieve PLL lock :( "); 351 | // Using best guess defaults: 352 | adf_config.r0.vco_adjust = 0; 353 | adf_config.r3.vco_bias = 5; 354 | 355 | return 0; 356 | } 357 | } 358 | } 359 | //Serial.println("VCO was locked"); 360 | 361 | 362 | return 1; 363 | } 364 | 365 | int RadioAdf7012::adf_locked(void) 366 | { 367 | analogReference(DEFAULT); 368 | analogRead(ADC6_PIN); 369 | int adc = analogRead(ADC6_PIN); 370 | // Serial.print("A6 lock: "); 371 | Serial.println(adc); 372 | delay(500); 373 | if (adc > 500U) 374 | { 375 | return 1; 376 | } 377 | else 378 | { 379 | return 0; 380 | } 381 | } 382 | 383 | 384 | void RadioAdf7012::set_freq(unsigned long freq) 385 | { 386 | 387 | // Set the output divider according to recommended ranges given in ADF7012 datasheet 388 | // 2012-08-10 TK lowered the borders a bit in order to keep n high enough for 144, 222 and 430 MHz amateur bands 389 | // with a constant crystal divider of 8 390 | adf_config.r0.output_divider = ADF_OUTPUT_DIVIDER_BY_1; 391 | if (freq < 410000000) { adf_config.r0.output_divider = ADF_OUTPUT_DIVIDER_BY_2; }; 392 | if (freq < 210000000) { adf_config.r0.output_divider = ADF_OUTPUT_DIVIDER_BY_4; }; 393 | if (freq < 130000000) { adf_config.r0.output_divider = ADF_OUTPUT_DIVIDER_BY_8; }; 394 | 395 | unsigned long f_pfd = ADF7012_CRYSTAL_FREQ / adf_config.r0.r_divider; 396 | 397 | unsigned int n = (unsigned int)(freq / f_pfd); 398 | 399 | float ratio = (float)freq / (float)f_pfd; 400 | float rest = ratio - (float)n; 401 | 402 | 403 | unsigned long m = (unsigned long)(rest * 4096); 404 | 405 | 406 | 407 | adf_config.r1.integer_n = n; 408 | adf_config.r1.fractional_n = m; 409 | /* 410 | Serial.println("ADF set Freq:"); 411 | Serial.print("n = "); 412 | Serial.println(n); 413 | Serial.print("m = "); 414 | Serial.println(m); 415 | Serial.print("f_pfd = "); 416 | Serial.println(f_pfd); 417 | */ 418 | } 419 | 420 | 421 | void RadioAdf7012::setup() 422 | { 423 | Serial.println(); 424 | Serial.println(); 425 | Serial.println("=============================================================="); 426 | Serial.println("ADF7012 setup start"); 427 | Serial.println("=============================================================="); 428 | 429 | // mySerial.begin(9600); 430 | // mySerial.println("SS setup start"); 431 | 432 | pinMode(PTT_PIN, OUTPUT); 433 | // pinMode(TX_PA_PIN, OUTPUT); 434 | pinMode(SCKpin, OUTPUT); 435 | pinMode(SSpin, OUTPUT); 436 | pinMode(MOSIpin, OUTPUT); 437 | pinMode(ADF7012_TX_DATA_PIN, OUTPUT); 438 | 439 | #if !defined(SPI_BITBANG) 440 | // Set up SPI 441 | SPI.setBitOrder(MSBFIRST); 442 | SPI.setDataMode(SPI_MODE0); 443 | SPI.setClockDivider(SPI_CLOCK_DIV32); 444 | 445 | // initialize SPI: 446 | SPI.begin(); 447 | #endif 448 | 449 | adf_reset_config(); 450 | set_freq(RADIO_FREQUENCY); // Set the default frequency 451 | adf_write_config(); 452 | digitalWrite(ADF7012_TX_DATA_PIN, LOW); 453 | // digitalWrite(TX_PA_PIN, HIGH); // HIGH = off => make sure the PA is off after boot. 454 | delay(100); 455 | 456 | Serial.println("ADF7012 setup done"); 457 | 458 | } 459 | 460 | void RadioAdf7012::ptt_on() 461 | { 462 | 463 | digitalWrite(PTT_PIN, HIGH); 464 | digitalWrite(ADF7012_TX_DATA_PIN, LOW); 465 | adf_config.r3.pa_enable = 0; 466 | adf_config.r2.power_amplifier_level = 0; 467 | adf_config.r3.muxout = ADF_MUXOUT_REG_READY; 468 | 469 | adf_write_config(); 470 | delay(100); 471 | 472 | // Do we have good power on the ADF7012 voltage regulator? 473 | analogReference(DEFAULT); 474 | analogRead(ADC6_PIN); 475 | int adc = analogRead(ADC6_PIN); 476 | Serial.print("ADC6 = "); 477 | Serial.println(adc); 478 | if (adc < 500U) // Power is bad 479 | { 480 | Serial.println("ERROR: Can't power up the ADF7012!"); 481 | } 482 | else // Power is good apparently 483 | { 484 | /* 485 | // Do some logic checks to see if we can properly talk to the ADF7012 486 | adf_config.r3.muxout = ADF_MUXOUT_LOGIC_LOW; 487 | adf_write_config(); 488 | delay(100); 489 | analogRead(ADC6_PIN); 490 | adc = analogRead(ADC6_PIN); 491 | Serial.print("MUXOUT should be LOW now. Measuring ADC6 = "); 492 | Serial.println(adc); 493 | 494 | adf_config.r3.muxout = ADF_MUXOUT_LOGIC_HIGH; 495 | adf_write_config(); 496 | delay(100); 497 | analogRead(ADC6_PIN); 498 | adc = analogRead(ADC6_PIN); 499 | Serial.print("MUXOUT should be HIGH now. Measuring ADC6 = "); 500 | Serial.println(adc); 501 | */ 502 | 503 | //predict the exact output frequency 504 | // unsigned long freq_calculated = (ADF7012_CRYSTAL_FREQ / adf_config.r0.r_divider) * ((float)adf_config.r1.integer_n + ((float)adf_config.r1.fractional_n / 4096.0)); 505 | 506 | // Serial.print("ADF7012 configured for "); 507 | // Serial.print(freq_calculated); 508 | // Serial.println(" Hz"); 509 | 510 | if (adf_lock()) 511 | { 512 | adf_config.r3.pa_enable = 1; 513 | adf_config.r2.power_amplifier_level = 63; //63 is max power 514 | // Serial.println("Turning on the PA"); 515 | adf_write_config(); 516 | delay(50); 517 | //digitalWrite(TX_PA_PIN, LOW); // Switch on the ADL5531 final PA (LOW = on) 518 | //delay(100); 519 | // Measure HF output 520 | analogRead(ADC6_PIN); // blank read for equilibration 521 | powerlevel = analogRead(ADC6_PIN); 522 | 523 | // Serial.print("HF output ADC6 = "); 524 | // Serial.println(powerlevel); 525 | if (powerlevel > 255) 526 | { 527 | powerlevel = 255; 528 | } 529 | } 530 | else 531 | { 532 | 533 | ptt_off(); 534 | 535 | /* 536 | // Testing with low power 537 | adf_config.r3.pa_enable = 1; 538 | adf_config.r2.power_amplifier_level = 20; //63 is max power 539 | Serial.println("Low power"); 540 | adf_write_config(); 541 | delay(100); 542 | // Measure HF output 543 | analogRead(ADC6_PIN); 544 | adc = analogRead(ADC6_PIN); 545 | Serial.print("HF output ADC6 = "); 546 | Serial.println(adc); 547 | */ 548 | } 549 | 550 | } 551 | } 552 | 553 | void RadioAdf7012::ptt_off() 554 | { 555 | // divider_test(); 556 | // change(); 557 | // digitalWrite(TX_PA_PIN, HIGH); // Switch off the ADL5531 final PA (HIGH = off) 558 | adf_config.r3.pa_enable = 0; 559 | adf_config.r2.power_amplifier_level = 0; 560 | adf_write_config(); 561 | delay(100); 562 | 563 | digitalWrite(PTT_PIN, LOW); 564 | digitalWrite(ADF7012_TX_DATA_PIN, LOW); 565 | 566 | } 567 | 568 | int RadioAdf7012::get_powerlevel() 569 | { 570 | return powerlevel; 571 | } 572 | 573 | /* 574 | void RadioAdf7012::lock_test(void) 575 | { 576 | analogReference(DEFAULT); 577 | adf_config.r3.muxout = ADF_MUXOUT_ANALOGUE_LOCK; 578 | adf_config.r3.pll_enable = 1; 579 | 580 | int max_a3 = 0; 581 | int max_divider = 0; 582 | int max_adjust = 0; 583 | int max_bias = 0; 584 | 585 | for( int divider = 1; divider < 16U; divider++) 586 | { 587 | adf_config.r0.r_divider = divider; 588 | set_freq(144390000UL); 589 | for (int adjust = 0U; adjust < 4U; adjust++) 590 | { 591 | adf_config.r0.vco_adjust = adjust; 592 | for (int bias = 1U; bias < 16; bias++) 593 | { 594 | adf_config.r3.vco_bias = bias; 595 | adf_write_config(); 596 | delay(1500); 597 | //Serial.print("."); 598 | digitalWrite(LED_PIN, LOW); 599 | delay(100); 600 | digitalWrite(LED_PIN, HIGH); 601 | delay(100); 602 | digitalWrite(LED_PIN, LOW); 603 | delay(100); 604 | 605 | 606 | analogRead(A3); 607 | if (analogRead(A3) > max_a3) 608 | { 609 | max_a3 = analogRead(A3); 610 | max_divider = divider; 611 | max_adjust = adjust; 612 | max_bias = bias; 613 | } 614 | } 615 | //Serial.println("*"); 616 | } 617 | //Serial.println("#"); 618 | } 619 | 620 | Serial.println(); 621 | Serial.print("Lock @ "); 622 | Serial.print(max_a3); 623 | Serial.print(","); 624 | Serial.print(max_divider); 625 | Serial.print(","); 626 | Serial.print(max_adjust); 627 | Serial.print(","); 628 | Serial.println(max_bias); 629 | 630 | 631 | } 632 | 633 | 634 | void RadioAdf7012::divider_test(void) 635 | { 636 | adf_config.r3.muxout = ADF_MUXOUT_RF_R_DIVIDER; 637 | adf_config.r3.pll_enable = 1; 638 | adf_config.r0.r_divider = 15; 639 | set_freq(144390000UL); 640 | adf_config.r3.clkout_enable = 1; 641 | adf_config.r0.clock_out_divider = 3; 642 | adf_config.r3.vco_bias = 6; 643 | adf_write_config(); 644 | 645 | } 646 | 647 | 648 | 649 | 650 | void RadioAdf7012::change(void) 651 | { 652 | 653 | mySerial.println("This is your chance to change the ADF7012 registers:"); 654 | 655 | int timeout = 1000; 656 | while (timeout > 0) 657 | { 658 | if (mySerial.available()) 659 | { 660 | char c = mySerial.read(); 661 | if ( c == 'a' ) 662 | { 663 | mySerial.println("Option a"); 664 | timeout = 0; 665 | } 666 | if ( c == 'b' ) 667 | { 668 | mySerial.println("Option b"); 669 | timeout = 0; 670 | } 671 | } 672 | delay(10); 673 | timeout--; 674 | } 675 | mySerial.println("game over..."); 676 | //mySerial.end(); 677 | } 678 | 679 | */ 680 | 681 | -------------------------------------------------------------------------------- /radio_adf7012.h: -------------------------------------------------------------------------------- 1 | /* pecan copyright (C) 2012 KT5TK 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #ifndef __RADIO_ADF7012_H__ 19 | #define __RADIO_ADF7012_H__ 20 | 21 | #include "radio.h" 22 | 23 | class RadioAdf7012 : public Radio { 24 | public: 25 | virtual void setup(); 26 | virtual void ptt_on(); 27 | virtual void ptt_off(); 28 | virtual void set_freq(unsigned long freq); 29 | virtual int get_powerlevel(); 30 | private: 31 | void send_cmd(const char *cmd, int cmd_len, char *res_adf7012, int res_len); 32 | void adf_reset_config(void); 33 | void adf_reset_register_zero(void); 34 | void adf_reset_register_one(void); 35 | void adf_reset_register_two(void); 36 | void adf_reset_register_three(void); 37 | void adf_write_config(void); 38 | void adf_write_register_zero(void); 39 | void adf_write_register_one(void); 40 | void adf_write_register_two(void); 41 | void adf_write_register_three(void); 42 | void adf_write_register(unsigned long data); 43 | void adf_reset(void); 44 | int adf_lock(void); 45 | int adf_locked(void); 46 | void lock_test(void); 47 | void divider_test(void); 48 | void change(void); 49 | 50 | // Register Constants ======================================================== 51 | 52 | // Register 1 ---------------------------------------------------------------- 53 | #define ADF_OUTPUT_DIVIDER_BY_1 0 54 | #define ADF_OUTPUT_DIVIDER_BY_2 1 55 | #define ADF_OUTPUT_DIVIDER_BY_4 2 56 | #define ADF_OUTPUT_DIVIDER_BY_8 3 57 | 58 | 59 | 60 | // Register 1 ---------------------------------------------------------------- 61 | #define ADF_PRESCALER_4_5 0 62 | #define ADF_PRESCALER_8_9 1 63 | #define ADF_PRESCALER ADF_PRESCALER_8_9 64 | 65 | // Register 2 ---------------------------------------------------------------- 66 | #define ADF_MODULATION_FSK 0 67 | #define ADF_MODULATION_GFSK 1 68 | #define ADF_MODULATION_ASK 2 69 | #define ADF_MODULATION_OOK 3 70 | 71 | // Register 3 ---------------------------------------------------------------- 72 | #define ADF_CP_CURRENT_0_3 0 73 | #define ADF_CP_CURRENT_0_9 1 74 | #define ADF_CP_CURRENT_1_5 2 75 | #define ADF_CP_CURRENT_2_1 3 76 | #define ADF_MUXOUT_LOGIC_LOW 0 77 | #define ADF_MUXOUT_LOGIC_HIGH 1 78 | #define ADF_MUXOUT_REG_READY 3 79 | #define ADF_MUXOUT_DIGITAL_LOCK 4 80 | #define ADF_MUXOUT_ANALOGUE_LOCK 5 81 | #define ADF_MUXOUT_R_DIVIDER_2 6 82 | #define ADF_MUXOUT_N_DIVIDER_2 7 83 | #define ADF_MUXOUT_RF_R_DIVIDER 8 84 | #define ADF_MUXOUT_DATA_RATE 9 85 | #define ADF_MUXOUT_BATT_2_35 10 86 | #define ADF_MUXOUT_BATT_2_75 11 87 | #define ADF_MUXOUT_BATT_3 12 88 | #define ADF_MUXOUT_BATT_3_25 13 89 | #define ADF_MUXOUT_TEST_MODE 14 90 | #define ADF_MUXOUT_SD_TEST_MODE 15 91 | #define ADF_LD_PRECISION_3_CYCLES 0 92 | #define ADF_LD_PRECISION_5_CYCLES 1 93 | 94 | // PIN Definitions: 95 | #define SCKpin 13 // SCK 96 | #define SSpin 10 // SS 97 | #define MOSIpin 11 // MOSI 98 | 99 | #define ADF7012_CRYSTAL_FREQ VCXO_FREQ 100 | 101 | }; 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /radio_hx1.cpp: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #include "config.h" 19 | #include "radio_hx1.h" 20 | 21 | #if defined(ARDUINO) && ARDUINO >= 100 22 | #include 23 | #else 24 | #include 25 | #endif 26 | 27 | void RadioHx1::setup() 28 | { 29 | // Nothing to set up... This is a really simple radio 30 | } 31 | 32 | void RadioHx1::ptt_on() 33 | { 34 | digitalWrite(PTT_PIN, HIGH); 35 | delay(5); // The HX1 takes 5 ms from PTT to full RF 36 | } 37 | 38 | void RadioHx1::ptt_off() 39 | { 40 | digitalWrite(PTT_PIN, LOW); 41 | } 42 | -------------------------------------------------------------------------------- /radio_hx1.h: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #ifndef __RADIO_HX1_H__ 19 | #define __RADIO_HX1_H__ 20 | 21 | #include "radio.h" 22 | 23 | class RadioHx1 : public Radio { 24 | public: 25 | virtual void setup(); 26 | virtual void ptt_on(); 27 | virtual void ptt_off(); 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /radio_mx146.cpp: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #include "config.h" 19 | #include "radio_mx146.h" 20 | #include 21 | 22 | #if defined(ARDUINO) && ARDUINO >= 100 23 | #include 24 | #else 25 | #include 26 | #endif 27 | 28 | const int MAX_RES = 16; 29 | char res[MAX_RES]; 30 | 31 | void RadioMx146::send_cmd(const char *cmd, int cmd_len, char *res, int res_len) 32 | { 33 | int i = 0; 34 | int success; 35 | Wire.beginTransmission(0x48); 36 | for (i = 0; i < cmd_len; i++) { 37 | #if defined(ARDUINO) && ARDUINO >= 100 38 | Wire.write(cmd[i]); 39 | #else 40 | Wire.send(cmd[i]); 41 | #endif 42 | } 43 | success = Wire.endTransmission(); 44 | delay(100); 45 | 46 | if (res_len > 0) 47 | { 48 | Wire.requestFrom(0x48, res_len); 49 | while(Wire.available() > 0 && i < res_len) 50 | { 51 | #if defined(ARDUINO) && ARDUINO >= 100 52 | res[i] = Wire.read(); 53 | #else 54 | res[i] = Wire.receive(); 55 | #endif 56 | i++; 57 | } 58 | } 59 | } 60 | 61 | void RadioMx146::set_freq(unsigned long freq) 62 | { 63 | char cmd[5]; 64 | cmd[0] = 'B'; 65 | *((unsigned long *)(cmd+1)) = freq; 66 | send_cmd(cmd, 5, res, MAX_RES); 67 | } 68 | 69 | int RadioMx146::query_temp() 70 | { 71 | send_cmd("QT", 2, res, 1); 72 | return res[0]; 73 | } 74 | 75 | void RadioMx146::setup() 76 | { 77 | Wire.begin(); // Join the I2C bus as a master 78 | set_freq(RADIO_FREQUENCY); // Set the default frequency 79 | //set_freq(MX146_FREQUENCY_TESTING); // Set the testing frequency 80 | } 81 | 82 | void RadioMx146::ptt_on() 83 | { 84 | digitalWrite(PTT_PIN, HIGH); 85 | // TODO: should wait for the "RDY" signal 86 | delay(25); 87 | } 88 | 89 | void RadioMx146::ptt_off() 90 | { 91 | digitalWrite(PTT_PIN, LOW); 92 | } 93 | -------------------------------------------------------------------------------- /radio_mx146.h: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #ifndef __RADIO_MX146_H__ 19 | #define __RADIO_MX146_H__ 20 | 21 | #include "radio.h" 22 | 23 | class RadioMx146 : public Radio { 24 | public: 25 | virtual void setup(); 26 | virtual void ptt_on(); 27 | virtual void ptt_off(); 28 | virtual void set_freq(unsigned long freq); 29 | private: 30 | void send_cmd(const char *cmd, int cmd_len, char *res, int res_len); 31 | 32 | int query_temp(); 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /radio_si446x.cpp: -------------------------------------------------------------------------------- 1 | /* pecanpico2 Si446x Driver copyright (C) 2013 KT5TK 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | 19 | #include "config.h" 20 | #include 21 | #include "radio_si446x.h" 22 | 23 | 24 | 25 | #include 26 | 27 | 28 | 29 | #if defined(ARDUINO) && ARDUINO >= 100 30 | #include 31 | #else 32 | #include 33 | #endif 34 | 35 | 36 | unsigned int si446x_powerlevel = 0; 37 | unsigned long active_freq = RADIO_FREQUENCY; 38 | 39 | //#include 40 | //SoftwareSerial mySerial(ADC2_PIN, ADC1_PIN); // RX, TX 41 | 42 | 43 | void RadioSi446x::SendCmdReceiveAnswer(int byteCountTx, int byteCountRx, const char* pData) 44 | { 45 | 46 | /* There was a bug in A1 hardware that will not handle 1 byte commands. 47 | It was supposedly fixed in B0 but the fix didn't make it at the last minute, so here we go again */ 48 | if (byteCountTx == 1) 49 | byteCountTx++; 50 | 51 | digitalWrite(SSpin,LOW); 52 | char answer; 53 | 54 | // Serial.print("sent: "); 55 | 56 | // for (int j = 0; j < byteCountTx; j++) // Loop through the bytes of the pData 57 | // { 58 | // byte wordb = pData[j]; 59 | // Serial.print(wordb,HEX); 60 | // Serial.print(" "); 61 | // } 62 | 63 | // Serial.println(); 64 | 65 | for (int j = 0; j < byteCountTx; j++) // Loop through the bytes of the pData 66 | { 67 | byte wordb = pData[j]; 68 | SPI.transfer(wordb); 69 | } 70 | 71 | digitalWrite(SSpin,HIGH); 72 | 73 | delayMicroseconds(20); 74 | 75 | digitalWrite(SSpin,LOW); 76 | 77 | int reply = 0x00; 78 | while (reply != 0xFF) 79 | { 80 | reply = SPI.transfer(0x44); 81 | Serial.print(reply,HEX); 82 | Serial.print(" "); 83 | if (reply != 0xFF) 84 | { 85 | digitalWrite(SSpin,HIGH); 86 | delayMicroseconds(20); 87 | digitalWrite(SSpin,LOW); 88 | } 89 | } 90 | 91 | Serial.println(); 92 | 93 | Serial.print("rx: "); 94 | 95 | 96 | for (int k = 1; k < byteCountRx; k++) 97 | { 98 | Serial.print(SPI.transfer(0x44), HEX); 99 | Serial.print(" "); 100 | } 101 | 102 | digitalWrite(SSpin,HIGH); 103 | Serial.println(); 104 | delay(500); // Wait half a second to prevent Serial buffer overflow 105 | } 106 | 107 | 108 | // Actions =============================================================== 109 | 110 | // Config reset ---------------------------------------------------------- 111 | void RadioSi446x::reset(void) 112 | { 113 | digitalWrite(VCXO_ENABLE_PIN,HIGH); 114 | Serial.println("VCXO is enabled"); 115 | delay(600); 116 | 117 | 118 | 119 | digitalWrite(RADIO_SDN_PIN, HIGH); // active high shutdown = reset 120 | delay(600); 121 | digitalWrite(RADIO_SDN_PIN, LOW); // booting 122 | Serial.println("Radio is powered up"); 123 | 124 | // Start talking to the Si446X radio chip 125 | 126 | const char PART_INFO_command[] = {0x01}; // Part Info 127 | SendCmdReceiveAnswer(1, 9, PART_INFO_command); 128 | Serial.println("Part info was checked"); 129 | 130 | //divide VCXO_FREQ into its bytes; MSB first 131 | unsigned int x3 = VCXO_FREQ / 0x1000000; 132 | unsigned int x2 = (VCXO_FREQ - x3 * 0x1000000) / 0x10000; 133 | unsigned int x1 = (VCXO_FREQ - x3 * 0x1000000 - x2 * 0x10000) / 0x100; 134 | unsigned int x0 = (VCXO_FREQ - x3 * 0x1000000 - x2 * 0x10000 - x1 * 0x100); 135 | 136 | //POWER_UP 137 | const char init_command[] = {0x02, 0x01, 0x01, x3, x2, x1, x0};// no patch, boot main app. img, FREQ_VCXO, return 1 byte 138 | SendCmdReceiveAnswer(7, 1 ,init_command); 139 | 140 | Serial.println("Radio booted"); 141 | 142 | const char get_int_status_command[] = {0x20, 0x00, 0x00, 0x00}; // Clear all pending interrupts and get the interrupt status back 143 | SendCmdReceiveAnswer(4, 9, get_int_status_command); 144 | 145 | 146 | Serial.println("Radio ready"); 147 | 148 | const char gpio_pin_cfg_command[] = {0x13, 0x02, 0x02, 0x02, 0x02, 0x08, 0x11, 0x00}; // Set all GPIOs LOW; Link NIRQ to CTS; Link SDO to MISO; Max drive strength 149 | SendCmdReceiveAnswer(8, 8, gpio_pin_cfg_command); 150 | 151 | Serial.println("LEDs should be switched off at this point"); 152 | 153 | setFrequency(active_freq); 154 | Serial.println("Frequency set"); 155 | 156 | setModem(); 157 | Serial.println("CW mode set"); 158 | 159 | tune_tx(); 160 | Serial.println("TX tune"); 161 | 162 | 163 | 164 | } 165 | 166 | void RadioSi446x::start_tx() 167 | { 168 | char change_state_command[] = {0x34, 0x07}; // Change to TX state 169 | SendCmdReceiveAnswer(2, 1, change_state_command); 170 | 171 | } 172 | 173 | void RadioSi446x::stop_tx() 174 | { 175 | char change_state_command[] = {0x34, 0x03}; // Change to Ready state 176 | SendCmdReceiveAnswer(2, 1, change_state_command); 177 | 178 | } 179 | 180 | void RadioSi446x::tune_tx() 181 | { 182 | char change_state_command[] = {0x34, 0x05}; // Change to TX tune state 183 | SendCmdReceiveAnswer(2, 1, change_state_command); 184 | 185 | } 186 | 187 | 188 | 189 | 190 | // Configuration parameter functions --------------------------------------- 191 | 192 | void RadioSi446x::setModem() 193 | { 194 | // Set to CW mode 195 | Serial.println("Setting modem into CW mode"); 196 | char set_modem_mod_type_command[] = {0x11, 0x20, 0x01, 0x00, 0x00}; 197 | SendCmdReceiveAnswer(5, 1, set_modem_mod_type_command); 198 | 199 | } 200 | 201 | 202 | 203 | void RadioSi446x::setFrequency(unsigned long freq) 204 | { 205 | 206 | // Set the output divider according to recommended ranges given in Si446x datasheet 207 | int outdiv = 4; 208 | int band = 0; 209 | if (freq < 705000000UL) { outdiv = 6; band = 1;}; 210 | if (freq < 525000000UL) { outdiv = 8; band = 2;}; 211 | if (freq < 353000000UL) { outdiv = 12; band = 3;}; 212 | if (freq < 239000000UL) { outdiv = 16; band = 4;}; 213 | if (freq < 177000000UL) { outdiv = 24; band = 5;}; 214 | 215 | 216 | unsigned long f_pfd = 2 * VCXO_FREQ / outdiv; 217 | 218 | unsigned int n = ((unsigned int)(freq / f_pfd)) - 1; 219 | 220 | float ratio = (float)freq / (float)f_pfd; 221 | float rest = ratio - (float)n; 222 | 223 | 224 | unsigned long m = (unsigned long)(rest * 524288UL); 225 | 226 | 227 | 228 | // set the band parameter 229 | unsigned int sy_sel = 8; // 230 | char set_band_property_command[] = {0x11, 0x20, 0x01, 0x51, (band + sy_sel)}; // 231 | // send parameters 232 | SendCmdReceiveAnswer(5, 1, set_band_property_command); 233 | 234 | // Set the pll parameters 235 | unsigned int m2 = m / 0x10000; 236 | unsigned int m1 = (m - m2 * 0x10000) / 0x100; 237 | unsigned int m0 = (m - m2 * 0x10000 - m1 * 0x100); 238 | // assemble parameter string 239 | char set_frequency_property_command[] = {0x11, 0x40, 0x04, 0x00, n, m2, m1, m0}; 240 | // send parameters 241 | SendCmdReceiveAnswer(8, 1, set_frequency_property_command); 242 | 243 | 244 | } 245 | 246 | 247 | // Public functions ----------------------------------------------------------- 248 | 249 | void RadioSi446x::setup() 250 | { 251 | // Not much radio configuration to do here 252 | // because we initialize the transmitter each time right before we transmit a packet 253 | 254 | // Just make sure that the VCXO is on and the transmitter chip is switched off at boot up 255 | pinMode(VCXO_ENABLE_PIN, OUTPUT); 256 | digitalWrite(VCXO_ENABLE_PIN,HIGH); 257 | Serial.println("VCXO is enabled"); 258 | 259 | pinMode(RADIO_SDN_PIN, OUTPUT); 260 | delay(300); 261 | digitalWrite(RADIO_SDN_PIN, HIGH); // active high = shutdown 262 | 263 | // Set up SPI 264 | pinMode(SCKpin, OUTPUT); 265 | pinMode(SSpin, OUTPUT); 266 | pinMode(MOSIpin, OUTPUT); 267 | 268 | digitalWrite(SSpin, HIGH); // ensure SS stays high for now 269 | 270 | // initialize SPI: 271 | SPI.begin(); 272 | SPI.setDataMode(SPI_MODE0); 273 | SPI.setBitOrder(MSBFIRST); 274 | SPI.setClockDivider(SPI_CLOCK_DIV8); // 8 MHz / 8 = 1 MHz 275 | delay(600); 276 | 277 | Serial.println("SPI is initialized"); 278 | 279 | } 280 | 281 | void RadioSi446x::ptt_on() 282 | { 283 | 284 | digitalWrite(VCXO_ENABLE_PIN, HIGH); 285 | reset(); 286 | // turn on the blue LED (GPIO2) to indicate TX 287 | char gpio_pin_cfg_command2[] = {0x13, 0x02, 0x02, 0x03, 0x02, 0x08, 0x11, 0x00}; // Set GPIO2 HIGH; Link NIRQ to CTS; Link SDO to MISO; Max drive strength 288 | SendCmdReceiveAnswer(8, 1, gpio_pin_cfg_command2); 289 | 290 | start_tx(); 291 | si446x_powerlevel = 1023; 292 | } 293 | 294 | void RadioSi446x::ptt_off() 295 | { 296 | stop_tx(); 297 | si446x_powerlevel = 0; 298 | // turn off the blue LED (GPIO2) 299 | char gpio_pin_cfg_command0[] = {0x13, 0x02, 0x02, 0x02, 0x02, 0x08, 0x11, 0x00}; // Set all GPIOs LOW; Link NIRQ to CTS; Link SDO to MISO; Max drive strength 300 | SendCmdReceiveAnswer(8, 1, gpio_pin_cfg_command0); 301 | 302 | digitalWrite(RADIO_SDN_PIN, HIGH); // active high = shutdown 303 | //digitalWrite(VCXO_ENABLE_PIN, LOW); //keep enabled for now 304 | 305 | } 306 | 307 | void RadioSi446x::set_freq(unsigned long freq) 308 | { 309 | active_freq = freq; 310 | } 311 | 312 | 313 | int RadioSi446x::get_powerlevel() 314 | { 315 | return si446x_powerlevel; 316 | 317 | } 318 | 319 | -------------------------------------------------------------------------------- /radio_si446x.h: -------------------------------------------------------------------------------- 1 | /* pecanpico2 copyright (C) 2013 KT5TK 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #ifndef __RADIO_SI446X_H__ 19 | #define __RADIO_SI446X_H__ 20 | 21 | #include "radio.h" 22 | 23 | class RadioSi446x : public Radio { 24 | public: 25 | virtual void setup(); 26 | virtual void ptt_on(); 27 | virtual void ptt_off(); 28 | virtual void set_freq(unsigned long freq); 29 | virtual int get_powerlevel(); 30 | private: 31 | void SendCmdReceiveAnswer(int byteCountTx, int byteCountRx, const char* pData); 32 | void reset(void); 33 | void setModem(void); 34 | void start_tx(void); 35 | void stop_tx(void); 36 | void tune_tx(void); 37 | void setFrequency(unsigned long freq); 38 | 39 | 40 | 41 | // PIN Definitions: 42 | #define RADIO_SDN_PIN 7 43 | #define VCXO_ENABLE_PIN 8 44 | 45 | #define SCKpin 13 // SCK 46 | #define SSpin 10 // SS 47 | #define MOSIpin 11 // MOSI 48 | #define MISOpin 12 // MISO 49 | 50 | 51 | 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /sensors.cpp: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | /* Credit to: 19 | * 20 | * cathedrow for this idea on using the ADC as a volt meter: 21 | * http://code.google.com/p/tinkerit/wiki/SecretVoltmeter 22 | */ 23 | 24 | #include "config.h" 25 | #include "sensors.h" 26 | #include 27 | 28 | #if defined(ARDUINO) && ARDUINO >= 100 29 | #include 30 | #else 31 | #include 32 | #endif 33 | 34 | 35 | 36 | #define BMP085_ADDRESS 0x77 // I2C address of BMP085 37 | 38 | const unsigned char OSS = 0; // Oversampling Setting 39 | unsigned char timeout_error = 0; 40 | 41 | // Calibration values 42 | //int ac1; 43 | #define BMP085_AC1 (int)bmp085ReadInt(0xAA) 44 | 45 | //int ac2; 46 | #define BMP085_AC2 (int)bmp085ReadInt(0xAC) 47 | 48 | //int ac3; 49 | #define BMP085_AC3 (int)bmp085ReadInt(0xAE) 50 | 51 | //unsigned int ac4; 52 | #define BMP085_AC4 (unsigned int)bmp085ReadInt(0xB0) 53 | 54 | //unsigned int ac5; 55 | #define BMP085_AC5 (unsigned int)bmp085ReadInt(0xB2) 56 | 57 | //unsigned int ac6; 58 | #define BMP085_AC6 (unsigned int)bmp085ReadInt(0xB4) 59 | 60 | //int b1; 61 | #define BMP085_B1 (int)bmp085ReadInt(0xB6) 62 | 63 | int b2; 64 | #define BMP085_B2 (int)bmp085ReadInt(0xB8) 65 | 66 | int mb; 67 | #define BMP085_MB (int)bmp085ReadInt(0xBA) 68 | 69 | int mc; 70 | #define BMP085_MC (int)bmp085ReadInt(0xBC) 71 | 72 | int md; 73 | #define BMP085_MD (int)bmp085ReadInt(0xBE) 74 | 75 | 76 | 77 | 78 | 79 | // b5 is calculated in bmp085GetTemperature(...), this variable is also used in bmp085GetPressure(...) 80 | // so ...Temperature(...) must be called before ...Pressure(...). 81 | long b5; 82 | 83 | 84 | 85 | 86 | /* 87 | * sensors_aref: measure an external voltage hooked up to the AREF pin, 88 | * optionally (and recommendably) through a pull-up resistor. This is 89 | * incompatible with all other functions that use internal references 90 | * (see config.h) 91 | */ 92 | #ifdef USE_AREF 93 | void sensors_setup() 94 | { 95 | // Nothing to set-up when AREF is in use 96 | } 97 | 98 | unsigned long sensors_aref() 99 | { 100 | unsigned long result; 101 | // Read 1.1V reference against AREF (p. 262) 102 | ADMUX = _BV(MUX3) | _BV(MUX2) | _BV(MUX1); 103 | delay(2); // Wait for Vref to settle 104 | ADCSRA |= _BV(ADSC); // Convert 105 | while (bit_is_set(ADCSRA,ADSC)); 106 | result = (ADCH << 8) | ADCL; 107 | 108 | // millivolts = 1.1 * 1024 * 1000 / result 109 | result = 1126400 / result; 110 | 111 | // aref = read aref * (32K + AREF_PULLUP) / 32K 112 | result = result * (32000UL + AREF_PULLUP) / 32000; 113 | 114 | return result; 115 | } 116 | #endif 117 | 118 | #ifndef USE_AREF 119 | void sensors_setup() 120 | { 121 | // pinMode(INTERNAL_LM60_VS_PIN, OUTPUT); 122 | // pinMode(EXTERNAL_LM60_VS_PIN, OUTPUT); 123 | // pinMode(LM335_VS_PIN, OUTPUT); 124 | // pinMode(LM50_VS_PIN, OUTPUT); 125 | 126 | Wire.begin(); 127 | // bmp085Calibration(); // now done with defines to save RAM 128 | 129 | } 130 | 131 | 132 | int sensors_lm50() 133 | { 134 | analogReference(DEFAULT); // Ref=3.3V. 135 | analogRead(LM50_VOUT_PIN); // Disregard the 1st conversion after changing ref (p.256) 136 | int adc = analogRead(LM50_VOUT_PIN); // Real read 137 | int mV = 3300L * adc / 1024L; // Millivolts 138 | 139 | switch (TEMP_UNIT)//Added by: Kyle Crockett 140 | { 141 | case 1://C 142 | return ((mV - 500) / 10) + CALIBRATION_VAL ; 143 | break; 144 | case 2://K 145 | return ((mV - 500) / 10) + 273 + CALIBRATION_VAL; //C + 273 = K 146 | break; 147 | case 3://F 148 | return (9L * (mV - 500) / 50) + 32+ CALIBRATION_VAL; // (9/5)C + 32 = F 149 | break; 150 | }; 151 | } 152 | 153 | /* 154 | int sensors_lm60(int powerPin, int readPin) 155 | { 156 | digitalWrite(powerPin, HIGH); // Turn the LM60 on 157 | // analogReference(INTERNAL); // Ref=1.1V. Okay up to 108 degC (424 + 6.25*108 = 1100mV) 158 | analogReference(DEFAULT); // Ref=3.3V. 159 | analogRead(readPin); // Disregard the 1st conversion after changing ref (p.256) 160 | int adc = analogRead(readPin); // Real read 161 | digitalWrite(powerPin, LOW); // Turn the LM60 off 162 | // int mV = 1100L * adc / 1024L; // Millivolts 163 | int mV = 3300L * adc / 1024L; // Millivolts 164 | 165 | switch (TEMP_UNIT)//Added by: Kyle Crockett 166 | { 167 | case 1://C 168 | return (4L * (mV - 424) / 25)+ CALIBRATION_VAL ; // Vo(mV) = (6.25*T) + 424 -> T = (Vo - 424) * 100 / 625 169 | break; 170 | case 2://K 171 | return (4L * (mV - 424) / 25) + 273 + CALIBRATION_VAL; //C + 273 = K 172 | break; 173 | case 3://F 174 | return (36L * (mV - 424) / 125) + 32+ CALIBRATION_VAL; // (9/5)C + 32 = F 175 | break; 176 | }; 177 | } 178 | 179 | int sensors_ext_lm60() 180 | { 181 | return sensors_lm60(EXTERNAL_LM60_VS_PIN, EXTERNAL_LM60_VOUT_PIN); 182 | } 183 | 184 | int sensors_int_lm60() 185 | { 186 | return sensors_lm60(INTERNAL_LM60_VS_PIN, INTERNAL_LM60_VOUT_PIN); 187 | } 188 | 189 | int sensors_lm335() 190 | { 191 | digitalWrite(LM335_VS_PIN, HIGH); // Turn LM335 on 192 | analogReference(DEFAULT); 193 | analogRead(LM335_VOUT_PIN); 194 | int adc = analogRead(LM335_VOUT_PIN); 195 | digitalWrite(LM335_VS_PIN, LOW); // Turn LM335 off 196 | int kelvin = adc * 3300L / 10240L; 197 | int celsius = kelvin - 273; 198 | //int farenheit = celsius * 9L / 5L + 32L; 199 | return celsius; 200 | } 201 | */ 202 | 203 | 204 | int getUBatt() 205 | { 206 | analogReference(DEFAULT); 207 | analogRead(0); // Disregard the 1st conversion after changing ref 208 | int adc = analogRead(0); // Real read 209 | // mV = ( Uref * adc / 1024bit resolution ) / Voltage divider ratio 210 | // mV = ( Uref * adc / 1024bit resolution ) / (33 kOhm / (33 kOhm + 100 kOhm)) for PecanPico 211 | 212 | // 213 | // Uref = 3.3 V 214 | long mV = adc * 13300L / 1024L; 215 | 216 | return (int)mV; 217 | } 218 | 219 | /* 220 | int sensors_humidity() 221 | { 222 | // TO DO 223 | return 0; 224 | } 225 | 226 | int sensors_pressure() 227 | { 228 | // TO DO 229 | return 0; 230 | } 231 | 232 | int sensors_uv_ray() 233 | { 234 | // Nice to have at 40 km altitude 235 | return 0; 236 | } 237 | 238 | int sensors_gamma_ray() 239 | { 240 | // http://www.cooking-hacks.com/index.php/documentation/tutorials/geiger-counter-arduino-radiation-sensor-board 241 | return 0; 242 | } 243 | 244 | int sensors_graviton() 245 | { 246 | // Wait, what? 247 | return 0; 248 | } 249 | */ 250 | 251 | // Read 1 byte from the BMP085 at 'address' 252 | char bmp085Read(unsigned char address) 253 | { 254 | unsigned char data; 255 | unsigned int timeout = 2000; 256 | 257 | Wire.beginTransmission(BMP085_ADDRESS); 258 | Wire.write(address); 259 | Wire.endTransmission(); 260 | 261 | Wire.requestFrom(BMP085_ADDRESS, 1); 262 | while(!Wire.available()) 263 | { 264 | timeout--; 265 | if (timeout == 0) 266 | { 267 | timeout_error = 1; 268 | return '0'; 269 | } 270 | } 271 | 272 | 273 | return Wire.read(); 274 | } 275 | 276 | // Read 2 bytes from the BMP085 277 | // First byte will be from 'address' 278 | // Second byte will be from 'address'+1 279 | int bmp085ReadInt(unsigned char address) 280 | { 281 | unsigned char msb, lsb; 282 | unsigned int timeout = 2000; 283 | 284 | Wire.beginTransmission(BMP085_ADDRESS); 285 | Wire.write(address); 286 | Wire.endTransmission(); 287 | 288 | Wire.requestFrom(BMP085_ADDRESS, 2); 289 | while(Wire.available()<2) 290 | { 291 | timeout--; 292 | if (timeout == 0) 293 | { 294 | timeout_error = 1; 295 | return '0'; 296 | } 297 | } 298 | 299 | 300 | msb = Wire.read(); 301 | lsb = Wire.read(); 302 | 303 | return (int) msb<<8 | lsb; 304 | } 305 | 306 | // Stores all of the bmp085's calibration values into global variables 307 | // Calibration values are required to calculate temp and pressure 308 | // This function should be called at the beginning of the program 309 | //void bmp085Calibration() 310 | //{ 311 | // ac1 = bmp085ReadInt(0xAA); 312 | // ac2 = bmp085ReadInt(0xAC); 313 | // ac3 = bmp085ReadInt(0xAE); 314 | // ac4 = bmp085ReadInt(0xB0); 315 | // ac5 = bmp085ReadInt(0xB2); 316 | // ac6 = bmp085ReadInt(0xB4); 317 | // b1 = bmp085ReadInt(0xB6); 318 | // b2 = bmp085ReadInt(0xB8); 319 | // mb = bmp085ReadInt(0xBA); 320 | // mc = bmp085ReadInt(0xBC); 321 | // md = bmp085ReadInt(0xBE); 322 | //} 323 | 324 | // Read the uncompensated temperature value 325 | unsigned int bmp085ReadUT() 326 | { 327 | unsigned int ut; 328 | 329 | // Write 0x2E into Register 0xF4 330 | // This requests a temperature reading 331 | Wire.beginTransmission(BMP085_ADDRESS); 332 | Wire.write(0xF4); 333 | Wire.write(0x2E); 334 | Wire.endTransmission(); 335 | 336 | // Wait at least 4.5ms 337 | delay(5); 338 | 339 | // Read two bytes from registers 0xF6 and 0xF7 340 | ut = bmp085ReadInt(0xF6); 341 | return ut; 342 | } 343 | 344 | // Read the uncompensated pressure value 345 | unsigned long bmp085ReadUP() 346 | { 347 | unsigned char msb, lsb, xlsb; 348 | unsigned long up = 0; 349 | unsigned int timeout = 2000; 350 | 351 | // Write 0x34+(OSS<<6) into register 0xF4 352 | // Request a pressure reading w/ oversampling setting 353 | Wire.beginTransmission(BMP085_ADDRESS); 354 | Wire.write(0xF4); 355 | Wire.write(0x34 + (OSS<<6)); 356 | Wire.endTransmission(); 357 | 358 | // Wait for conversion, delay time dependent on OSS 359 | delay(2 + (3<> (8-OSS); 384 | 385 | return up; 386 | } 387 | 388 | 389 | // Calculate temperature given ut. 390 | // Value returned will be in units of 0.1 deg C 391 | short bmp085GetTemperature(unsigned int ut) 392 | { 393 | long x1, x2; 394 | 395 | x1 = (((long)ut - (long)BMP085_AC6)*(long)BMP085_AC5) >> 15; 396 | x2 = ((long)BMP085_MC << 11)/(x1 + BMP085_MD); 397 | b5 = x1 + x2; 398 | if (timeout_error == 1) 399 | { 400 | timeout_error = 0; 401 | return 0; 402 | } 403 | else 404 | { 405 | return ((b5 + 8)>>4); 406 | } 407 | } 408 | 409 | // Calculate pressure given up 410 | // calibration values must be known 411 | // b5 is also required so bmp085GetTemperature(...) must be called first. 412 | // Value returned will be pressure in units of Pa. 413 | long bmp085GetPressure(unsigned long up) 414 | { 415 | long x1, x2, x3, b3, b6, p; 416 | unsigned long b4, b7; 417 | 418 | b6 = b5 - 4000; 419 | // Calculate B3 420 | x1 = (BMP085_B2 * (b6 * b6)>>12)>>11; 421 | x2 = (BMP085_AC2 * b6)>>11; 422 | x3 = x1 + x2; 423 | b3 = (((((long)BMP085_AC1)*4 + x3)<>2; 424 | 425 | // Calculate B4 426 | x1 = (BMP085_AC3 * b6)>>13; 427 | x2 = (BMP085_B1 * ((b6 * b6)>>12))>>16; 428 | x3 = ((x1 + x2) + 2)>>2; 429 | b4 = (BMP085_AC4 * (unsigned long)(x3 + 32768))>>15; 430 | 431 | b7 = ((unsigned long)(up - b3) * (50000>>OSS)); 432 | if (b7 < 0x80000000) 433 | p = (b7<<1)/b4; 434 | else 435 | p = (b7/b4)<<1; 436 | 437 | x1 = (p>>8) * (p>>8); 438 | x1 = (x1 * 3038)>>16; 439 | x2 = (-7357 * p)>>16; 440 | p += (x1 + x2 + 3791)>>4; 441 | 442 | if (timeout_error == 1) 443 | { 444 | timeout_error = 0; 445 | return 0; 446 | } 447 | else 448 | { 449 | return p; 450 | } 451 | } 452 | 453 | 454 | #endif 455 | -------------------------------------------------------------------------------- /sensors.h: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #ifndef __SENSORS_H__ 19 | #define __SENSORS_H__ 20 | 21 | void sensors_setup(); 22 | unsigned long sensors_aref(); 23 | //long sensors_internal_temp(); 24 | //int sensors_int_lm60(); 25 | //int sensors_ext_lm60(); 26 | //int sensors_lm335(); 27 | int sensors_lm50(); 28 | //void bmp085Calibration(void); 29 | unsigned long bmp085ReadUP(void); 30 | unsigned int bmp085ReadUT(void); 31 | short bmp085GetTemperature(unsigned int ut); 32 | long bmp085GetPressure(unsigned long up); 33 | int getUBatt(); 34 | #endif 35 | -------------------------------------------------------------------------------- /trackuino.h: -------------------------------------------------------------------------------- 1 | /* trackuino copyright (C) 2010 EA5HAV Javi 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of the GNU General Public License 5 | * as published by the Free Software Foundation; either version 2 6 | * of the License, or (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | */ 17 | 18 | #ifndef __TRACKUINO_H__ 19 | #define __TRACKUINO_H__ 20 | 21 | 22 | extern bool newPositionStillUnknown; 23 | 24 | #endif 25 | --------------------------------------------------------------------------------