summaryrefslogtreecommitdiff
path: root/ezdop
diff options
context:
space:
mode:
authorjcorgan2006-08-12 23:25:38 +0000
committerjcorgan2006-08-12 23:25:38 +0000
commit676acd5cc47828bc92c2fad1e474a39aacbd2ba4 (patch)
tree55a35ee87ae50ea6acc113b20ec28045ca0c9b5f /ezdop
parent0b9b3564fd50c630fbbce7a06fda134ae5468828 (diff)
downloadgnuradio-676acd5cc47828bc92c2fad1e474a39aacbd2ba4.tar.gz
gnuradio-676acd5cc47828bc92c2fad1e474a39aacbd2ba4.tar.bz2
gnuradio-676acd5cc47828bc92c2fad1e474a39aacbd2ba4.zip
Merged jcorgan/ezdop into trunk.
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@3264 221aa14e-8319-0410-a670-987f0aec2ac5
Diffstat (limited to 'ezdop')
-rw-r--r--ezdop/AUTHORS1
-rw-r--r--ezdop/ChangeLog0
-rw-r--r--ezdop/Makefile.am26
-rw-r--r--ezdop/README0
-rw-r--r--ezdop/ezdop.pc.in11
-rw-r--r--ezdop/src/Makefile.am22
-rw-r--r--ezdop/src/firmware/Makefile.am60
-rw-r--r--ezdop/src/firmware/dopctrl.c193
-rw-r--r--ezdop/src/firmware/dopctrl.h38
-rw-r--r--ezdop/src/host/Makefile.am22
-rw-r--r--ezdop/src/host/avrdude/Makefile.am29
-rw-r--r--ezdop/src/host/avrdude/README7
-rw-r--r--ezdop/src/host/avrdude/ftbb.c412
-rw-r--r--ezdop/src/host/avrdude/ftbb.h33
-rw-r--r--ezdop/src/host/avrdude/ftbb_ftdi.m456
-rw-r--r--ezdop/src/host/avrdude/patch-avrdude-5.1-ftbb.diff133
-rw-r--r--ezdop/src/host/ezdop/Makefile.am33
-rw-r--r--ezdop/src/host/ezdop/ezdop.cc297
-rw-r--r--ezdop/src/host/ezdop/ezdop.h81
-rw-r--r--ezdop/src/host/hunter/AUTHORS1
-rw-r--r--ezdop/src/host/hunter/ChangeLog0
-rw-r--r--ezdop/src/host/hunter/Makefile.am1
-rw-r--r--ezdop/src/host/hunter/NEWS0
-rw-r--r--ezdop/src/host/hunter/README11
-rwxr-xr-xezdop/src/host/hunter/bootstrap28
-rw-r--r--ezdop/src/host/hunter/config/hunter_ftdi.m418
-rw-r--r--ezdop/src/host/hunter/config/hunter_wx.m411
-rw-r--r--ezdop/src/host/hunter/configure.ac42
-rw-r--r--ezdop/src/host/hunter/src/Makefile.am42
-rw-r--r--ezdop/src/host/hunter/src/calibrate.cc82
-rw-r--r--ezdop/src/host/hunter/src/calibrate.h53
-rw-r--r--ezdop/src/host/hunter/src/doppler.cc517
-rw-r--r--ezdop/src/host/hunter/src/doppler.h130
-rw-r--r--ezdop/src/host/hunter/src/gps.cc247
-rw-r--r--ezdop/src/host/hunter/src/gps.h119
-rw-r--r--ezdop/src/host/hunter/src/histogram.cc170
-rw-r--r--ezdop/src/host/hunter/src/histogram.h89
-rw-r--r--ezdop/src/host/hunter/src/hunter.bmpbin0 -> 3126 bytes
-rw-r--r--ezdop/src/host/hunter/src/hunter.cc1269
-rw-r--r--ezdop/src/host/hunter/src/hunter.h156
-rw-r--r--ezdop/src/host/hunter/src/hunter.xpm39
-rw-r--r--ezdop/src/host/hunter/src/hunter.xrc1020
-rw-r--r--ezdop/src/host/hunter/src/hunterapp.cc52
-rw-r--r--ezdop/src/host/hunter/src/hunterapp.h43
-rw-r--r--ezdop/src/host/hunter/src/known.cc55
-rw-r--r--ezdop/src/host/hunter/src/known.h59
-rw-r--r--ezdop/src/host/hunter/src/sample.cc139
-rw-r--r--ezdop/src/host/hunter/src/sample.h97
-rw-r--r--ezdop/src/host/hunter/src/samplelog.cc107
-rw-r--r--ezdop/src/host/hunter/src/samplelog.h63
-rw-r--r--ezdop/src/host/hunter/src/search.cc211
-rw-r--r--ezdop/src/host/hunter/src/search.h107
-rw-r--r--ezdop/src/host/hunter/src/serial.cc216
-rw-r--r--ezdop/src/host/hunter/src/serial.h56
-rw-r--r--ezdop/src/host/hunter/src/settings.cpp310
-rw-r--r--ezdop/src/host/hunter/src/settings.h101
-rw-r--r--ezdop/src/host/hunter/src/spherical.cc76
-rw-r--r--ezdop/src/host/hunter/src/spherical.h45
-rw-r--r--ezdop/src/host/hunter/src/tactical.cc142
-rw-r--r--ezdop/src/host/hunter/src/tactical.h73
-rw-r--r--ezdop/src/host/hunter/src/util.h79
-rw-r--r--ezdop/src/host/tests/Makefile.am31
-rw-r--r--ezdop/src/host/tests/dopper.cc99
63 files changed, 7660 insertions, 0 deletions
diff --git a/ezdop/AUTHORS b/ezdop/AUTHORS
new file mode 100644
index 000000000..cdb61c9f1
--- /dev/null
+++ b/ezdop/AUTHORS
@@ -0,0 +1 @@
+Johnathan Corgan <jcorgan@aeinet.com>
diff --git a/ezdop/ChangeLog b/ezdop/ChangeLog
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ezdop/ChangeLog
diff --git a/ezdop/Makefile.am b/ezdop/Makefile.am
new file mode 100644
index 000000000..3f6865dbf
--- /dev/null
+++ b/ezdop/Makefile.am
@@ -0,0 +1,26 @@
+# Copyright 2001,2002,2003,2004,2005,2006 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+include $(top_srcdir)/Makefile.common
+
+SUBDIRS = src
+EXTRA_DIST = ezdop.pc.in
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = ezdop.pc
diff --git a/ezdop/README b/ezdop/README
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ezdop/README
diff --git a/ezdop/ezdop.pc.in b/ezdop/ezdop.pc.in
new file mode 100644
index 000000000..fcfe1dd02
--- /dev/null
+++ b/ezdop/ezdop.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: ezdop
+Description: AE6HO EZ-Doppler
+Requires:
+Version: @VERSION@
+Libs: -L${libdir} -lftdi
+Cflags: -I${includedir} @DEFINES@
diff --git a/ezdop/src/Makefile.am b/ezdop/src/Makefile.am
new file mode 100644
index 000000000..caca90edb
--- /dev/null
+++ b/ezdop/src/Makefile.am
@@ -0,0 +1,22 @@
+# Copyright 2001,2002,2003,2004,2005,2006 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+include $(top_srcdir)/Makefile.common
+
+SUBDIRS = firmware host
diff --git a/ezdop/src/firmware/Makefile.am b/ezdop/src/firmware/Makefile.am
new file mode 100644
index 000000000..6cb674811
--- /dev/null
+++ b/ezdop/src/firmware/Makefile.am
@@ -0,0 +1,60 @@
+# Copyright 2001,2002,2003,2004,2005,2006 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+include $(top_srcdir)/Makefile.common
+
+datadir = $(prefix)/share/ezdop
+
+# Use avr-gcc versions of compiler and binutils
+CC=$(AVRGCC)
+OBJCOPY=$(AVROBJCOPY)
+
+# What's defined for rest of project not good for avr-gcc (no -pthread for instance)
+override CFLAGS=
+
+noinst_PROGRAMS = \
+ dopctrl.elf \
+ dopctrl.hex
+
+EXTRA_DIST = \
+ dopctrl.hex
+
+dopctrl_elf_CFLAGS=-mmcu=atmega8 -funsigned-char -funsigned-bitfields \
+ -fpack-struct -fshort-enums -Wall -Wstrict-prototypes \
+ -Wa,-adhlns=$(<:.c=.lst)
+
+dopctrl_elf_SOURCES = \
+ dopctrl.c
+
+#dopctrl_elfdir = $(includedir)
+
+include_HEADERS = \
+ dopctrl.h
+
+%.hex: %.elf
+ $(OBJCOPY) -O ihex -R .eeprom $< $@
+
+dopctrl.hex : dopctrl.elf
+
+install-data-local:
+ $(INSTALL_DATA) -D $(top_builddir)/ezdop/src/firmware/dopctrl.hex $(DESTDIR)$(datadir)/dopctrl.hex
+
+uninstall-local:
+ $(RM) $(DESTDIR)$(datadir)/dopctrl.hex
+ \ No newline at end of file
diff --git a/ezdop/src/firmware/dopctrl.c b/ezdop/src/firmware/dopctrl.c
new file mode 100644
index 000000000..f499d6c35
--- /dev/null
+++ b/ezdop/src/firmware/dopctrl.c
@@ -0,0 +1,193 @@
+/*
+ * AE6HO EZ-Doppler firmware for onboard ATmega8 microcontroller
+ * Copyright 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * ARCHITECTURE
+ *
+ * Timer0 is 8000 Hz time base
+ * PORTD.2 through PORTD.5 are doppler antenna array element enables.
+ * PORTB.0 is a diagnostic LED, set low to light up
+ * ADC7 is audio input
+ * ADC6 is RSSI input - not yet used
+ *
+ */
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/signal.h>
+#include "dopctrl.h"
+
+#define LED 0
+#define turn_off_led() PORTB |= _BV(LED);
+#define turn_on_led() PORTB &= ~_BV(LED);
+
+#define ANT1 _BV(2)
+#define ANT2 _BV(3)
+#define ANT3 _BV(4)
+#define ANT4 _BV(5)
+#define ANTMASK ANT1|ANT2|ANT3|ANT4
+
+#define ADCAIN 7
+#define BAUDRATE 250000
+
+/* Assume these are all set to zero in startup code */
+
+uint8_t rotate; /* Flag to indicate antennas should rotate or not */
+uint8_t streaming; /* Flag to indicate continuous sampling */
+uint8_t antennas; /* Holds shadow copy of PORTD antennas */
+uint8_t speed; /* Holds samples per phase increment */
+uint8_t phase; /* Holds rotation phase (measured in samples */
+
+uint8_t audio_hi; /* High byte of ADC sample of audio */
+uint8_t audio_lo; /* Low byte of ADC sample of audio */
+
+uint8_t rx; /* Temporary holds received byte from USART */
+uint8_t command; /* Temporary to hold command when getting operand */
+uint8_t cmdbyte; /* Tracks bytes received in multi-byte commands */
+
+int main(void)
+{
+ /* Diagnostic port setup */
+ DDRB = _BV(LED); /* PB0 is output */
+ turn_off_led();
+
+ /* Antenna control port setup */
+ speed = 4; /* Todo: read from EEPROM */
+ antennas = ANT1; /* Start with antenna #1 */
+ PORTD = antennas; /* Set port value */
+ DDRD = ANTMASK; /* Set antenna enables as PORTD outputs */
+
+ /* ADC port setup */
+ ADMUX = _BV(REFS0)|ADCAIN; /* AVCC is reference, use ADC for audio input (ADC7) */
+ ADCSRA = _BV(ADEN)|_BV(ADIE)|0x07; /* Enable converter, prescale by 128, enable ADC interrupt */
+
+ /* USART port setup*/
+ UCSRA = 0; /* Normal asynchronous mode */
+ UCSRB = _BV(TXEN)|_BV(RXEN)|_BV(RXCIE); /* Enable transmitter and receiver, and receiver interrupts */
+ UCSRC = _BV(URSEL)|_BV(UCSZ1)|_BV(UCSZ0); /* 8N1 format */
+ UBRRH = 0; /* Set baud rate prescaler to 3 */
+ UBRRL = 3; /* To get 250000 bps */
+
+ /* Set up 8000 Hz time base */
+ timer_enable_int(_BV(TOIE0)); /* Turn on Timer0 output overflow interrupt */
+ TCCR0 = _BV(CS01); /* Clock Timer0 from CLK/8 */
+
+ sei(); /* Let 'er rip! */
+ return 0;
+}
+
+/* Timer0 overflow interrupt handler
+ *
+ * Creates 8000 Hz time base, or 125us budget
+ *
+ */
+SIGNAL(SIG_OVERFLOW0)
+{
+ /* Reload Timer0 samples to 8, results in 8000 Hz overflow interrupt */
+ TCNT0 = 0x08;
+
+ if (streaming) {
+ /* Kick-off an audio sample conversion, will interrupt 104us later */
+ ADCSRA |= _BV(ADSC);
+
+ /* Write the first byte of previous sample and enable UDRIE */
+ UDR = audio_lo;
+ UCSRB |= _BV(UDRIE);
+ }
+
+ if (!rotate) /* Skip rotating antenna if not started */
+ return;
+
+ /* Increment antenna phase and see if antenna need to be rotated */
+ if (++phase == speed) {
+ phase = 0;
+
+ /* Sequence antenna array elements */
+ antennas >>= 1;
+ antennas &= ANTMASK;
+
+ if (!antennas)
+ antennas = ANT4;
+ PORTD = antennas;
+ }
+}
+
+/* ADC conversion complete interrupt handler
+ *
+ * Read value and store. Assume prior sample has been handled.
+ *
+ */
+SIGNAL(SIG_ADC)
+{
+ audio_lo = ADCL;
+ audio_hi = ADCH;
+}
+
+/* USART data transmit holding register empty interrupt handler
+ *
+ * First byte is always sent from timer interrupt
+ * So second byte gets sent here with UDRIE disabled
+ *
+ */
+SIGNAL(SIG_UART_DATA)
+{
+ /* Write second byte of previous sample and disable UDRIE */
+ UDR = audio_hi | (antennas << 2);
+ UCSRB &= ~_BV(UDRIE);
+}
+
+/* USART receive complete interrupt handler
+ *
+ * Received bytes are commands, with one or two bytes of operands following
+ *
+ */
+SIGNAL(SIG_UART_RECV)
+{
+ rx = UDR;
+
+ if (cmdbyte == 0) {
+ if (rx == EZDOP_CMD_ROTATE) /* Start rotation */
+ rotate = 1;
+ else if (rx == EZDOP_CMD_STOP) /* Stop rotation */
+ rotate = 0;
+ else if (rx == EZDOP_CMD_RATE) { /* Set rotation rate */
+ command = rx;
+ cmdbyte = 1;
+ }
+ else if (rx == EZDOP_CMD_STREAM) /* Stream audio samples */
+ streaming = 1;
+ else if (rx == EZDOP_CMD_STROFF) /* Stop streaming */
+ streaming = 0;
+ else
+ turn_on_led(); /* Unknown command */
+ }
+ else if (cmdbyte == 1) {
+ if (command == EZDOP_CMD_RATE) { /* Operand is number of samples per phase increment */
+ speed = rx;
+ cmdbyte = 0;
+ }
+ else
+ turn_on_led(); /* Bogus command state */
+ }
+ else
+ turn_on_led(); /* Bogus command state */
+}
diff --git a/ezdop/src/firmware/dopctrl.h b/ezdop/src/firmware/dopctrl.h
new file mode 100644
index 000000000..b689990dc
--- /dev/null
+++ b/ezdop/src/firmware/dopctrl.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+// Opcodes for commands to AVR
+#define EZDOP_CMD_ROTATE 0x00 /* No operand */
+#define EZDOP_CMD_STOP 0x01 /* No operand */
+#define EZDOP_CMD_RATE 0x02 /* One byte operand */
+#define EZDOP_CMD_STREAM 0x03 /* No operand */
+#define EZDOP_CMD_STROFF 0x04 /* No operand */
+
+#define EZDOP_DEFAULT_RATE 4
+
+// TODO: Update with actual id's once established for product
+// These are for FTDI unprogrammed parts
+#define EZDOP_VENDORID 0x0403
+#define EZDOP_PRODUCTID 0x6001
+
+// These determine the FTDI bitbang settings
+#define EZDOP_BAUDRATE 250000 // Fixed in EZ Doppler hardware design
+#define EZDOP_BBDIR 0x16 // Bits 4=RESET 2=SCK 1=MOSI 0=MISO, MISO is input
diff --git a/ezdop/src/host/Makefile.am b/ezdop/src/host/Makefile.am
new file mode 100644
index 000000000..f7efaf7bf
--- /dev/null
+++ b/ezdop/src/host/Makefile.am
@@ -0,0 +1,22 @@
+# Copyright 2001,2002,2003,2004,2005,2006 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+include $(top_srcdir)/Makefile.common
+
+SUBDIRS = avrdude ezdop tests
diff --git a/ezdop/src/host/avrdude/Makefile.am b/ezdop/src/host/avrdude/Makefile.am
new file mode 100644
index 000000000..962876da7
--- /dev/null
+++ b/ezdop/src/host/avrdude/Makefile.am
@@ -0,0 +1,29 @@
+# Copyright 2001,2002,2003,2004,2005,2006 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+include $(top_srcdir)/Makefile.common
+
+SUBDIRS =
+
+EXTRA_DIST = \
+ README \
+ ftbb.h \
+ ftbb.c \
+ ftbb_ftdi.m4 \
+ patch-avrdude-5.1-ftbb.diff
diff --git a/ezdop/src/host/avrdude/README b/ezdop/src/host/avrdude/README
new file mode 100644
index 000000000..2eb37af5a
--- /dev/null
+++ b/ezdop/src/host/avrdude/README
@@ -0,0 +1,7 @@
+These files add bitbang over FTDI USB controller as a programming option
+to the avrdude Atmel AVR programmer. This is used to program the firmware
+of the EZDOP microcontroller over the USB port.
+
+AVRDUDE is a free program available at:
+
+http://savannah.nongnu.org/projects/avrdude/
diff --git a/ezdop/src/host/avrdude/ftbb.c b/ezdop/src/host/avrdude/ftbb.c
new file mode 100644
index 000000000..53b5794ca
--- /dev/null
+++ b/ezdop/src/host/avrdude/ftbb.c
@@ -0,0 +1,412 @@
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2003-2004 Theodore A. Roth <troth@openavr.org>
+ * Copyright (C) 2005 Johnathan Corgan <jcorgan@aeinet.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * avrdude interface for serial programming via FTDI bit bang mode operation.
+ */
+
+#include "ac_cfg.h"
+#include "avr.h"
+#include "pgm.h"
+#include "ftbb.h"
+#include <string.h>
+
+#if FTDI_SUPPORT
+#define FTBB_DEBUG 0
+
+#if HAVE_LIBFTD2XX
+#include <ftd2xx.h>
+#elif HAVE_FTDI_H
+#include <ftdi.h>
+#endif
+
+#define RESET (1<<(pgm->pinno[PIN_AVR_RESET]-1))
+#define SCK (1<<(pgm->pinno[PIN_AVR_SCK]-1))
+#define MOSI (1<<(pgm->pinno[PIN_AVR_MOSI]-1))
+#define MISO (1<<(pgm->pinno[PIN_AVR_MISO]-1))
+#define FTDDR (MOSI|SCK|RESET)
+
+#if HAVE_LIBFTD2XX
+static FT_STATUS status;
+static FT_HANDLE handle;
+#elif HAVE_FTDI_H
+static struct ftdi_context device;
+static int status;
+#endif
+
+static unsigned char txbits;
+
+/* Send 8 bits over SPI bus with per-bit read back
+ *
+ */
+static unsigned char ftbb_txrx(PROGRAMMER *pgm, unsigned char val)
+{
+ int i;
+ unsigned char bits;
+ unsigned char res;
+
+#if HAVE_LIBFTD2XX
+ DWORD written;
+#endif /* HAVE_LIBFTD2XX */
+
+ res = 0;
+ bits = 0;
+ for (i = 0; i < 8; i++) { // For each bit
+ // Set up data on low phase of SCK
+ txbits &= ~SCK;
+
+ // Set MOSI to high bit of transmit data
+ if (val & 0x80)
+ txbits |= MOSI;
+ else
+ txbits &= ~MOSI;
+
+#if HAVE_LIBFTD2XX
+ FT_Write(handle, &txbits, 1, &written);
+ // Clock just fell, read previous input bit and shift in
+ FT_GetBitMode(handle, &bits);
+#elif HAVE_FTDI_H
+ ftdi_write_data(&device, &txbits, 1);
+ // Clock just fell, read previous input bit and shift in
+ ftdi_read_pins(&device, &bits);
+#endif /* HAVE_LIBFTD2XX */
+ res = (res << 1) | (bits & MISO);
+
+ // Now raise SCK to latch data in AVR
+ txbits |= SCK;
+
+#if HAVE_LIBFTD2XX
+ FT_Write(handle, &txbits, 1, &written);
+#elif HAVE_FTDI_H
+ ftdi_write_data(&device, &txbits, 1);
+#endif /* HAVE_LIBFTD2XX */
+ // Move on to next bit in transmit data
+ val = val << 1;
+ }
+
+ return res;
+}
+
+/* Generic programmer command function for pgm->cmd
+ *
+ * Sends four bytes of command in sequence and collects responses
+ *
+ */
+static int ftbb_cmd(PROGRAMMER *pgm, unsigned char cmd[4], unsigned char res[4])
+{
+ int i;
+
+#if FTBB_DEBUG
+ printf("CMD: %02X%02X%02X%02X ", cmd[0], cmd[1], cmd[2], cmd[3]);
+#endif
+
+ for (i = 0; i < 4; i++) {
+ res[i] = ftbb_txrx(pgm, cmd[i]);
+ }
+
+#if FTBB_DEBUG
+ printf("RES: %02X%02X%02X%02X\n", res[0], res[1], res[2], res[3]);
+#endif
+
+ return 0;
+}
+
+/* Programmer initialization command for pgm->initialize
+ *
+ * Pulse RESET with SCK low, then send SPI start programming command
+ *
+ */
+static int ftbb_initialize(PROGRAMMER *pgm, AVRPART *p)
+{
+#if HAVE_LIBFTD2XX
+ DWORD written;
+#endif /* HAVE_LIBFTD2XX */
+
+ // Atmel documentation says to raise RESET for 2 cpu clocks while sclk is low
+ // then lower RESET and wait 20 ms.
+ txbits |= RESET;
+
+#if HAVE_LIBFTD2XX
+ FT_Write(handle, &txbits, 1, &written);
+#elif HAVE_FTDI_H
+ ftdi_write_data(&device, &txbits, 1);
+#endif /* HAVE_LIBFTD2XX */
+
+ usleep(1000); // 2 AVR cpu clocks at any realistic clock rate
+ txbits &= ~RESET;
+
+#if HAVE_LIBFTD2XX
+ FT_Write(handle, &txbits, 1, &written);
+#elif HAVE_FTDI_H
+ ftdi_write_data(&device, &txbits, 1);
+#endif /* HAVE_LIBFTD2XX */
+
+ usleep(20000);
+
+ pgm->program_enable(pgm, p);
+ return 0;
+}
+
+/* Programmer status display for pgm->display
+ *
+ * Not-implemented
+ *
+ */
+static void ftbb_display(PROGRAMMER *pgm, char *p)
+{
+#if FTBB_DEBUG
+ printf("ftbb: display called with: %s\n", p);
+#endif
+
+ printf("FTBB: RESET mapped to pinno %d\n", pgm->pinno[PIN_AVR_RESET]);
+ printf("FTBB: SCK mapped to pinno %d\n", pgm->pinno[PIN_AVR_SCK]);
+ printf("FTBB: MOSI mapped to pinno %d\n", pgm->pinno[PIN_AVR_MOSI]);
+ printf("FTBB: MISO mapped to pinno %d\n", pgm->pinno[PIN_AVR_MISO]);
+}
+
+/* Programmer enable command for pgm->enable
+ *
+ * Lowers SCK and RESET in preparation for serial programming
+ *
+ */
+static void ftbb_enable(PROGRAMMER *pgm)
+{
+#if HAVE_LIBFTD2XX
+ DWORD written;
+#endif /* HAVE_LIBFTD2XX */
+
+ // Lower SCK & RESET
+ txbits &= ~SCK;
+ txbits &= ~RESET;
+
+#if HAVE_LIBFTD2XX
+ FT_Write(handle, &txbits, 1, &written);
+#elif HAVE_FTDI_H
+ ftdi_write_data(&device, &txbits, 1);
+#endif /* HAVE_LIBFTD2XX */
+}
+
+/* Programmer disable command for pgm->disable
+ *
+ * Raises RESET to return to normal chip operation
+ *
+ */
+static void ftbb_disable(PROGRAMMER *pgm)
+{
+#if HAVE_LIBFTD2XX
+ DWORD written;
+#endif /* HAVE_LIBFTD2XX */
+ // Raise RESET to return to normal mode
+ txbits |= RESET;
+
+#if HAVE_LIBFTD2XX
+ FT_Write(handle, &txbits, 1, &written);
+#elif HAVE_FTDI_H
+ ftdi_write_data(&device, &txbits, 1);
+#endif /* HAVE_LIBFTD2XX */
+}
+
+/* Programmer programming mode enable function for pgm->program_enable
+ *
+ * Starts SPI programming mode
+ *
+ */
+static int ftbb_program_enable(PROGRAMMER *pgm, AVRPART *p)
+{
+ unsigned char cmd[4];
+ unsigned char res[4];
+
+ if (p->op[AVR_OP_PGM_ENABLE] == NULL) {
+ fprintf(stderr, "program enable instruction not defined for part \"%s\"\n", p->desc);
+ return -1;
+ }
+
+ memset(cmd, 0, sizeof(cmd));
+ avr_set_bits(p->op[AVR_OP_PGM_ENABLE], cmd);
+ pgm->cmd(pgm, cmd, res);
+
+ return 0;
+}
+
+/* Progammer erase function for pgm->erase
+ *
+ * Sends chip erase command and sleeps the chip erase delay
+ *
+ */
+static int ftbb_chip_erase(PROGRAMMER *pgm, AVRPART *p)
+{
+ unsigned char cmd[4];
+ unsigned char res[4];
+
+ if (p->op[AVR_OP_CHIP_ERASE] == NULL) {
+ fprintf(stderr, "chip erase instruction not defined for part \"%s\"\n", p->desc);
+ return -1;
+ }
+
+ memset(cmd, 0, sizeof(cmd));
+ avr_set_bits(p->op[AVR_OP_CHIP_ERASE], cmd);
+ pgm->cmd(pgm, cmd, res);
+ usleep(p->chip_erase_delay);
+
+ return 0;
+}
+
+
+/* Parse routine for device name
+ *
+ * FFFF:FFFF:0
+ *
+ */
+int ftbb_parse_name(char *name, int *vid, int *pid, int *ifc)
+{
+ printf("name=%s\n", name);
+ return 0;
+}
+
+/* Programmer open command for pgm->open
+ *
+ * Opens FTD2XX device specified by 'name', performs a chip reset, then
+ * sets the baudrate and bit bang mode
+ *
+ */
+static int ftbb_open(PROGRAMMER *pgm, char *name)
+{
+ int vid, pid, ifc;
+ ftbb_parse_name(name, &vid, &pid, &ifc);
+
+#if HAVE_LIBFTD2XX
+ int devnum = 0;
+ if (strcmp(name, "ft0")) {
+ fprintf(stderr, "ERROR: FTD2XX device selection not yet implemented!\n");
+ return -1;
+ }
+
+ // Call FTD2XX library to open device
+ if ((status = FT_Open(devnum, &handle)) != FT_OK) {
+ fprintf(stderr, "Failed to open FTD2XX device #%d.\n", devnum);
+ return -1;
+ }
+
+ // Reset chipset
+ if ((status = FT_ResetDevice(handle)) != FT_OK) {
+ fprintf(stderr, "Failed to reset chipset for FTD2XX device #%d.\n", devnum);
+ return -1;
+ }
+
+ // Set baud rate for bit bang interface
+ if ((status = FT_SetBaudRate(handle, pgm->baudrate)) != FT_OK) {
+ fprintf(stderr, "Failed to set baud rate for FTD2XX device #%d.\n", devnum);
+ return -1;
+ }
+
+ // Set bit bang direction and mode
+ if ((status = FT_SetBitMode(handle, FTDDR, 1)) != FT_OK) {
+ fprintf(stderr, "Failed to set bit bang mode for FTD2XX device #%d.\n", devnum);
+ return -1;
+ }
+#elif HAVE_FTDI_H
+ // Open device via FTDI library *** FIXME *** hardcoded VID and PID
+ if (ftdi_usb_open(&device, EZDOP_VENDORID, EZDOP_PRODUCTID)) {
+ fprintf(stderr, "ftdi_usb_open: %s", device.error_str);
+ return -1;
+ }
+
+ // Reset FTDI chipset
+ if (ftdi_usb_reset(&device)) {
+ fprintf(stderr, "ftdi_usb_reset: %s", device.error_str);
+ return -1;
+ }
+
+ // Set FTDI chipset baudrate for bitbang
+ if (ftdi_set_baudrate(&device, pgm->baudrate)) {
+ fprintf(stderr, "ftdi_set_baudrate: %s", device.error_str);
+ return -1;
+ }
+
+ // Enable bitbang
+ if (ftdi_enable_bitbang(&device, FTDDR)) {
+ fprintf(stderr, "ftdi_enable_bitbang: %s", device.error_str);
+ return -1;
+ }
+
+ // Minimum chunk size for reads to reduce latency
+ if (ftdi_read_data_set_chunksize(&device, 256)) {
+ fprintf(stderr, "ftdi_read_data_set_chunksize: %s", device.error_str);
+ return -1;
+ }
+#endif /* HAVE_LIBFTD2XX */
+
+ return 0;
+}
+
+/* Programmer close function for pgm->close
+ *
+ * Releases FTD2XX device
+ *
+ */
+static void ftbb_close(PROGRAMMER *pgm)
+{
+#if HAVE_LIBFTD2XX
+ FT_Close(handle);
+#elif HAVE_FTDI_H
+ ftdi_deinit(&device);
+#endif
+}
+#endif /* FTDI_SUPPORT */
+
+/* Programmer initialization command for startup.
+ *
+ * Sets appropriate function pointers in structure
+ * Tests FTD2XX interface by enumerating number of devices
+ *
+ */
+void ftbb_initpgm (PROGRAMMER *pgm)
+{
+#if FTDI_SUPPORT
+
+#if HAVE_LIBFTD2XX
+ DWORD num_devices;
+#endif
+
+ strcpy(pgm->type, "ftbb");
+ pgm->initialize = ftbb_initialize;
+ pgm->display = ftbb_display;
+ pgm->enable = ftbb_enable;
+ pgm->disable = ftbb_disable;
+ pgm->program_enable = ftbb_program_enable;
+ pgm->chip_erase = ftbb_chip_erase;
+ pgm->cmd = ftbb_cmd;
+ pgm->open = ftbb_open;
+ pgm->close = ftbb_close;
+
+#if HAVE_LIBFTD2XX
+ if ((status = FT_ListDevices(&num_devices, NULL, FT_LIST_NUMBER_ONLY)) != FT_OK)
+ fprintf(stderr, "Failed to initialize FTD2XX interface. (%li)\n", status);
+ else
+ printf("%lu FTD2XX device(s) found.\n", num_devices);
+#elif HAVE_FTDI_H
+ if ((status = ftdi_init(&device)) < 0)
+ fprintf(stderr, "Failed to initialize FTDI interface. (%i)\n", status);
+#endif
+
+ txbits = RESET;
+#endif /* FTDI_SUPPORT */
+}
+
diff --git a/ezdop/src/host/avrdude/ftbb.h b/ezdop/src/host/avrdude/ftbb.h
new file mode 100644
index 000000000..d51e1a954
--- /dev/null
+++ b/ezdop/src/host/avrdude/ftbb.h
@@ -0,0 +1,33 @@
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2003-2004 Theodore A. Roth <troth@openavr.org>
+ * Copyright (C) 2005 Johnathan Corgan <jcorgan@aeinet.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ftbb_h__
+#define __ftbb_h__
+
+#include "avrpart.h"
+
+void ftbb_initpgm (PROGRAMMER *pgm);
+
+// TODO: Update with actual id's once established for product
+// These are for FTDI unprogrammed parts
+#define EZDOP_VENDORID 0x0403
+#define EZDOP_PRODUCTID 0x6001
+
+#endif /* __ftbb_h__ */
diff --git a/ezdop/src/host/avrdude/ftbb_ftdi.m4 b/ezdop/src/host/avrdude/ftbb_ftdi.m4
new file mode 100644
index 000000000..ce020868b
--- /dev/null
+++ b/ezdop/src/host/avrdude/ftbb_ftdi.m4
@@ -0,0 +1,56 @@
+#
+# avrdude - A Downloader/Uploader for AVR device programmers
+# Copyright (C) 2003-2004 Theodore A. Roth <troth@openavr.org>
+# Copyright (C) 2005 Johnathan Corgan <jcorgan@aeinet.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+AC_DEFUN([FTBB_FTDI],
+[
+ AC_ARG_ENABLE(
+ [ftdi-support],
+ AC_HELP_STRING(
+ [--enable-ftdi-support],
+ [support serial programming via FTDI bit-bang mode (default is no)]),
+ [case "${enableval}" in
+ yes) ftdi_support=yes ;;
+ no) ftdi_support=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for ftdi-support option) ;;
+ esac],
+ [ftdi_support=no])
+ AM_CONDITIONAL(FTDI_SUPPORT, [test "$ftdi_support" = "yes"])
+
+ if test "$ftdi_support" = "yes"; then
+ AC_REQUIRE([AC_CANONICAL_HOST])
+ AC_LANG_PUSH(C)
+
+ AC_CHECK_HEADERS([ftdi.h],
+ [],
+ [ AC_MSG_ERROR([FTDI support requires libftdi. ftdi.h not found, stop.]) ]
+ )
+
+ save_LIBS="$LIBS"
+ AC_SEARCH_LIBS(ftdi_init, [ftdi],
+ [ FTDILIB="$LIBS" ],
+ [ AC_MSG_ERROR([FTDI support requires libftdi. ftdi_init not found, stop.]) ]
+ )
+ LIBS="$save_LIBS"
+
+ AC_LANG_POP
+ AC_SUBST(FTDILIB)
+
+ AC_DEFINE(FTDI_SUPPORT,[1],[Set to 1 to compile in support for FTDI bit-bang mode programming.])
+ fi
+])
diff --git a/ezdop/src/host/avrdude/patch-avrdude-5.1-ftbb.diff b/ezdop/src/host/avrdude/patch-avrdude-5.1-ftbb.diff
new file mode 100644
index 000000000..05836df20
--- /dev/null
+++ b/ezdop/src/host/avrdude/patch-avrdude-5.1-ftbb.diff
@@ -0,0 +1,133 @@
+Index: Makefile.am
+===================================================================
+RCS file: /sources/avrdude/avrdude/Makefile.am,v
+retrieving revision 1.29
+diff -u -r1.29 Makefile.am
+--- Makefile.am 12 Jan 2006 23:13:50 -0000 1.29
++++ Makefile.am 7 Jul 2006 03:57:10 -0000
+@@ -71,6 +71,8 @@
+ fileio.c \
+ fileio.h \
+ freebsd_ppi.h \
++ ftbb.c \
++ ftbb.h \
+ jtagmkI.c \
+ jtagmkI.h \
+ jtagmkI_private.h \
+@@ -109,6 +111,8 @@
+ usbdevs.h \
+ usb_libusb.c
+
++avrdude_LDADD = $(FTDILIB)
++
+ man_MANS = avrdude.1
+
+ sysconf_DATA = avrdude.conf
+Index: avrdude.conf.in
+===================================================================
+RCS file: /sources/avrdude/avrdude/avrdude.conf.in,v
+retrieving revision 1.79
+diff -u -r1.79 avrdude.conf.in
+--- avrdude.conf.in 23 May 2006 22:27:43 -0000 1.79
++++ avrdude.conf.in 7 Jul 2006 03:57:10 -0000
+@@ -223,6 +223,17 @@
+ #
+
+ programmer
++ id = "ezdop";
++ desc = "AE6HO EZ-Doppler";
++ type = ftbb;
++ baudrate = 230400;
++ reset = 5;
++ sck = 3;
++ mosi = 2;
++ miso = 1;
++;
++
++programmer
+ id = "avrisp";
+ desc = "Atmel AVR ISP";
+ type = stk500;
+Index: bootstrap
+===================================================================
+RCS file: /sources/avrdude/avrdude/bootstrap,v
+retrieving revision 1.10
+diff -u -r1.10 bootstrap
+--- bootstrap 14 May 2005 08:06:18 -0000 1.10
++++ bootstrap 7 Jul 2006 03:57:10 -0000
+@@ -40,7 +40,7 @@
+
+ rm -rf autom4te.cache
+
+-${ACLOCAL}
++${ACLOCAL} -I .
+ ${AUTOHEADER}
+ ${AUTOCONF}
+ ${AUTOMAKE} -a -c
+Index: config_gram.y
+===================================================================
+RCS file: /sources/avrdude/avrdude/config_gram.y,v
+retrieving revision 1.45
+diff -u -r1.45 config_gram.y
+--- config_gram.y 23 May 2006 22:27:43 -0000 1.45
++++ config_gram.y 7 Jul 2006 03:57:10 -0000
+@@ -40,6 +40,7 @@
+ #include "avr.h"
+ #include "jtagmkI.h"
+ #include "jtagmkII.h"
++#include "ftbb.h"
+
+ #if defined(WIN32NATIVE)
+ #define strtok_r( _s, _sep, _lasts ) \
+@@ -123,6 +124,7 @@
+ %token K_STK500V2
+ %token K_AVR910
+ %token K_BUTTERFLY
++%token K_FTBB
+ %token K_TYPE
+ %token K_VCC
+ %token K_VFYLED
+@@ -374,6 +376,12 @@
+ }
+ } |
+
++ K_TYPE TKN_EQUAL K_FTBB {
++ {
++ ftbb_initpgm(current_prog);
++ }
++ } |
++
+ K_DESC TKN_EQUAL TKN_STRING {
+ strncpy(current_prog->desc, $3->value.string, PGM_DESCLEN);
+ current_prog->desc[PGM_DESCLEN-1] = 0;
+Index: configure.ac
+===================================================================
+RCS file: /sources/avrdude/avrdude/configure.ac,v
+retrieving revision 1.33
+diff -u -r1.33 configure.ac
+--- configure.ac 23 Jan 2006 21:04:13 -0000 1.33
++++ configure.ac 7 Jul 2006 03:57:10 -0000
+@@ -115,6 +115,8 @@
+ DIST_SUBDIRS_AC='windows'
+ fi
+
++FTBB_FTDI
++
+ AC_SUBST(DOC_INST_DIR, $DOC_INST_DIR)
+ AC_SUBST(SUBDIRS_AC, $SUBDIRS_AC)
+ AC_SUBST(DIST_SUBDIRS_AC, $DIST_SUBDIRS_AC)
+Index: lexer.l
+===================================================================
+RCS file: /sources/avrdude/avrdude/lexer.l,v
+retrieving revision 1.38
+diff -u -r1.38 lexer.l
+--- lexer.l 23 May 2006 22:27:43 -0000 1.38
++++ lexer.l 7 Jul 2006 03:57:11 -0000
+@@ -138,6 +138,7 @@
+ enablepageprogramming { yylval=NULL; return K_ENABLEPAGEPROGRAMMING; }
+ errled { yylval=NULL; return K_ERRLED; }
+ flash { yylval=NULL; return K_FLASH; }
++ftbb { yylval=NULL; return K_FTBB; }
+ has_jtag { yylval=NULL; return K_HAS_JTAG; }
+ id { yylval=NULL; return K_ID; }
+ idr { yylval=NULL; return K_IDR; }
diff --git a/ezdop/src/host/ezdop/Makefile.am b/ezdop/src/host/ezdop/Makefile.am
new file mode 100644
index 000000000..5f47e43b1
--- /dev/null
+++ b/ezdop/src/host/ezdop/Makefile.am
@@ -0,0 +1,33 @@
+# Copyright 2001,2002,2003,2004,2005,2006 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+include $(top_srcdir)/Makefile.common
+
+INCLUDES=$(EZDOP_INCLUDES)
+
+lib_LTLIBRARIES = \
+ libezdop.la
+libezdop_la_LIBADD = \
+ $(FTDI_LIBS)
+
+libezdop_la_SOURCES = \
+ ezdop.cc
+
+include_HEADERS = \
+ ezdop.h \ No newline at end of file
diff --git a/ezdop/src/host/ezdop/ezdop.cc b/ezdop/src/host/ezdop/ezdop.cc
new file mode 100644
index 000000000..43a70dacb
--- /dev/null
+++ b/ezdop/src/host/ezdop/ezdop.cc
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+// Application specific includes
+#include "ezdop.h"
+
+// System includes (FIXME: autoconf these)
+#include <cassert>
+#include <cstdio>
+#include <unistd.h>
+
+ezdop::ezdop()
+{
+ d_device = new struct ftdi_context;
+ if (ftdi_init(d_device))
+ fprintf(stderr, "ftdi_init: %s", d_device->error_str);
+ d_online = false;
+ d_rotating = false;
+ d_rate = EZDOP_DEFAULT_RATE;
+ d_state = ST_HI;
+ d_ant = 8; // FIXME: get from controller.h
+ d_seq = 0;
+ d_val = 0;
+ d_in_phase = 0;
+ d_quadrature = 0;
+ d_rotating = false;
+}
+
+ezdop::~ezdop()
+{
+ assert(d_device);
+ delete d_device;
+}
+
+bool ezdop::init()
+{
+ assert(d_device);
+
+ d_online = false;
+
+ // Attaches to first found device matching ID strings
+ if (ftdi_usb_open(d_device, EZDOP_VENDORID, EZDOP_PRODUCTID)) {
+ fprintf(stderr, "ftdi_usb_open: %s", d_device->error_str);
+ return false;
+ }
+
+ d_online = true;
+ reset();
+
+ return d_online;
+}
+
+bool ezdop::finish()
+{
+ assert(d_device);
+
+ if (!d_online)
+ return true;
+
+ if (d_rotating)
+ stop_rotating();
+
+ if (ftdi_usb_close(d_device)) {
+ fprintf(stderr, "ftdi_usb_close: %s", d_device->error_str);
+ return false;
+ }
+
+ d_online = false;
+ return true;
+}
+
+bool ezdop::reset()
+{
+ assert(d_device);
+ assert(d_online);
+
+ // Reset FTDI chipset
+ if (ftdi_usb_reset(d_device)) {
+ fprintf(stderr, "ftdi_usb_reset: %s", d_device->error_str);
+ return false;
+ }
+
+ // Set FTDI chipset baudrate for bitbang
+ if (ftdi_set_baudrate(d_device, EZDOP_BAUDRATE)) {
+ fprintf(stderr, "ftdi_set_baudrate: %s", d_device->error_str);
+ return false;
+ }
+
+ // Toggle DTR (-->AVR RESET)
+ // Enable bitbang
+ if (ftdi_enable_bitbang(d_device, EZDOP_BBDIR)) {
+ fprintf(stderr, "ftdi_enable_bitbang: %s", d_device->error_str);
+ return false;
+ }
+
+ // Lower DTR by writing 0 to bitbang output
+ if (!send_byte(0x00)) // This actually lowers all outputs, not just DTR
+ return false;
+
+ // 10 ms sleep with RESET low
+ usleep(10000);
+
+ // Now raise DTR by writing 1 to bitbang output
+ if (!send_byte(0xFF)) // This actually raises all outputs, not just DTR
+ return false;
+
+ if (ftdi_disable_bitbang(d_device)) {
+ fprintf(stderr, "ftdi_disable_bitbang: %s", d_device->error_str);
+ return false;
+ }
+
+ // Minimum chunk size for reads to reduce latency
+ if (ftdi_read_data_set_chunksize(d_device, 256)) {
+ fprintf(stderr, "ftdi_read_data_set_chunksize: %s", d_device->error_str);
+ return false;
+ }
+
+ // 100 ms after RESET cleared to let things warm up
+ usleep(100000);
+
+ d_rate = EZDOP_DEFAULT_RATE;
+ return true;
+}
+
+bool ezdop::set_rate(int rate)
+{
+ assert(d_device);
+ assert(d_online);
+
+ // Rate command is one byte, followed by rate as operand
+ int divisor = 2000/rate;
+ if (send_byte(EZDOP_CMD_RATE) && send_byte((unsigned char)divisor)) {
+ d_rate = divisor;
+ return true;
+ }
+
+ return false;
+}
+
+bool ezdop::rotate()
+{
+ assert(d_online);
+ assert(d_device);
+
+ d_rotating = send_byte(EZDOP_CMD_ROTATE);
+ return d_rotating;
+}
+
+bool ezdop::stop_rotating()
+{
+ assert(d_online);
+ assert(d_device);
+
+ // TODO: set to antenna #1, perhaps do this in firmware instead
+ d_rotating = send_byte(EZDOP_CMD_STOP);
+ return d_rotating;
+}
+
+bool ezdop::stream()
+{
+ assert(d_online);
+ assert(d_device);
+
+ return (send_byte(EZDOP_CMD_STREAM));
+}
+
+bool ezdop::stop_streaming()
+{
+ assert(d_online);
+ assert(d_device);
+
+ return (send_byte(EZDOP_CMD_STROFF));
+}
+
+bool ezdop::send_byte(unsigned char data)
+{
+ assert(d_online);
+ assert(d_device);
+
+ if (ftdi_write_data(d_device, &data, 1) != 1) {
+ fprintf(stderr, "ftdi_write_data: %s", d_device->error_str);
+ return false;
+ }
+
+ return true;
+}
+
+int ezdop::read_raw(unsigned char *buffer, unsigned int length)
+{
+ assert(d_online);
+ assert(d_device);
+ assert(buffer);
+
+ // Read samples from USB port, 2 bytes per sample
+ int rd = ftdi_read_data(d_device, buffer, length);
+ if (rd < 0) {
+ fprintf(stderr, "ftdi_read_data: %s", d_device->error_str);
+ return -1;
+ }
+
+ return rd;
+}
+
+int ezdop::read_iq(complex<float> *buffer, unsigned int samples)
+{
+ assert(d_online);
+ assert(d_device);
+ assert(buffer);
+
+ // 4 phases, d_rate samples per phase, 2 bytes per sample
+ int bufsize = 8*d_rate*samples;
+ unsigned char *raw = new unsigned char[bufsize];
+
+ // Read until required bytes are read. Will block until bytes arrive.
+ int rd = 0;
+ while (rd < bufsize)
+ rd += read_raw(&raw[rd], bufsize-rd);
+
+ // Iterate through read bytes and invoke state machine
+ int i = 0, j = 0; // i index inputs, j indexes outputs
+ unsigned char ant;
+
+ while (i < bufsize) {
+ unsigned char ch = raw[i++];
+ if (d_state == ST_LO) {
+ d_val = ch; // Save lo byte
+ d_state = ST_HI; // Switch states
+ continue; // Done with this state
+ }
+
+ if (d_state == ST_HI) {
+ unsigned char ant = ch >> 4; // antenna is high nibble
+ if (ant != d_ant) { // Didn't get expected antenna
+ // Abort current sequence
+ d_ant = 8;
+ d_seq = 0;
+ d_val = 0;
+ d_in_phase = 0; d_quadrature = 0;
+ d_val = ch; // Act as if this were a lo byte instead
+ continue; // Stay in ST_HI
+ }
+ }
+
+ // Got correct antenna
+ d_val |= (ch & 0x03) << 8; // Mask off and save audio high value
+
+ // This down-converts rotation frequency to exactly 0 Hz
+ // while integrating audio response over duration of one antenna phase
+ if (d_ant == 8) // +I
+ d_in_phase += d_val;
+ else if (d_ant == 4) // +Q
+ d_quadrature += d_val;
+ else if (d_ant == 2) // -I
+ d_in_phase -= d_val;
+ else if (d_ant == 1) // -Q
+ d_quadrature -= d_val;
+ d_val = 0;
+
+ // Update expected antenna and sequence
+ if (++d_seq == d_rate) {
+ d_ant = d_ant >> 1;
+ d_seq = 0;
+ if (d_ant == 0) { // fell off the end
+ d_ant = 8; // FIXME: grab from controller.h
+
+ // We've accumulated I and Q over a whole antenna rotation
+ // Output complex<float> in range [-1.0, 1.0]
+ buffer[j++] = complex<float>(d_in_phase/(1024.0*d_rate),
+ d_quadrature/(1024.0*d_rate));
+ d_in_phase = 0; d_quadrature = 0;
+ }
+ }
+
+ d_state = ST_LO; // Switch states
+ };
+
+ delete raw;
+ return j;
+}
diff --git a/ezdop/src/host/ezdop/ezdop.h b/ezdop/src/host/ezdop/ezdop.h
new file mode 100644
index 000000000..b4ce1a454
--- /dev/null
+++ b/ezdop/src/host/ezdop/ezdop.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EZDOP_H__
+#define __EZDOP_H__
+
+// Application level includes
+#include <dopctrl.h>
+
+// System level includes
+#include <ftdi.h>
+#include <complex>
+
+using std::complex;
+
+class ezdop
+{
+public:
+ ezdop();
+ ~ezdop();
+
+ // Housekeeping
+ bool init(); // Attach to first device found, TODO: serial no
+ bool finish(); // Release device
+ bool reset(); // Reset state to post-init()
+
+ // Parameters
+ bool set_rate(int rate); // Set rotation rate
+ int rate() const // Get rotation rate
+ { return 2000/d_rate; }
+
+ // Commands
+ bool rotate(); // Start antenna rotation
+ bool stop_rotating(); // Stop antenna rotation
+ bool stream(); // Start audio streaming
+ bool stop_streaming(); // Stop audio streaming
+
+ // Raw read of sample stream from device, length in bytes (2 per sample)
+ int read_raw(unsigned char *buffer, unsigned int length);
+
+ // Read synced, downconverted I and Q samples, one per rotation
+ int read_iq(complex<float> *buffer, unsigned int samples);
+
+ // Status
+ bool is_online() const { return d_online; }
+
+private:
+ struct ftdi_context *d_device; // libftdi device instance data
+ bool d_online; // Successfully found and connected to device
+ bool d_rotating; // Doppler is rotating
+ int d_rate; // Current rotation rate (samples per antenna)
+ enum { ST_HI, ST_LO } d_state; // Current byte sync state
+ char d_ant; // Current antenna being sequenced
+ int d_seq; // Current phase sample number
+ int d_val; // Current reassembled audio sample value
+ int d_in_phase; // Downconverted I accumulator
+ int d_quadrature; // Downconverted Q accumulator
+
+ bool send_byte(unsigned char data); // Send a byte to AVR
+
+};
+
+#endif
diff --git a/ezdop/src/host/hunter/AUTHORS b/ezdop/src/host/hunter/AUTHORS
new file mode 100644
index 000000000..cdb61c9f1
--- /dev/null
+++ b/ezdop/src/host/hunter/AUTHORS
@@ -0,0 +1 @@
+Johnathan Corgan <jcorgan@aeinet.com>
diff --git a/ezdop/src/host/hunter/ChangeLog b/ezdop/src/host/hunter/ChangeLog
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ezdop/src/host/hunter/ChangeLog
diff --git a/ezdop/src/host/hunter/Makefile.am b/ezdop/src/host/hunter/Makefile.am
new file mode 100644
index 000000000..af437a64d
--- /dev/null
+++ b/ezdop/src/host/hunter/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = src
diff --git a/ezdop/src/host/hunter/NEWS b/ezdop/src/host/hunter/NEWS
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ezdop/src/host/hunter/NEWS
diff --git a/ezdop/src/host/hunter/README b/ezdop/src/host/hunter/README
new file mode 100644
index 000000000..d0368d842
--- /dev/null
+++ b/ezdop/src/host/hunter/README
@@ -0,0 +1,11 @@
+The files in this directory are a prototype implementation of the AE6HO
+radio-location system. It will not compile under the gnuradio build
+system at this time, nor does the source code use the libezdop interface
+to the EZ-Doppler hardware. The build does not recurse into this directory
+nor does the distribution tarball contain these files.
+
+Over time this functionality will be migrated in the 'gnuradio way' over
+to the gr-rdf component.
+
+The files here are not officially part of the GNU Radio project, but are
+here in the archive under GPL license as noted in the files.
diff --git a/ezdop/src/host/hunter/bootstrap b/ezdop/src/host/hunter/bootstrap
new file mode 100755
index 000000000..d8d180a61
--- /dev/null
+++ b/ezdop/src/host/hunter/bootstrap
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# Copyright 2001,2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+
+rm -fr config.cache autom4te*.cache
+
+aclocal -I config
+autoconf
+autoheader
+automake --add-missing
diff --git a/ezdop/src/host/hunter/config/hunter_ftdi.m4 b/ezdop/src/host/hunter/config/hunter_ftdi.m4
new file mode 100644
index 000000000..64977e803
--- /dev/null
+++ b/ezdop/src/host/hunter/config/hunter_ftdi.m4
@@ -0,0 +1,18 @@
+AC_DEFUN([HUNTER_FTDI],[
+ AC_LANG_PUSH(C)
+
+ AC_CHECK_HEADER([ftdi.h],[],[
+ AC_MSG_ERROR(["Hunter requires ftdi.h, not found, stop."])]
+ )
+
+ save_LIBS="$LIBS"
+ AC_SEARCH_LIBS(ftdi_init, [ftdi], [FTDI_LIBS=$LIBS],[
+ AC_MSG_ERROR(["Hunter requires libftdi, not found, stop."])]
+ )
+
+ LIBS="$save_LIBS"
+ AC_LANG_POP(C)
+
+ AC_SUBST(FTDI_LIBS)
+ AC_DEFINE([HAVE_LIBFTDI],[1],[Define to 1 if your system has libftdi.])
+])
diff --git a/ezdop/src/host/hunter/config/hunter_wx.m4 b/ezdop/src/host/hunter/config/hunter_wx.m4
new file mode 100644
index 000000000..5d6dd08e7
--- /dev/null
+++ b/ezdop/src/host/hunter/config/hunter_wx.m4
@@ -0,0 +1,11 @@
+AC_DEFUN([HUNTER_WX], [
+ AC_PATH_PROG([WXCONFIG],[wx-config],[no])
+ if test $WXCONFIG = no; then
+ AC_MSG_ERROR(["wxWidgets is required, not found, stop."])
+ fi
+
+ WX_FLAGS=`$WXCONFIG --cflags`
+ WX_LIBS=`$WXCONFIG --libs`
+ AC_SUBST(WX_FLAGS)
+ AC_SUBST(WX_LIBS)
+])
diff --git a/ezdop/src/host/hunter/configure.ac b/ezdop/src/host/hunter/configure.ac
new file mode 100644
index 000000000..b6bd4cf7e
--- /dev/null
+++ b/ezdop/src/host/hunter/configure.ac
@@ -0,0 +1,42 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.59)
+AC_INIT(hunter, 0.1svn, jcorgan@aeinet.com)
+AM_INIT_AUTOMAKE
+
+AC_CONFIG_SRCDIR([src/hunter.cc])
+AC_CONFIG_HEADER([config.h])
+
+# Checks for programs.
+AC_PROG_CXX
+AC_PROG_CC
+
+# Checks for libraries.
+
+# Checks for header files.
+AC_CHECK_HEADERS([fcntl.h sys/ioctl.h termios.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_STDBOOL
+AC_C_CONST
+AC_C_INLINE
+AC_TYPE_SIZE_T
+AC_STRUCT_TM
+
+# Checks for library functions.
+AC_PROG_GCC_TRADITIONAL
+AC_HEADER_STDC
+AC_FUNC_MKTIME
+AC_CHECK_FUNCS([modf sqrt])
+
+# Application specific checks
+HUNTER_WX
+HUNTER_FTDI
+
+AC_CONFIG_FILES([ \
+ Makefile
+ src/Makefile
+])
+
+AC_OUTPUT
diff --git a/ezdop/src/host/hunter/src/Makefile.am b/ezdop/src/host/hunter/src/Makefile.am
new file mode 100644
index 000000000..d1fe5eec2
--- /dev/null
+++ b/ezdop/src/host/hunter/src/Makefile.am
@@ -0,0 +1,42 @@
+# Copyright 2006 Johnathan Corgan.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2
+# as published by the Free Software Foundation.
+#
+# This software is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+bin_PROGRAMS = hunter
+hunter_SOURCES = \
+ calibrate.cc \
+ doppler.cc \
+ gps.cc \
+ histogram.cc \
+ hunter.cc \
+ hunterapp.cc \
+ hunter.xrc \
+ known.cc \
+ resource.cc \
+ sample.cc \
+ samplelog.cc \
+ search.cc \
+ serial.cc \
+ settings.cpp \
+ spherical.cc \
+ tactical.cc
+
+hunter_CXXFLAGS = $(WX_FLAGS)
+hunter_LDADD = \
+ $(FTDI_LIBS) \
+ $(WX_LIBS)
+
+resource.cc: hunter.xrc
+ wxrc -c -o resource.cc hunter.xrc
diff --git a/ezdop/src/host/hunter/src/calibrate.cc b/ezdop/src/host/hunter/src/calibrate.cc
new file mode 100644
index 000000000..2d4117f5e
--- /dev/null
+++ b/ezdop/src/host/hunter/src/calibrate.cc
@@ -0,0 +1,82 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application level includes
+#include "calibrate.h"
+#include "hunter.h"
+
+// wxWidgets includes
+#include <wx/xrc/xmlres.h>
+#include <wx/button.h>
+#include <wx/gauge.h>
+#include <wx/stattext.h>
+#include <wx/log.h>
+
+// Event table for CalibrationDialog
+BEGIN_EVENT_TABLE(CalibrationDialog, wxDialog)
+ EVT_BUTTON(XRCID("calibration_start_button"), CalibrationDialog::OnStart)
+ EVT_BUTTON(XRCID("calibration_cancel_button"), CalibrationDialog::OnCancelOrZero)
+END_EVENT_TABLE()
+
+CalibrationDialog::CalibrationDialog(HunterFrame *parent)
+{
+ m_hunter_frame = parent;
+ wxXmlResource::Get()->LoadDialog(this, parent, _T("calibration_dialog"));
+ m_start_button = XRCCTRL(*this, "calibration_start_button", wxButton);
+ m_cancel_button = XRCCTRL(*this, "calibration_cancel_button", wxButton);
+ m_progress_gauge = XRCCTRL(*this, "calibration_progress_gauge", wxGauge);
+ m_instructions_text = XRCCTRL(*this, "calibration_instructions_text", wxStaticText);
+ m_progress_text = XRCCTRL(*this, "calibration_progress_text", wxStaticText);
+ m_cancelled = false;
+}
+
+void CalibrationDialog::OnStart(wxCommandEvent &event)
+{
+ wxLogDebug(_T("CalibrationDialog::OnStart()"));
+ wxString msg;
+
+ m_start_button->Disable();
+ m_equalized = false;
+ for (int i = 0; i < 6; i++) {
+ msg.Printf(_T("Peforming calibration step #%i"), i+1);
+ m_progress_text->SetLabel(msg);
+ if (m_cancelled)
+ break;
+ wxYield();
+ m_hunter_frame->DoCalibrationStep(i);
+ if (m_cancelled)
+ break;
+ m_progress_gauge->SetValue(i+1);
+ }
+ m_equalized = true;
+ m_instructions_text->SetLabel(_T("After pressing OK, you will need to\n" \
+ "set the signal source to straight ahead\n" \
+ "and press the Doppler 'Zero' button."));
+ m_progress_text->SetLabel(_T("Calibration completed."));
+ m_cancel_button->SetLabel(_T("OK"));
+}
+
+void CalibrationDialog::OnCancelOrZero(wxCommandEvent &event)
+{
+ wxLogDebug(_T("CalibrationDialog::OnCancel()"));
+ if (!m_equalized) {
+ m_cancelled = true;
+ }
+
+ EndModal(0);
+}
diff --git a/ezdop/src/host/hunter/src/calibrate.h b/ezdop/src/host/hunter/src/calibrate.h
new file mode 100644
index 000000000..7f3f399e2
--- /dev/null
+++ b/ezdop/src/host/hunter/src/calibrate.h
@@ -0,0 +1,53 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __CALIBRATE_H__
+#define __CALIBRATE_H__
+
+// wxWidgets includes
+#include <wx/event.h>
+#include <wx/dialog.h>
+#include <wx/gauge.h> // Can't forward declare because wxGauge is a macro
+
+// Forward declarations
+class wxStaticText;
+class HunterFrame;
+class wxButton;
+
+class CalibrationDialog : public wxDialog
+{
+public:
+ CalibrationDialog(HunterFrame *parent);
+
+private:
+ void OnStart(wxCommandEvent &event);
+ void OnCancelOrZero(wxCommandEvent &event);
+
+ HunterFrame *m_hunter_frame;
+ wxGauge *m_progress_gauge;
+ wxStaticText *m_progress_text;
+ wxStaticText *m_instructions_text;
+ wxButton *m_start_button;
+ wxButton *m_cancel_button;
+ bool m_cancelled;
+ bool m_equalized;
+
+ DECLARE_EVENT_TABLE();
+};
+
+#endif // __CALIBRATE_H__
diff --git a/ezdop/src/host/hunter/src/doppler.cc b/ezdop/src/host/hunter/src/doppler.cc
new file mode 100644
index 000000000..1e7b3cf49
--- /dev/null
+++ b/ezdop/src/host/hunter/src/doppler.cc
@@ -0,0 +1,517 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application level includes
+#include "doppler.h"
+#include "util.h"
+#include <dopctrl.h>
+
+// wxWidgets includes
+#include <wx/log.h>
+#include <wx/frame.h>
+
+// System level includes
+#include <cmath>
+
+#define SAMPLERATE 8000
+#define QUANTUM 0.2 // Sample period in seconds
+#define MAXSAMPLE 0x3FF // 12 bit ADC
+
+#define DEFAULT_SELECTED_ROTATION_RATE 2 // 500 Hz until told otherwise
+#define DEFAULT_FILTER_LEVEL 20
+
+#define NORMALIZEPHASE(x) \
+ if ((x) > M_PI) \
+ (x) -= 2*M_PI; \
+ if ((x) < -M_PI) \
+ (x) += 2*M_PI;
+
+unsigned char rotation_rates[] = {
+ 8, // 250 Hz
+ 5, // 400 Hz
+ 4, // 500 Hz
+ 3, // 666 Hz
+ 2, // 1000 Hz
+ 1 // 2000 Hz
+};
+
+const wxEventType wxEVT_DOPPLER_UPDATE = wxNewEventType();
+
+EZDopplerUpdate::EZDopplerUpdate(const wxEventType &event, float &in_phase,
+ float &quadrature, float &volume) :
+wxNotifyEvent(event)
+{
+ m_in_phase = in_phase;
+ m_quadrature = quadrature;
+ m_volume = volume;
+}
+
+DopplerBackground::DopplerBackground(wxWindow *window, EZDoppler *doppler)
+{
+ wxASSERT(window);
+ wxASSERT(doppler);
+
+ m_running = false;
+ m_dest = window;
+ m_doppler = doppler;
+ Create();
+}
+
+// It's in thread.h but somehow gets undef'd
+typedef void *ExitCode;
+ExitCode DopplerBackground::Entry()
+{
+ float in_phase, quadrature, phase, magnitude, volume, rflevel;
+
+ m_running = true;
+ while (!TestDestroy()) {
+ if (m_doppler->Sample((int)(QUANTUM*SAMPLERATE), in_phase, quadrature, volume)) {
+ EZDopplerUpdate update(wxEVT_DOPPLER_UPDATE, in_phase, quadrature, volume);
+ wxPostEvent(m_dest, update);
+ }
+ }
+ m_running = false;
+}
+
+EZDoppler::EZDoppler(wxWindow *gui)
+{
+ wxASSERT(gui);
+
+ m_thread = NULL;
+ m_online = false;
+ m_selected_rate = DEFAULT_SELECTED_ROTATION_RATE;
+ m_gui = gui;
+ m_in_phase = 0.0;
+ m_quadrature = 0.0;
+ m_alpha = 1.0/(DEFAULT_FILTER_LEVEL*200);
+ m_beta = 1.0-m_alpha;
+ m_phase = 0.0;
+ m_offset = 0.0;
+
+ for(int i = 0; i < NUM_RATES; i++)
+ m_calibration[i] = 0.0;
+
+#if HAVE_LIBFTDI
+ m_device = new struct ftdi_context;
+ wxASSERT(m_device);
+ if (ftdi_init(m_device)) {
+ wxLogWarning(_T("ftdi_init: %s"), m_device->error_str);
+ return;
+ }
+#endif
+
+}
+
+EZDoppler::~EZDoppler()
+{
+ if (m_online) {
+ wxLogMessage(_T("EZDoppler::~EZDoppler(): doppler still online in destructor, finalizing"));
+ Finalize();
+ }
+#if HAVE_LIBFTDI
+ wxASSERT(m_device);
+ ftdi_deinit(m_device);
+ delete m_device;
+#endif
+}
+
+bool EZDoppler::Initialize()
+{
+ m_online = false;
+
+#if HAVE_LIBFTDI
+ if (ftdi_usb_open(m_device, EZDOP_VENDORID, EZDOP_PRODUCTID)) {
+ wxLogDebug(_T("ftdi_usb_open: %s"), m_device->error_str);
+ return false;
+ }
+#elif HAVE_LIBFTD2XX
+ if ((m_status = FT_Open(0, &m_handle)) != FT_OK) {
+ wxLogError(_T("FT_Open failed: %i"), m_status);
+ return false;
+ }
+#endif
+
+ m_online = true;
+ if (m_online)
+ Reset();
+
+ return m_online;
+ }
+
+bool EZDoppler::Finalize()
+{
+ if (!m_online)
+ return true;
+
+ if (m_thread && m_thread->IsRunning()) {
+ wxLogDebug(_T("EZDoppler::Finalize: finalizing a running doppler"));
+ Stop();
+ }
+
+#if HAVE_LIBFTDI
+ if (ftdi_usb_close(m_device)) {
+ wxLogWarning(_T("ftdi_usb_close: %s"), m_device->error_str);
+ return false;
+ }
+#elif HAVE_LIBFTD2XX
+ if ((m_status = FT_Close(m_handle)) != FT_OK) {
+ wxLogWarning(_T("FT_Close failed: %i"), m_status);
+ return false;
+ }
+#endif
+
+ m_online = false;
+ return true;
+}
+
+bool EZDoppler::IsOnline()
+{
+ return m_online;
+}
+
+bool EZDoppler::send_byte(unsigned char data)
+{
+ wxASSERT(m_online);
+#if HAVE_LIBFTDI
+ if (ftdi_write_data(m_device, &data, 1) != 1) {
+ wxLogWarning(_T("ftdi_write_data: %s"), m_device->error_str);
+ return false;
+ }
+#elif HAVE_LIBFTD2XX
+ DWORD written;
+ if ((m_status = FT_Write(m_handle, &data, 1, &written)) != FT_OK || written != 1) {
+ wxLogError(_T("FT_Write failed: %i"), m_status);
+ return false;
+ }
+#endif
+ return true;
+}
+
+bool EZDoppler::Reset()
+{
+ wxASSERT(m_online);
+
+ if (m_thread && m_thread->IsRunning()) {
+ wxLogDebug(_T("EZDoppler::Reset: resetting running doppler"));
+ Stop();
+ }
+
+
+ // Reset FTDI chipset
+#if HAVE_LIBFTDI
+ if (ftdi_usb_reset(m_device)) {
+ wxLogWarning(_T("ftdi_usb_reset: %s"), m_device->error_str);
+ return false;
+ }
+#elif HAVE_LIBFTD2XX
+ if ((m_status = FT_ResetDevice(m_handle) != FT_OK)) {
+ wxLogError(_T("FT_ResetDevice failed: %i"), m_status);
+ return false;
+ }
+#endif
+
+ // Set FTDI chipset baudrate for bitbang
+#if HAVE_LIBFTDI
+ if (ftdi_set_baudrate(m_device, EZDOP_BAUDRATE)) {
+ wxLogWarning(_T("ftdi_set_baudrate: %s"), m_device->error_str);
+ return false;
+ }
+#elif HAVE_LIBFTD2XX
+ if ((m_status = FT_SetBaudRate(m_handle, EZDOP_BAUDRATE)) != FT_OK) {
+ wxLogError(_T("FT_SetBaudRate failed: %i"), m_status);
+ return false;
+ }
+#endif
+
+ // Toggle DTR (-->AVR RESET)
+#if HAVE_LIBFTDI
+ // Enable bitbang
+ if (ftdi_enable_bitbang(m_device, EZDOP_BBDIR)) {
+ wxLogWarning(_T("ftdi_enable_bitbang: %s"), m_device->error_str);
+ return false;
+ }
+
+ // Lower DTR by writing 0 to bitbang output
+ if (!send_byte(0x00)) // HMMM: this actually lowers all outputs, of course
+ return false;
+#elif HAVE_LIBFTD2XX
+ // Set DTR line (goes low) to reset AVR and delay
+ if ((m_status = FT_SetDtr(m_handle)) != FT_OK) {
+ wxLogError(_T("FT_SetDtr failed: %i"), m_status);
+ return false;
+ }
+#endif
+
+ // 10 ms sleep with RESET low
+ wxMilliSleep(10);
+
+#if HAVE_LIBFTDI
+ // Now raise DTR by writing 1 to bitbang output
+ if (!send_byte(0xFF))
+ return false;
+
+ if (ftdi_disable_bitbang(m_device)) {
+ wxLogWarning(_T("ftdi_disable_bitbang: %s"), m_device->error_str);
+ return false;
+ }
+
+ // Minimum chunk size for reads to reduce latency
+ if (ftdi_read_data_set_chunksize(m_device, 256)) {
+ wxLogWarning(_T("ftdi_read_data_set_chunksize: %s"), m_device->error_str);
+ return false;
+ }
+#elif HAVE_LIBFTD2XX
+ if ((m_status = FT_ClrDtr(m_handle)) != FT_OK) {
+ wxLogError(_T("FT_ClrDtr failed: %i"), m_status);
+ return false;
+ }
+#endif
+
+ // 100 ms after RESET cleared to let things warm up
+ wxMilliSleep(100);
+
+ m_selected_rate = DEFAULT_SELECTED_ROTATION_RATE;
+
+ return true;
+}
+
+bool EZDoppler::Start()
+{
+ wxASSERT(m_online);
+ // TODO: flush stream data
+
+ if (!send_byte(EZDOP_CMD_ROTATE) || !send_byte(EZDOP_CMD_STREAM))
+ return false;
+
+ m_thread = new DopplerBackground(m_gui, this);
+ m_thread->Run();
+}
+
+bool EZDoppler::Stop()
+{
+ wxASSERT(m_online);
+ // TODO: flush stream data
+
+ if (m_thread && m_thread->IsRunning()) {
+ m_thread->Delete();
+ while (m_thread->IsRunning()) {
+ wxYield();
+ }
+ }
+
+ m_thread = NULL;
+ return (send_byte(EZDOP_CMD_STROFF) && send_byte(EZDOP_CMD_STOP));
+}
+
+bool EZDoppler::SelectRotationRate(int n)
+{
+ wxASSERT(m_online);
+ wxASSERT(n >= 0 && n < 6);
+
+ unsigned char rate = rotation_rates[n];
+ if (send_byte(EZDOP_CMD_RATE) && send_byte(rate)) {
+ m_selected_rate = n;
+ m_in_phase = 0.0;
+ m_quadrature = 0.0;
+ return true;
+ }
+
+ return false;
+}
+
+int EZDoppler::GetSelectedRotationRate()
+{
+ return m_selected_rate;
+}
+
+bool EZDoppler::Zero()
+{
+ return true;
+}
+
+bool EZDoppler::SetFilter(int n)
+{
+ wxASSERT(n > 0);
+ m_alpha = 1.0/(n*200); // Time constant is filter value divided by 5 (empirically determined)
+ m_beta = 1.0-m_alpha;
+ return true;
+}
+
+bool EZDoppler::Sample(int nsamples, float &in_phase, float &quadrature, float &volume)
+{
+ unsigned short *audio = new unsigned short[nsamples*2];
+ unsigned char *antenna = new unsigned char[nsamples];
+
+ unsigned int rd;
+ unsigned int count = 0;
+
+ // Read samples from USB port, 2 bytes per sample
+ while (count < nsamples*2) {
+ unsigned int amt = nsamples*2-count;
+ unsigned char *ptr = (unsigned char *)&audio[count/2]; // if count is odd, causes frame slip?
+ if ((count/2)*2 != count)
+ wxLogDebug(_T("EZDoppler::Sample: count is odd (%i)"), count);
+#if HAVE_LIBFTDI
+ rd = ftdi_read_data(m_device, ptr, amt);
+ if (rd < 0) {
+ wxLogWarning(_T("ftdi_read_data: %s"), m_device->error_str);
+ return false; // FIXME: memory leak for antenna and audio!
+ }
+ count += rd;
+#elif HAVE_LIBFTD2XX
+ DWORD num;
+ FT_STATUS status = FT_Read(m_handle, ptr, amt, &num);
+ if (status != FT_OK) {
+ wxLogWarning(_T("FT_Read: %i"), status);
+ return false; // FIXME: memory leak for antenna and audio!
+ }
+ count += num;
+#endif
+ }
+
+ // Extract antenna array position from samples, flag unsynced if not a valid antenna value
+ bool sync = true;
+ for (int i = 0; i < nsamples; i++) {
+ unsigned char ant = (audio[i] & 0xF000) >> 12;
+ if (ant != 8 && ant != 4 && ant != 2 && ant != 1)
+ sync = false;
+ antenna[i] = ant;
+ audio[i] &= 0x03FF;
+ }
+
+ // If not synced, throw away a byte in receive stream to resync
+ unsigned char dummy;
+ if (!sync) {
+ wxLogDebug(_T("EZDoppler::Sample: sync failure detected"));
+#if HAVE_LIBFTDI
+ ftdi_read_data(m_device, &dummy, 1);
+#elif HAVE_LIBFTD2XX
+ DWORD rd;
+ FT_Read(m_handle, &dummy, 1, &rd);
+#endif
+ return false; // FIXME: memory leak for antenna and audio!
+ }
+
+ // Calculate DC offset and max and min values
+ float sum = 0.0;
+ float mean = 0.0;
+ for (int i = 0; i < nsamples; i++)
+ sum += audio[i];
+ mean = sum/nsamples;
+
+ // Calculate doppler response
+ unsigned char ant;
+ float sample;
+ volume = 0.0;
+ for (int i = 0; i < nsamples; i++) {
+ ant = antenna[i];
+
+ // Subtract DC offset and scale to -1 to 1
+ sample = 2*(((float)audio[i])-mean)/MAXSAMPLE;
+
+ // Calculate peak volume
+ if (fabs(sample) > volume)
+ volume = fabs(sample);
+
+ // Integrate and lowpass filter sample into I/Q based on which antenna is selected
+ // Order here creates a clockwise rotating I/Q phasor
+ switch(ant) {
+ case 8:
+ m_in_phase = m_in_phase*m_beta + sample*m_alpha;
+ break;
+ case 4:
+ m_quadrature = m_quadrature*m_beta - sample*m_alpha;
+ break;
+ case 2:
+ m_in_phase = m_in_phase*m_beta - sample*m_alpha;
+ break;
+ case 1:
+ m_quadrature = m_quadrature*m_beta + sample*m_alpha;
+ break;
+ default:
+ wxLogError(_T("EZDoppler::Sample: Unknown antenna value %i"), ant);
+ break;
+ }
+ }
+
+ // m_phase is the actual instrument reading regardless of calibration
+ m_phase = atan2(m_quadrature, m_in_phase);
+
+ // Calibration angle is sum of equalized offset and global offset
+ float cal = m_calibration[m_selected_rate] + m_offset;
+
+ // Rotate I, Q by calibration angle
+ float i_cal = cos(cal);
+ float q_cal = sin(cal);
+ in_phase = m_in_phase*i_cal - m_quadrature*q_cal;
+ quadrature = m_quadrature*i_cal + m_in_phase*q_cal;
+
+ delete antenna;
+ delete audio;
+ return true;
+}
+
+bool EZDoppler::Calibrate(float phase)
+{
+ float offset = phase - m_phase;
+ NORMALIZEPHASE(offset);
+ m_calibration[m_selected_rate] = offset;
+ return true;
+}
+
+bool EZDoppler::SetCalibration(int rate, float offset)
+{
+ wxASSERT(rate >= 0 && rate < 7);
+ if (rate < 6)
+ m_calibration[rate] = offset;
+ else
+ m_offset = offset;
+}
+
+float EZDoppler::GetCalibration(int rate)
+{
+ wxASSERT(rate >= 0 && rate < 7);
+ if (rate < 6)
+ return m_calibration[rate];
+ else
+ return m_offset;
+}
+
+bool EZDoppler::SetOffset(float offset)
+{
+ m_offset = offset-m_phase-m_calibration[m_selected_rate];
+ NORMALIZEPHASE(m_offset);
+ NORMALIZEPHASE(m_offset);
+ NORMALIZEPHASE(m_offset);
+}
+
+bool EZDoppler::Nudge(float amount)
+{
+ float cal = m_calibration[m_selected_rate];
+ cal += amount;
+ NORMALIZEPHASE(cal);
+ m_calibration[m_selected_rate] = cal;
+ return true;
+}
+
+bool EZDoppler::NudgeAll(float amount)
+{
+ m_offset += amount;
+ NORMALIZEPHASE(m_offset);
+ return true;
+}
diff --git a/ezdop/src/host/hunter/src/doppler.h b/ezdop/src/host/hunter/src/doppler.h
new file mode 100644
index 000000000..1471de6a4
--- /dev/null
+++ b/ezdop/src/host/hunter/src/doppler.h
@@ -0,0 +1,130 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __DOPPLER_H__
+#define __DOPPLER_H__
+
+// Autoconf generated configure options
+#if HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+// USB access library
+#if HAVE_LIBFTDI
+ #include <ftdi.h>
+#elif HAVE_LIBFTD2XX
+ #if __WIN32__
+ #include <windows.h>
+ #endif
+ #include <FTD2XX.H>
+#endif
+
+#include <wx/event.h>
+
+#define NUM_RATES 6
+
+class EZDoppler;
+
+class DopplerBackground : public wxThread
+{
+public:
+ DopplerBackground(wxWindow *window, EZDoppler *doppler);
+ virtual ExitCode Entry();
+ bool IsRunning() { return m_running; }
+
+private:
+ bool m_running;
+ wxWindow *m_dest;
+ EZDoppler *m_doppler;
+};
+
+class EZDopplerUpdate : public wxNotifyEvent
+{
+public:
+ EZDopplerUpdate(const wxEventType &event, float &in_phase, float &quadrature,
+ float &volume);
+ virtual wxEvent *Clone() const { return new EZDopplerUpdate(*this); }
+
+ float m_in_phase;
+ float m_quadrature;
+ float m_volume;
+};
+
+extern const wxEventType wxEVT_DOPPLER_UPDATE;
+
+typedef void(wxEvtHandler::*EZDopplerUpdateFunction)(EZDopplerUpdate&);
+
+#define EVT_DOPPLER_UPDATE(fn) \
+ DECLARE_EVENT_TABLE_ENTRY( \
+ wxEVT_DOPPLER_UPDATE, -1, -1, \
+ (wxObjectEventFunction)(wxEventFunction)(EZDopplerUpdateFunction)&fn, \
+ (wxObject *)NULL \
+ ),
+
+class EZDoppler
+{
+public:
+ EZDoppler(wxWindow *gui);
+ ~EZDoppler();
+
+ // Control commands
+ bool Initialize();
+ bool Finalize();
+ bool IsOnline();
+ bool Start();
+ bool Stop();
+ bool Zero();
+ bool SetFilter(int n);
+ bool SelectRotationRate(int n);
+ int GetSelectedRotationRate();
+ bool Reset();
+ bool Sample(int nsamples, float &in_phase, float &quadrature, float &volume);
+ bool Calibrate(float phase);
+ bool SetCalibration(int rate, float offset);
+ float GetCalibration(int rate);
+ bool SetOffset(float offset = 0.0);
+ bool Nudge(float amount);
+ bool NudgeAll(float amount);
+
+private:
+ // USB interaction
+#if HAVE_LIBFTDI
+ struct ftdi_context *m_device; // libftdi device instance data
+#elif HAVE_LIBFTD2XX
+ FT_HANDLE m_handle; // FTD2XX device instance data
+ FT_STATUS m_status; // FTD2XX device function call results
+#endif
+ bool send_byte(unsigned char data);
+
+ // Doppler control
+ bool m_online;
+ int m_selected_rate;
+ wxWindow *m_gui;
+ DopplerBackground *m_thread;
+
+ // DSP state
+ float m_in_phase; // Filtered I value
+ float m_quadrature; // Filtered Q value
+ float m_alpha; // Exponential lowpass constant
+ float m_beta; // Exponential lowpass constant = 1-alpha
+ float m_phase; // Actual phase of doppler before calibration
+ float m_offset; // Global calibration angle
+ float m_calibration[NUM_RATES]; // Individual rotation rate offset
+};
+
+#endif // __DOPPLER_H__
diff --git a/ezdop/src/host/hunter/src/gps.cc b/ezdop/src/host/hunter/src/gps.cc
new file mode 100644
index 000000000..c891251e8
--- /dev/null
+++ b/ezdop/src/host/hunter/src/gps.cc
@@ -0,0 +1,247 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application level includes
+#include "gps.h"
+#include "serial.h"
+
+// wxWidgets includes
+#include <wx/log.h>
+
+#define NMEA_BAUD 4800
+#define NMEA_BUFSIZE 82 // Maximum NMEA sentence length
+
+const wxEventType wxEVT_GPS_UPDATE = wxNewEventType();
+
+GPSUpdate::GPSUpdate(const wxEventType &event, GPRMC *gprmc) :
+wxNotifyEvent(event)
+{
+ m_gprmc = gprmc;
+}
+
+GPSBackground::GPSBackground(GPS *gps)
+{
+ wxLogDebug(_T("GPSBackground::GPSBackground()"));
+ wxASSERT(gps);
+
+ m_running = false;
+ m_gps = gps;
+ m_port = gps->GetPort();
+ Create();
+}
+
+// It's in thread.h but somehow gets undef'd
+typedef void *ExitCode;
+ExitCode GPSBackground::Entry()
+{
+ wxLogDebug(_T("GPSBackground::GPSBackground: entry"));
+
+ m_running = true;
+ while (!TestDestroy())
+ PerLoop();
+ m_running = false;
+
+ wxLogDebug(_T("GPSBackground::GPSBackground: exit"));
+}
+
+void GPSBackground::PerLoop()
+{
+ static char buffer[NMEA_BUFSIZE];
+ static int offset = 0;
+
+ while(m_port->RxReady() > 0) {
+ while (offset < NMEA_BUFSIZE) {
+ // Read a byte into the buffer from the GPS data
+ if (m_port->Read(&buffer[offset], 1) != 1)
+ return; // No more to read or read error/timeout, bail
+
+ // Test for end of NMEA message
+ if (buffer[offset] == '\r' || buffer[offset] == '\n') {
+ buffer[offset] = '\0'; // Append end of string null
+ if (strlen(buffer))
+ m_gps->RxData(buffer);
+ offset = 0;
+ }
+ else
+ offset++;
+ }
+
+ wxLogDebug(_T("GPSBackground: discarding too long input"));
+ offset = 0;
+ }
+
+ wxMilliSleep(500);
+}
+
+GPS::GPS(wxEvtHandler *dest)
+{
+ wxLogDebug(_T("GPS::GPS()"));
+ m_thread = NULL;
+ m_dest = dest;
+}
+
+GPS::~GPS()
+{
+ wxLogDebug(_T("GPS::~GPS()"));
+}
+
+bool GPS::Start(wxString &port)
+{
+ wxLogDebug(_T("GPS::Start(): %s"), port.c_str());
+ m_port = new SerialPort(port);
+
+ if (m_port->Open(NMEA_BAUD) == false) {
+ delete m_port;
+ return false;
+ }
+
+ m_thread = new GPSBackground(this);
+ m_thread->Run();
+ return true;
+}
+
+bool GPS::Stop()
+{
+ wxLogDebug(_T("GPS::Stop()"));
+
+ if (m_thread && m_thread->IsRunning()) {
+ m_thread->Delete();
+ while (m_thread->IsRunning()) {
+ wxYieldIfNeeded();
+ }
+ }
+
+ m_thread = NULL;
+
+ m_port->Close();
+ if (m_port)
+ delete m_port;
+
+ return true;
+}
+
+bool NMEA::Checksum(char *sentence)
+{
+ unsigned char checksum = '\0';
+ char ch, *pos = sentence, ctxt[3];
+
+ while ((ch = *pos++) != '*' && ch != '\0')
+ checksum ^= ch;
+
+ sprintf(ctxt, "%02X", checksum);
+ if (strncmp(ctxt, pos, 2))
+ return false;
+ else
+ return true;
+}
+
+char *NMEA::Field(char *sentence, int num)
+{
+ static char result[NMEA_BUFSIZE];
+ char ch, *pos = sentence;
+
+ while (num-- > 0)
+ while ((ch = *pos++) != ',' && ch != '\0')
+ continue;
+
+ strncpy(result, pos, NMEA_BUFSIZE-1);
+ int i = 0;
+ pos = result;
+ while (*pos && *pos != ',' && *pos != '*' && *pos != '\r' && ++i < NMEA_BUFSIZE)
+ pos++;
+
+ *pos = 0;
+ return result;
+}
+
+double NMEA::Coord(char *sentence, int num)
+{
+ double coord, degrees, minutes;
+
+ sscanf(Field(sentence, num), "%lf", &coord);
+ minutes = 100.0*modf(coord/100.0, &degrees);
+ coord = degrees+minutes/60.0;
+
+ char *ptr = Field(sentence, num+1);
+ if (*ptr == 'S' || *ptr == 'W')
+ coord = -coord;
+
+ return coord;
+}
+
+void GPS::RxData(char *buffer)
+{
+ wxASSERT(buffer);
+
+ if (NMEA::Checksum(buffer+1)) {
+ if (strncmp("$GPRMC", buffer, 6) == 0) {
+ GPRMC *fix = new GPRMC(buffer);
+ GPSUpdate update(wxEVT_GPS_UPDATE, fix);
+ wxPostEvent(m_dest, update);
+ }
+ }
+ else
+ wxLogDebug(_T("GPS::RxData: NMEA checksum failed for input"));
+}
+
+GPRMC::GPRMC(char *sentence)
+{
+ wxASSERT(sentence);
+
+ struct tm stamp;
+ char digits[2];
+
+ char *p = Field(sentence, 1);
+ wxASSERT(p);
+ strncpy(digits, p, 2);
+ stamp.tm_hour = atoi(digits);
+ strncpy(digits, p+2, 2);
+ stamp.tm_min = atoi(digits);
+ strncpy(digits, p+4, 2);
+ stamp.tm_sec = atoi(digits);
+
+ p = Field(sentence, 9);
+ wxASSERT(p);
+ strncpy(digits, p, 2);
+ stamp.tm_mday = atoi(digits);
+ strncpy(digits, p+2, 2);
+ stamp.tm_mon = atoi(digits)-1;
+ strncpy(digits, p+4, 2);
+ stamp.tm_year = atoi(digits)+100;
+
+ m_stamp = mktime(&stamp);
+ m_valid = !strcmp(Field(sentence, 2), "A");
+ m_fix.SetLatitude(Coord(sentence, 3));
+ m_fix.SetLongitude(Coord(sentence, 5));
+ sscanf(Field(sentence, 7), "%f", &m_speed);
+ sscanf(Field(sentence, 8), "%f", &m_heading);
+ sscanf(Field(sentence, 10), "%f", &m_magnetic);
+ if (!strcmp(Field(sentence, 11), "W"))
+ m_magnetic = -m_magnetic;
+ m_mode = *Field(sentence, 12);
+}
+
+void GPRMC::AsString(char *buf)
+{
+ sprintf(buf, "%s %lf %lf %f %f %f, %s",
+ ctime(&m_stamp),
+ m_fix.Latitude(),
+ m_fix.Longitude(),
+ m_speed, m_heading, m_magnetic,
+ m_valid ? "valid" : "invalid");
+}
diff --git a/ezdop/src/host/hunter/src/gps.h b/ezdop/src/host/hunter/src/gps.h
new file mode 100644
index 000000000..da0d8e4c8
--- /dev/null
+++ b/ezdop/src/host/hunter/src/gps.h
@@ -0,0 +1,119 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __GPS_H__
+#define __GPS_H__
+
+// Autoconf generated configure options
+#if HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+// Application level includes
+#include "spherical.h"
+
+// wxWidgets includes
+#include <wx/event.h>
+
+// System level includes
+#include <math.h> // For some reason, <cmath> doesn't include modf needed in gps.cpp
+
+class GPRMC;
+
+class GPSUpdate : public wxNotifyEvent
+{
+public:
+ GPSUpdate(const wxEventType &event, GPRMC *gprmc);
+ virtual wxEvent *Clone() const { return new GPSUpdate(*this); }
+ GPRMC *m_gprmc;
+};
+
+extern const wxEventType wxEVT_GPS_UPDATE;
+
+typedef void(wxEvtHandler::*GPSUpdateFunction)(GPSUpdate&);
+
+#define EVT_GPS_UPDATE(fn) \
+ DECLARE_EVENT_TABLE_ENTRY( \
+ wxEVT_GPS_UPDATE, -1, -1, \
+ (wxObjectEventFunction)(wxEventFunction)(GPSUpdateFunction)&fn, \
+ (wxObject *)NULL \
+ ),
+
+class NMEA
+{
+public:
+ static bool Checksum(char *sentence);
+ static char *Field(char *sentence, int num);
+ static double Coord(char *sentence, int num);
+};
+
+class GPRMC : public NMEA
+{
+public:
+ GPRMC(char *sentence);
+ void AsString(char *buf);
+
+ time_t m_stamp;
+ bool m_valid;
+ Spherical m_fix;
+ float m_speed;
+ float m_heading;
+ float m_magnetic;
+ unsigned char m_mode;
+};
+
+class GPS;
+class SerialPort;
+
+class GPSBackground : public wxThread
+{
+public:
+ GPSBackground(GPS *gps);
+ virtual ExitCode Entry();
+ bool IsRunning() { return m_running; }
+
+private:
+ void PerLoop();
+
+ bool m_running;
+ GPS *m_gps;
+ SerialPort *m_port;
+};
+
+class wxString;
+
+class GPS
+{
+public:
+ GPS(wxEvtHandler *dest);
+ ~GPS();
+
+ bool Start(wxString &port);
+ bool Stop();
+ SerialPort *GetPort() { return m_port; }
+ void RxData(char *buffer);
+
+private:
+ void RxGPRMC(char *buffer);
+
+ GPSBackground *m_thread;
+ wxEvtHandler *m_dest;
+ SerialPort *m_port;
+};
+
+#endif // __GPS_H__
diff --git a/ezdop/src/host/hunter/src/histogram.cc b/ezdop/src/host/hunter/src/histogram.cc
new file mode 100644
index 000000000..abadad5e6
--- /dev/null
+++ b/ezdop/src/host/hunter/src/histogram.cc
@@ -0,0 +1,170 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application level includes
+#include "histogram.h"
+
+// wxWidgets includes
+#include <wx/log.h>
+#include <wx/dc.h>
+#include <wx/dcclient.h>
+
+using namespace std;
+
+ErrorHistogram::ErrorHistogram()
+{
+ Reset();
+}
+
+void ErrorHistogram::Reset()
+{
+ m_bins = vector<float>(360);
+ m_mode = 0;
+ m_modal_frequency = 0.0;
+ m_isum = 0.0;
+ m_qsum = 0.0;
+ m_msum = 0.0;
+ m_conc = 0.0;
+ m_mean = 0.0;
+ m_count = 0;
+}
+
+void ErrorHistogram::Calc(const vector<Sample> &samples, const Spherical &location)
+{
+ Reset();
+ for (int i = 0; i < samples.size(); i++) {
+ const Sample &sample = samples[i];
+ float angle, ierror, qerror;
+ sample.CalcError(location, angle, ierror, qerror);
+ Add(angle, sample.Strength(), ierror, qerror);
+ }
+ Normalize();
+}
+
+void ErrorHistogram::Add(float angle, float magnitude, float ierror, float qerror)
+{
+ int index = (int)(angle+180.0);
+ while (index > 359)
+ index -= 360;
+ while (index < 0)
+ index += 360;
+ wxASSERT(index >= 0 && index < 360);
+
+ float freq = m_bins[index] += magnitude;
+ if (freq > m_modal_frequency) {
+ m_modal_frequency = freq;
+ m_mode = index-180;
+ }
+
+ m_isum += ierror;
+ m_qsum += qerror;
+ m_msum += magnitude;
+ m_count++;
+}
+
+// This turns the histogram into an actual PDF
+void ErrorHistogram::Normalize()
+{
+ if (m_msum == 0.0)
+ return;
+
+ for (int i = 0; i < 360; i++)
+ m_bins[i] = m_bins[i]/(m_msum);
+
+ m_modal_frequency /= m_msum;
+ m_conc = (m_isum*m_isum+m_qsum*m_qsum)/(m_msum*m_msum);
+ if (m_conc > 0.0)
+ m_mean = atan2(m_qsum, m_isum)*180.0/M_PI;
+}
+
+// Event table for HistogramPanel
+BEGIN_EVENT_TABLE(HistogramPanel, wxPanel)
+ EVT_PAINT(HistogramPanel::OnPaint)
+ EVT_SIZE(HistogramPanel::OnSize)
+END_EVENT_TABLE()
+
+HistogramPanel::HistogramPanel(wxWindow *parent) :
+wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
+{
+ SetBackgroundColour(*wxBLACK);
+ m_mode = Rectangular;
+}
+
+void HistogramPanel::SetData(const ErrorHistogram &histogram)
+{
+ m_histogram = histogram; // Copy constructor invoked
+ Refresh();
+}
+
+void HistogramPanel::OnPaint(wxPaintEvent &event)
+{
+ wxPaintDC dc(this);
+ draw_panel(dc);
+}
+
+void HistogramPanel::draw_panel(wxDC &dc)
+{
+ const vector<float> &data = m_histogram.Data();
+ if (m_histogram.Count() == 0)
+ return;
+
+ if (m_mode == Polar) {
+ // Draw histogram bars
+ dc.SetPen(wxPen(*wxRED, 1, wxSOLID));
+ for (int i = 0; i < 360; i++) {
+ float len = data[i]*(m_extent*0.75-10)/m_histogram.ModalFrequency();
+ float radians = i*M_PI/180.0;
+ wxPoint tip = wxPoint((int)(m_center.x-sin(radians)*len),
+ (int)(m_center.y+cos(radians)*len));
+
+ dc.DrawLine(m_center, tip);
+ }
+ }
+ else if (m_mode == Rectangular) {
+ // Draw zero tick
+ dc.SetPen(wxPen(*wxWHITE, 1, wxSOLID));
+ dc.DrawLine(m_center.x, 0, m_center.x, 10);
+
+ // Draw mode tick
+ dc.SetPen(wxPen(*wxGREEN, 1, wxSOLID));
+ int mode = (int)((m_histogram.Mode()+180)/360.0*m_width);
+ dc.DrawLine(mode, 0, mode, 9);
+
+ // Draw histogram bars
+ dc.SetPen(wxPen(*wxRED, 1, wxSOLID));
+ float freq = m_histogram.ModalFrequency();
+ for (int i = 0; i < 360; i++) {
+ int len = (int)(data[i]/freq*(m_height-10));
+ int pos = (int)(i/360.0*m_width);
+ dc.DrawLine(pos, m_height, pos, m_height-len);
+ }
+ }
+}
+
+void HistogramPanel::OnSize(wxSizeEvent &event)
+{
+ GetClientSize(&m_width, &m_height);
+ m_center = wxPoint(m_width/2, (int)(m_height*0.75));
+
+ if (m_width > m_height)
+ m_extent = m_height;
+ else
+ m_extent = m_width;
+
+ Refresh();
+}
diff --git a/ezdop/src/host/hunter/src/histogram.h b/ezdop/src/host/hunter/src/histogram.h
new file mode 100644
index 000000000..99486ff31
--- /dev/null
+++ b/ezdop/src/host/hunter/src/histogram.h
@@ -0,0 +1,89 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __HISTOGRAM_H__
+#define __HISTOGRAM_H__
+
+// Application level includes
+#include "sample.h"
+
+// wxWidgets includes
+#include <wx/panel.h>
+
+// System level includes
+#include <vector>
+
+class ErrorHistogram
+{
+public:
+ ErrorHistogram();
+
+ void Reset();
+ void Calc(const std::vector<Sample> &samples, const Spherical &location);
+ void Add(float angle, float magnitude, float ierror, float qerror); // analytic errors are -pi to pi
+ void Normalize();
+
+ int Count() const { return m_count; }
+ int Mode() const { return m_mode; }
+ float ModalFrequency() const { return m_modal_frequency; }
+ float Mean() const { return m_mean; }
+ float Concentration() const { return m_conc; }
+ const std::vector<float> &Data() const { return m_bins; }
+
+private:
+ int m_mode;
+ float m_modal_frequency;
+ float m_isum;
+ float m_qsum;
+ float m_msum;
+ float m_conc;
+ float m_mean;
+ int m_count;
+ std::vector<float> m_bins;
+};
+
+class HistogramPanel : public wxPanel
+{
+public:
+ enum Mode { Rectangular, Polar };
+
+ HistogramPanel(wxWindow *parent);
+ void SetData(const ErrorHistogram &histogram);
+ void SetMode(Mode mode) { m_mode = mode; Refresh(); }
+
+ // Event handlers
+ void OnPaint(wxPaintEvent &event);
+ void OnSize(wxSizeEvent &event);
+
+private:
+ void draw_panel(wxDC &dc);
+ ErrorHistogram m_histogram;
+
+ // State
+ Mode m_mode;
+
+ // Window size derived parameters
+ wxPoint m_center;
+ int m_width;
+ int m_height;
+ int m_extent;
+
+ DECLARE_EVENT_TABLE();
+};
+
+#endif
diff --git a/ezdop/src/host/hunter/src/hunter.bmp b/ezdop/src/host/hunter/src/hunter.bmp
new file mode 100644
index 000000000..6d67dd46d
--- /dev/null
+++ b/ezdop/src/host/hunter/src/hunter.bmp
Binary files differ
diff --git a/ezdop/src/host/hunter/src/hunter.cc b/ezdop/src/host/hunter/src/hunter.cc
new file mode 100644
index 000000000..39b8325c8
--- /dev/null
+++ b/ezdop/src/host/hunter/src/hunter.cc
@@ -0,0 +1,1269 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application includes
+#include "hunter.h"
+#include "hunter.xpm"
+#include "doppler.h"
+#include "tactical.h"
+#include "calibrate.h"
+#include "settings.h"
+#include "gps.h"
+#include "serial.h"
+#include "search.h"
+#include "util.h"
+#include "spherical.h"
+#include "sample.h"
+#include "samplelog.h"
+#include "known.h"
+#include "histogram.h"
+
+// wxWidgets includes
+#include <wx/xrc/xmlres.h>
+#include <wx/wx.h> // Too many to do individually
+#include <wx/file.h> // hmmm, fails compile on mingw32 without it
+
+// Event table for HunterFrame
+BEGIN_EVENT_TABLE(HunterFrame, wxFrame)
+ // Application level events
+ EVT_CLOSE(HunterFrame::OnClose)
+
+ // File menu events
+ EVT_MENU(XRCID("file_new_menuitem"), HunterFrame::OnFileNew)
+ EVT_MENU(XRCID("file_open_menuitem"), HunterFrame::OnFileOpen)
+ EVT_MENU(XRCID("file_close_menuitem"), HunterFrame::OnFileClose)
+ EVT_MENU(XRCID("file_save_menuitem"), HunterFrame::OnFileSave)
+ EVT_MENU(XRCID("file_exit_menuitem"), HunterFrame::OnExit)
+
+ // Doppler menu events
+ EVT_MENU(XRCID("doppler_toggle_menuitem"), HunterFrame::OnDopplerToggle)
+ EVT_MENU(XRCID("doppler_autostart_menuitem"), HunterFrame::OnDopplerAutostart)
+ EVT_MENU(XRCID("doppler_reset_menuitem"), HunterFrame::OnDopplerReset)
+
+ // Calibration menu events
+ EVT_MENU(XRCID("calibration_savetofile_menuitem"), HunterFrame::OnCalibrationSaveToFile)
+ EVT_MENU(XRCID("calibration_loadfromfile_menuitem"), HunterFrame::OnCalibrationLoadFromFile)
+ EVT_MENU(XRCID("calibration_loadtransmitter_menuitem"), HunterFrame::OnCalibrationLoadTransmitter)
+ EVT_MENU(XRCID("calibration_savetransmitter_menuitem"), HunterFrame::OnCalibrationSaveTransmitter)
+
+ // About menu events
+ EVT_MENU(XRCID("about_menuitem"), HunterFrame::OnAbout)
+
+ // Status panel events
+ EVT_RADIOBOX(XRCID("statistics_source_radiobox"), HunterFrame::OnHistogramSourceChg)
+ EVT_RADIOBOX(XRCID("statistics_coords_radiobox"), HunterFrame::OnHistogramCoordsChg)
+
+ // Doppler tab events
+ EVT_BUTTON(XRCID("doppler_toggle_button"), HunterFrame::OnDopplerToggle)
+ EVT_CHECKBOX(XRCID("doppler_autostart_checkbox"), HunterFrame::OnDopplerAutostart)
+ EVT_BUTTON(XRCID("doppler_reset_button"), HunterFrame::OnDopplerReset)
+ EVT_COMMAND_SCROLL(XRCID("doppler_filter_slider"), HunterFrame::OnDopplerFilterChg)
+ EVT_RADIOBOX(XRCID("doppler_rotation_radiobox"), HunterFrame::OnDopplerRotationChg)
+ EVT_DOPPLER_UPDATE(HunterFrame::OnDopplerUpdate)
+
+ // GPS tab events
+ EVT_BUTTON(XRCID("gps_toggle_button"), HunterFrame::OnGPSToggle)
+ EVT_CHECKBOX(XRCID("gps_autostart_checkbox"), HunterFrame::OnGPSAutostart)
+ EVT_COMBOBOX(XRCID("gps_device_combobox"), HunterFrame::OnGPSDeviceSelect)
+ EVT_TEXT_ENTER(XRCID("gps_device_combobox"), HunterFrame::OnGPSDeviceSelect)
+ EVT_GPS_UPDATE(HunterFrame::OnGPSUpdate)
+
+ // Calibration tab events
+ EVT_BUTTON(XRCID("calibration_equalize_button"), HunterFrame::OnCalibrationEqualize)
+ EVT_BUTTON(XRCID("calibration_zero_button"), HunterFrame::OnCalibrationZero)
+ EVT_SPIN_UP(XRCID("calibration_adjust_spinner"), HunterFrame::OnCalibrationAdjustRight)
+ EVT_SPIN_DOWN(XRCID("calibration_adjust_spinner"), HunterFrame::OnCalibrationAdjustLeft)
+ EVT_CHECKBOX(XRCID("calibration_all_checkbox"), HunterFrame::OnCalibrationAffectAllRates)
+ EVT_BUTTON(XRCID("known_transmitter_update_button"), HunterFrame::OnKnownTransmitterUpdate)
+ EVT_CHECKBOX(XRCID("known_transmitter_checkbox"), HunterFrame::OnUseKnownTransmitter)
+
+ // Search tab events
+ EVT_BUTTON(XRCID("search_newsave_button"), HunterFrame::OnSearchNewSave)
+ EVT_BUTTON(XRCID("search_openclose_button"), HunterFrame::OnSearchOpenClose)
+ EVT_BUTTON(XRCID("search_toggle_button"), HunterFrame::OnSearchToggle)
+ EVT_BUTTON(XRCID("search_once_button"), HunterFrame::OnSearchOnce)
+ EVT_SEARCH_UPDATE(HunterFrame::OnSearchUpdate)
+
+ // Display tab events
+ EVT_RADIOBOX(XRCID("display_orientation_radiobox"), HunterFrame::OnDisplayOrientation)
+ EVT_CHECKBOX(XRCID("display_doppler_checkbox"), HunterFrame::OnDisplayDoppler)
+ EVT_CHECKBOX(XRCID("display_known_checkbox"), HunterFrame::OnDisplayKnown)
+ EVT_CHECKBOX(XRCID("display_estimated_checkbox"), HunterFrame::OnDisplayEstimated)
+
+END_EVENT_TABLE()
+
+HunterFrame::HunterFrame() :
+wxFrame(),
+m_search(this)
+{
+ m_settings = NULL;
+ m_doppler = NULL;
+ m_gps = NULL;
+ m_log = NULL;
+ m_tactical_panel = NULL;
+ m_error_histogram_panel = NULL;
+
+ m_doppler_started = false;
+ m_gps_started = false;
+ m_capture = false;
+ m_one_shot = false;
+ m_histogram_source = 0;
+
+ InitializeSettings();
+ InitializeWindows();
+ InitializeDoppler();
+ InitializeGPS();
+ InitializeCalibration();
+ InitializeSearch();
+ InitializeDisplay();
+}
+
+void HunterFrame::InitializeSettings()
+{
+ m_settings = new HunterSettings();
+ wxSetWorkingDirectory(m_settings->GetWorkingDirectory());
+}
+
+void HunterFrame::InitializeWindows()
+{
+ // Build main window controls
+ bool loaded = wxXmlResource::Get()->LoadFrame(this, NULL, wxT("main_frame"));
+ wxASSERT(loaded);
+
+ // Hack until XRC understands <gravity> for wxSplitterWindow!!
+ XRCCTRL(*this, "horiz_splitter", wxSplitterWindow)->SetSashGravity(1.0);
+
+ // Increase font size for better visibility in certain text displays
+ wxFont font = XRCCTRL(*this, "doppler_relative_bearing_text", wxStaticText)->GetFont();
+ float points = font.GetPointSize()*2.0;
+ font.SetPointSize((int)points);
+ XRCCTRL(*this, "doppler_relative_bearing_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "doppler_absolute_bearing_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "transmitter_estimated_bearing_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "transmitter_estimated_range_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "transmitter_actual_bearing_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "transmitter_actual_range_text", wxStaticText)->SetFont(font);
+ points *= 0.75;
+ font.SetPointSize((int)points);
+ XRCCTRL(*this, "gps_latitude_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "gps_longitude_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "gps_heading_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "gps_speed_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "search_latitude_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "search_longitude_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "search_count_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "search_status_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "search_mode_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "search_mean_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "search_score_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "search_disterror_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "error_count_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "error_mode_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "error_mean_text", wxStaticText)->SetFont(font);
+ XRCCTRL(*this, "error_conc_text", wxStaticText)->SetFont(font);
+
+ // Create the menu
+ SetMenuBar(wxXmlResource::Get()->LoadMenuBar(wxT("main_menu")));
+ CreateStatusBar(1);
+ SetIcon(wxIcon(hunter_xpm));
+
+ // Restore saved window size and position
+ int x = m_settings->GetWindowXPos();
+ int y = m_settings->GetWindowYPos();
+ wxSize size = m_settings->GetWindowSize();
+ int hsplitpos = size.GetHeight()-170; // HACK!
+ SetSize(x, y, size.GetWidth(), size.GetHeight());
+ XRCCTRL(*this, "horiz_splitter", wxSplitterWindow)->SetSashPosition(hsplitpos);
+
+ // Error histogram is a custom control outside XRC system
+ m_error_histogram_panel = new HistogramPanel(this);
+ wxXmlResource::Get()->AttachUnknownControl(wxT("error_histogram_panel"),
+ m_error_histogram_panel, this);
+}
+
+void HunterFrame::InitializeDoppler()
+{
+ m_doppler = new EZDoppler(this);
+ m_doppler->Initialize();
+ m_doppler_started = false;
+ SetDopplerParams();
+}
+
+void HunterFrame::InitializeGPS()
+{
+ m_gps = new GPS(this);
+ m_gps_started = false;
+
+ // Populate device selection combobox
+ XRCCTRL(*this, "gps_device_combobox", wxComboBox)->Clear();
+ wxArrayString ports = EnumerateSerialPorts();
+ for (int i = 0; i < ports.GetCount(); i++)
+ XRCCTRL(*this, "gps_device_combobox", wxComboBox)->Append(ports[i]);
+ XRCCTRL(*this, "gps_device_combobox", wxComboBox)->SetValue(m_settings->GetGPSDeviceName());
+
+ // GPS autostart
+ if (m_settings->GetGPSAutostart()) {
+ XRCCTRL(*this, "gps_autostart_checkbox", wxCheckBox)->SetValue(true);
+ StartGPS();
+ }
+}
+
+void HunterFrame::InitializeCalibration()
+{
+ XRCCTRL(*this, "calibration_all_checkbox", wxCheckBox)->SetValue(m_settings->GetCalibrationAffectAllRates());
+
+ if (m_settings->GetUseKnownTransmitter()) {
+ m_known.Location(Spherical(m_settings->GetKnownTransmitterLatitude(),
+ m_settings->GetKnownTransmitterLongitude()));
+ XRCCTRL(*this, "known_transmitter_checkbox", wxCheckBox)->SetValue(true);
+ }
+
+ UpdateKnownLocation();
+}
+
+void HunterFrame::InitializeDisplay()
+{
+ // Tactical display is a custom control outside XRC system
+ m_tactical_panel = new TacticalPanel(this);
+ wxXmlResource::Get()->AttachUnknownControl(wxT("tactical_panel"),
+ m_tactical_panel, this);
+
+ if (m_settings->GetDisplayOrientation()) {
+ XRCCTRL(*this, "display_orientation_radiobox", wxRadioBox)->SetSelection(1);
+ m_tactical_panel->SetOrientation(NorthUp);
+ }
+
+ if (m_settings->GetDisplayDoppler()) {
+ XRCCTRL(*this, "display_doppler_checkbox", wxCheckBox)->SetValue(true);
+ m_tactical_panel->SetDisplayDoppler(true);
+ }
+
+ if (m_settings->GetDisplayKnown()) {
+ XRCCTRL(*this, "display_known_checkbox", wxCheckBox)->SetValue(true);
+ m_tactical_panel->SetDisplayKnown(true);
+ }
+
+ if (m_settings->GetDisplayEstimated()) {
+ XRCCTRL(*this, "display_estimated_checkbox", wxCheckBox)->SetValue(true);
+ m_tactical_panel->SetDisplayEstimated(true);
+ }
+}
+
+void HunterFrame::InitializeSearch()
+{
+ EnableSearchItems(false);
+}
+
+void HunterFrame::SetDopplerParams()
+{
+ // NOTE: This is not in InitializeDoppler() as it needs to be called
+ // separately when resetting Doppler
+
+ // Adjust windows based on device status
+ if (!m_doppler->IsOnline()) {
+ // Disable all GUI elements associated with Doppler
+
+ // Doppler control tab
+ XRCCTRL(*this, "doppler_control_panel", wxPanel)->Disable();
+
+ // Doppler menu items
+ this->GetMenuBar()->FindItem(XRCID("doppler_toggle_menuitem"))->Enable(false);
+ this->GetMenuBar()->FindItem(XRCID("doppler_autostart_menuitem"))->Enable(false);
+ this->GetMenuBar()->FindItem(XRCID("doppler_reset_menuitem"))->Enable(false);
+
+ // Calibration control tab
+ XRCCTRL(*this, "calibration_equalize_button", wxButton)->Enable(false);
+ XRCCTRL(*this, "calibration_zero_button", wxButton)->Enable(false);
+ XRCCTRL(*this, "calibration_adjust_spinner", wxSpinButton)->Enable(false);
+
+ return;
+ }
+
+ // Doppler filter level
+ int filter = m_settings->GetDopplerFilter();
+ XRCCTRL(*this, "doppler_filter_slider", wxSlider)->SetValue(filter);
+ wxScrollEvent dummy;
+ OnDopplerFilterChg(dummy);
+
+ // Doppler rotation rate
+ int rate = m_settings->GetDopplerRotation();
+ XRCCTRL(*this, "doppler_rotation_radiobox", wxRadioBox)->SetSelection(rate);
+ wxCommandEvent dummy2;
+ OnDopplerRotationChg(dummy2);
+
+ // Doppler calibration values
+ for (int i=0; i < 7; i++) { // i==6 gets zero offset
+ float offset = m_settings->GetDopplerCalibration(i);
+ m_doppler->SetCalibration(i, offset);
+ }
+
+ // Doppler autostart
+ if (m_settings->GetDopplerAutostart()) {
+ this->GetMenuBar()->FindItem(XRCID("doppler_autostart_menuitem"))->Check(true);
+ XRCCTRL(*this, "doppler_autostart_checkbox", wxCheckBox)->SetValue(true);
+ StartDoppler();
+ }
+}
+
+void HunterFrame::StartDoppler()
+{
+ m_doppler->Start();
+ m_doppler_started = true;
+ XRCCTRL(*this, "doppler_toggle_button", wxButton)->SetLabel(_T("Stop"));
+ XRCCTRL(*this, "calibration_equalize_button", wxButton)->Enable();
+ XRCCTRL(*this, "calibration_zero_button", wxButton)->Enable();
+ XRCCTRL(*this, "calibration_adjust_spinner", wxSpinButton)->Enable();
+ this->GetMenuBar()->FindItem(XRCID("doppler_toggle_menuitem"))->SetText(_T("&Stop"));
+ this->GetMenuBar()->FindItem(XRCID("doppler_reset_menuitem"))->Enable(true);
+}
+
+void HunterFrame::StopDoppler()
+{
+ m_doppler->Stop();
+ m_doppler_started = false;
+ UpdateDopplerStatus(false);
+ XRCCTRL(*this, "doppler_toggle_button", wxButton)->SetLabel(_T("Start"));
+ this->GetMenuBar()->FindItem(XRCID("doppler_toggle_menuitem"))->SetText(_("&Start"));
+ this->GetMenuBar()->FindItem(XRCID("doppler_reset_menuitem"))->Enable(false);
+ XRCCTRL(*this, "calibration_equalize_button", wxButton)->Disable();
+ XRCCTRL(*this, "calibration_zero_button", wxButton)->Disable();
+ XRCCTRL(*this, "calibration_adjust_spinner", wxSpinButton)->Disable();
+}
+
+void HunterFrame::StartGPS()
+{
+ wxString port = XRCCTRL(*this, "gps_device_combobox", wxComboBox)->GetValue();
+ if (!m_gps->Start(port)) {
+ wxMessageDialog(this, wxT("Failed to start GPS!"), wxT("GPS Error"),
+ wxOK|wxICON_ERROR).ShowModal();
+ return;
+ }
+
+ m_gps_started = true;
+ XRCCTRL(*this, "gps_toggle_button", wxButton)->SetLabel(_T("Stop"));
+ XRCCTRL(*this, "gps_device_combobox", wxComboBox)->Disable();
+ XRCCTRL(*this, "display_orientation_radiobox", wxRadioBox)->Enable();
+ if (XRCCTRL(*this, "display_orientation_radiobox", wxRadioBox)->GetSelection() == 1) {
+ m_tactical_panel->SetOrientation(NorthUp);
+ }
+
+ if (m_search.HasSolution())
+ UpdateSearchDirection(true);
+
+ UpdateKnownDirection();
+ UpdateKnownStatistics();
+}
+
+void HunterFrame::StopGPS()
+{
+ m_gps->Stop();
+ m_gps_started = false;
+ m_tactical_panel->SetOrientation(TrackUp);
+ m_tactical_panel->SetActualBearing(-1.0);
+ m_tactical_panel->SetEstimatedBearing(-1.0);
+ XRCCTRL(*this, "gps_toggle_button", wxButton)->SetLabel(_T("Start"));
+ XRCCTRL(*this, "gps_device_combobox", wxComboBox)->Enable();
+ XRCCTRL(*this, "display_orientation_radiobox", wxRadioBox)->Disable();
+
+ UpdateGPSStatus(false);
+ UpdateSearchDirection(false);
+ UpdateKnownDirection();
+ UpdateKnownStatistics();
+
+ // Note: can't replace with call to UpdateDopplerStatus as we only want to clear one field
+ XRCCTRL(*this, "doppler_absolute_bearing_text", wxStaticText)->SetLabel(_T(""));
+}
+
+void HunterFrame::EnableSearchItems(bool enable)
+{
+ XRCCTRL(*this, "search_toggle_button", wxButton)->Enable(enable);
+ XRCCTRL(*this, "search_once_button", wxButton)->Enable(enable);
+
+ // These fields will get populated when samples come in
+ UpdateSearchStatus(false);
+ UpdateSearchLocation(false);
+ UpdateSearchDirection(false);
+
+ this->GetMenuBar()->FindItem(XRCID("file_save_menuitem"))->Enable(enable);
+ this->GetMenuBar()->FindItem(XRCID("file_close_menuitem"))->Enable(enable);
+
+ if (!enable) {
+ XRCCTRL(*this, "search_toggle_button", wxButton)->SetLabel(_T("Stop"));
+ XRCCTRL(*this, "search_toggle_button", wxButton)->SetLabel(_T("Automatic"));
+ }
+}
+
+void HunterFrame::OnClose(wxCloseEvent &event)
+{
+ wxCommandEvent dummy;
+
+ // Cleanup for this scope should be done here
+ // TODO: factor out into methods corresponding to start up initialization
+
+ // Save window size and position
+ int x, y;
+ GetPosition(&x, &y);
+ m_settings->SetWindowXPos(x);
+ m_settings->SetWindowYPos(y);
+ m_settings->SetWindowSize(GetSize());
+
+ wxASSERT(m_doppler != NULL);
+ if (m_doppler_started)
+ StopDoppler();
+ m_doppler->Finalize();
+ if (m_gps_started)
+ StopGPS();
+
+ if (m_log)
+ OnFileClose(dummy);
+
+
+ m_settings->SetWorkingDirectory(wxGetCwd());
+
+ delete m_doppler;
+ delete m_gps;
+ delete m_settings;
+ if (m_log)
+ delete m_log;
+
+ // Last thing to do
+ Destroy();
+}
+
+void HunterFrame::OnExit(wxCommandEvent &event)
+{
+ // Cleanup for this scope should be done in ::OnClose, not here. This
+ // method is not called when exiting from the system menu, but rather it
+ // goes straight to ::OnClose
+
+ // Sends close event
+ this->Close();
+}
+
+void HunterFrame::OnAbout(wxCommandEvent &event)
+{
+ wxMessageBox(wxT("Copyright(C) 2005 Johnathan Corgan"),
+ wxT("About AE6HO Radio Location System"),
+ wxOK | wxICON_INFORMATION, this);
+}
+
+void HunterFrame::OnDopplerToggle(wxCommandEvent &event)
+{
+ if (m_doppler_started)
+ StopDoppler();
+ else
+ StartDoppler();
+}
+
+void HunterFrame::OnDopplerAutostart(wxCommandEvent &event)
+{
+ bool autostart = event.IsChecked();
+ this->GetMenuBar()->FindItem(XRCID("doppler_autostart_menuitem"))->Check(autostart);
+ XRCCTRL(*this, "doppler_autostart_checkbox", wxCheckBox)->SetValue(autostart);
+ m_settings->SetDopplerAutostart(autostart);
+}
+
+void HunterFrame::OnDopplerReset(wxCommandEvent &event)
+{
+ StopDoppler();
+ m_doppler->Reset();
+ SetDopplerParams(); // restarts Doppler if autostart is configured
+}
+
+void HunterFrame::OnDopplerFilterChg(wxScrollEvent &event)
+{
+ int n = XRCCTRL(*this, "doppler_filter_slider", wxSlider)->GetValue();
+ m_doppler->SetFilter(n);
+ m_settings->SetDopplerFilter(n);
+}
+
+void HunterFrame::OnDopplerRotationChg(wxCommandEvent &event)
+{
+ int n = XRCCTRL(*this, "doppler_rotation_radiobox", wxRadioBox)->GetSelection();
+ m_doppler->SelectRotationRate(n);
+ m_settings->SetDopplerRotation(n);
+}
+
+void HunterFrame::OnDopplerUpdate(EZDopplerUpdate &event)
+{
+ if (!m_doppler_started) // Spurious event after doppler stopped
+ return;
+
+ // Update current state variables
+ m_sample.Volume(event.m_volume);
+ m_sample.InPhase(event.m_in_phase);
+ m_sample.Quadrature(event.m_quadrature);
+ m_sample.Strength(sqrt(event.m_in_phase*event.m_in_phase +
+ event.m_quadrature*event.m_quadrature));
+ m_sample.Phase(atan2(event.m_quadrature, event.m_in_phase));
+
+ UpdateDopplerStatus(true);
+
+ if (m_log && m_gps_started && m_capture &&
+ m_sample.Speed() >= 5.0 && m_sample.Valid()) {
+ m_log->Add(m_sample);
+ if (m_one_shot == true) {
+ StopCapture();
+ CalcSolution();
+ if (m_search.HasSolution()) {
+ UpdateSearchStatus(true);
+ UpdateSearchDirection(true);
+ }
+ }
+ }
+}
+
+void HunterFrame::UpdateDopplerStatus(bool display)
+{
+ wxString str;
+ if (!display) {
+ m_tactical_panel->SetDopplerBearing(-1.0);
+ XRCCTRL(*this, "doppler_level_gauge", wxGauge)->SetValue(0);
+ XRCCTRL(*this, "doppler_audio_gauge", wxGauge)->SetValue(0);
+ XRCCTRL(*this, "doppler_relative_bearing_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "doppler_absolute_bearing_text", wxStaticText)->SetLabel(_T(""));
+ return;
+ }
+
+ float display_relative_bearing = degree_normalize(to_degrees(m_sample.Phase()));
+ if (m_tactical_panel)
+ m_tactical_panel->SetDopplerBearing(display_relative_bearing);
+
+ int display_magnitude = (int)limit((m_sample.Strength()*200.0), 0.0, 100.0);
+ XRCCTRL(*this, "doppler_level_gauge", wxGauge)->SetValue(display_magnitude);
+
+ int display_amplitude = (int)limit((m_sample.Volume()*100.0), 0.0, 100.0);
+ XRCCTRL(*this, "doppler_audio_gauge", wxGauge)->SetValue(display_amplitude);
+
+ str.Printf(_T("%03i"), degree_normalize((int)(display_relative_bearing+0.5))); // So zero is from -0.5 to 0.5
+ XRCCTRL(*this, "doppler_relative_bearing_text", wxStaticText)->SetLabel(str);
+
+ if (m_gps_started) {
+ str.Printf(_T("%03i"), degree_normalize((int)(m_sample.Heading()+display_relative_bearing)));
+ XRCCTRL(*this, "doppler_absolute_bearing_text", wxStaticText)->SetLabel(str);
+ }
+}
+
+void HunterFrame::CalcKnownStatistics()
+{
+ if (m_log)
+ m_known.Calc(m_log->Samples());
+
+ UpdateKnownStatistics();
+}
+
+void HunterFrame::UpdateKnownStatistics()
+{
+ if (m_error_histogram_panel && m_histogram_source == 1)
+ m_error_histogram_panel->SetData(m_known.Histogram());
+
+ if (!m_known.HasStats()) {
+ XRCCTRL(*this, "error_count_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "error_mode_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "error_mean_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "error_conc_text", wxStaticText)->SetLabel(_T(""));
+ return;
+ }
+
+ wxString str;
+
+ str.Printf(_T("%i"), m_known.Count());
+ XRCCTRL(*this, "error_count_text", wxStaticText)->SetLabel(str);
+
+ str.Printf(_T("%i"), m_known.Mode());
+ XRCCTRL(*this, "error_mode_text", wxStaticText)->SetLabel(str);
+
+ str.Printf(_T("%5.1f"), m_known.Mean());
+ XRCCTRL(*this, "error_mean_text", wxStaticText)->SetLabel(str);
+
+ str.Printf(_T("%1.3f"), m_known.Concentration());
+ XRCCTRL(*this, "error_conc_text", wxStaticText)->SetLabel(str);
+}
+
+void HunterFrame::OnCalibrationEqualize(wxCommandEvent &event)
+{
+ CalibrationDialog *dlg = new CalibrationDialog(this);
+ dlg->ShowModal();
+}
+
+void HunterFrame::DoCalibrationStep(int which)
+{
+ static int delay;
+
+ if (which == 0) { // Set up doppler
+ delay = XRCCTRL(*this, "doppler_filter_slider", wxSlider)->GetValue()/3; // Empirically determined
+ if (delay == 0)
+ delay = 1;
+ }
+
+ // Set rotation rate for this step
+ wxCommandEvent dummy;
+ XRCCTRL(*this, "doppler_rotation_radiobox", wxRadioBox)->SetSelection(which);
+ OnDopplerRotationChg(dummy);
+
+ // Wait until stable value
+ wxStartTimer();
+ while(wxGetElapsedTime(false) < 1000*delay)
+ wxYield(); // eeewwww!
+
+ // Now stable reading can be calibrated
+ m_doppler->Calibrate(-M_PI); // Sets to zero
+
+ float offset=m_doppler->GetCalibration(which);
+ m_settings->SetDopplerCalibration(which, offset);
+}
+
+void HunterFrame::OnCalibrationZero(wxCommandEvent &event)
+{
+ m_doppler->SetOffset();
+ float offset=m_doppler->GetCalibration(6);
+ m_settings->SetDopplerCalibration(6, offset);
+}
+
+void HunterFrame::OnCalibrationAdjustLeft(wxSpinEvent &event)
+{
+ if (m_settings->GetCalibrationAffectAllRates())
+ m_doppler->NudgeAll(to_radians(-0.5));
+ else
+ m_doppler->Nudge(to_radians(-0.5));
+
+ float offset=m_doppler->GetCalibration(6);
+ m_settings->SetDopplerCalibration(6, offset);
+}
+
+void HunterFrame::OnCalibrationAdjustRight(wxSpinEvent &event)
+{
+ if (m_settings->GetCalibrationAffectAllRates())
+ m_doppler->NudgeAll(to_radians(0.5));
+ else
+ m_doppler->Nudge(to_radians(0.5));
+
+ float offset=m_doppler->GetCalibration(6);
+ m_settings->SetDopplerCalibration(6, offset);
+}
+
+void HunterFrame::OnCalibrationAffectAllRates(wxCommandEvent &event)
+{
+ bool affect = event.IsChecked();
+ XRCCTRL(*this, "calibration_all_checkbox", wxCheckBox)->SetValue(affect);
+ m_settings->SetCalibrationAffectAllRates(affect);
+}
+
+void HunterFrame::OnGPSToggle(wxCommandEvent &event)
+{
+ if (m_gps_started)
+ StopGPS();
+ else
+ StartGPS();
+}
+
+void HunterFrame::OnGPSAutostart(wxCommandEvent &event)
+{
+ bool start = XRCCTRL(*this, "gps_autostart_checkbox", wxCheckBox)->GetValue();
+ m_settings->SetGPSAutostart(start);
+}
+
+void HunterFrame::OnGPSDeviceSelect(wxCommandEvent &event)
+{
+ wxString device = XRCCTRL(*this, "gps_device_combobox", wxComboBox)->GetValue();
+ m_settings->SetGPSDeviceName(device);
+}
+
+void HunterFrame::OnGPSUpdate(GPSUpdate &update)
+{
+ // Update state variables
+ GPRMC *gprmc = update.m_gprmc;
+ m_sample.Valid(update.m_gprmc->m_valid);
+ m_sample.Time(update.m_gprmc->m_stamp);
+ m_sample.Location(update.m_gprmc->m_fix);
+ m_sample.Heading(update.m_gprmc->m_heading);
+ m_sample.Speed(update.m_gprmc->m_speed*1.15077945); // Conversion from knots to mph
+ m_sample.Rate(XRCCTRL(*this, "doppler_rotation_radiobox", wxRadioBox)->GetSelection());
+ m_sample.Filtering(XRCCTRL(*this, "doppler_filter_slider", wxSlider)->GetValue());
+
+ UpdateGPSValidity(update.m_gprmc->m_valid); // Colors red for invalid, black for valid
+ UpdateGPSStatus(true); // gps lat, lon, heading, speed
+ UpdateKnownDirection(); // actual bearing and range
+
+ CalcKnownStatistics();
+ if (m_capture)
+ CalcSolution();
+ if (m_search.HasSolution())
+ UpdateSearchDirection(true);
+
+ delete update.m_gprmc;
+}
+
+void HunterFrame::CalcSolution()
+{
+ GetStatusBar()->SetStatusText(wxT("Searching..."));
+ m_search.Solve(m_log);
+}
+
+void HunterFrame::OnSearchUpdate(SearchUpdate &update)
+{
+ if (update.m_done)
+ GetStatusBar()->SetStatusText(wxT(""));
+
+ if (m_search.HasSolution()) {
+ UpdateSearchStatus(true);
+ UpdateSearchLocation(true);
+ if (m_gps_started)
+ UpdateSearchDirection(true);
+ }
+}
+
+void HunterFrame::UpdateSearchStatus(bool display)
+{
+ wxString str;
+
+ if (m_error_histogram_panel && m_histogram_source == 0)
+ m_error_histogram_panel->SetData(m_search.Histogram());
+
+ if (!display) {
+ XRCCTRL(*this, "search_count_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "search_status_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "search_mode_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "search_mean_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "search_score_text", wxStaticText)->SetLabel(_T(""));
+ return;
+ }
+
+ str.Printf(_T("%i"), m_log->Count());
+ XRCCTRL(*this, "search_count_text", wxStaticText)->SetLabel(str);
+
+ str.Printf(_T("%s"), m_search.Busy() ? "BUSY" : "");
+ XRCCTRL(*this, "search_status_text", wxStaticText)->SetLabel(str);
+
+ str.Printf(_T("%i"), m_search.Mode());
+ XRCCTRL(*this, "search_mode_text", wxStaticText)->SetLabel(str);
+
+ str.Printf(_T("%6.1f"), m_search.Mean());
+ XRCCTRL(*this, "search_mean_text", wxStaticText)->SetLabel(str);
+
+ str.Printf(_T("%i"), (int)(m_search.Concentration()*100.0));
+ XRCCTRL(*this, "search_score_text", wxStaticText)->SetLabel(str);
+}
+
+void HunterFrame::UpdateGPSStatus(bool display)
+{
+ wxString str;
+
+ if (!display) {
+ XRCCTRL(*this, "gps_latitude_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "gps_longitude_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "gps_heading_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "gps_speed_text", wxStaticText)->SetLabel(_T(""));
+ return;
+ }
+
+ // Calculate latitude to display
+ str.Printf(_T("%1.5lf"), fabs(m_sample.Latitude()));
+ str.Append(m_sample.Latitude() < 0.0 ? _T("S") : _T("N"));
+ XRCCTRL(*this, "gps_latitude_text", wxStaticText)->SetLabel(str);
+
+ // Calculate longitude to display
+ str.Printf(_T("%1.5lf"), fabs(m_sample.Longitude()));
+ str.Append(m_sample.Longitude() < 0.0 ? _T("W") : _T("E"));
+ XRCCTRL(*this, "gps_longitude_text", wxStaticText)->SetLabel(str);
+
+ // Calculate heading to display
+ m_tactical_panel->SetHeading(m_sample.Heading());
+ str.Printf(_T("%03i"), (unsigned int)(m_sample.Heading()));
+ XRCCTRL(*this, "gps_heading_text", wxStaticText)->SetLabel(str);
+
+ // Calculate speed to display
+ str.Printf(_T("%i"), (unsigned int)(m_sample.Speed()));
+ XRCCTRL(*this, "gps_speed_text", wxStaticText)->SetLabel(str);
+}
+
+void HunterFrame::UpdateSearchDirection(bool display)
+{
+ wxString str;
+
+ if (!display) {
+ XRCCTRL(*this, "transmitter_estimated_bearing_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "transmitter_estimated_range_text", wxStaticText)->SetLabel(_T(""));
+ if (m_tactical_panel)
+ m_tactical_panel->SetEstimatedBearing(-1.0);
+ return;
+ }
+
+ float estimated_bearing =
+ bearing(m_sample.Location(), m_search.GetEstimatedLocation());
+
+ float estimated_range =
+ range(m_sample.Location(), m_search.GetEstimatedLocation());
+
+ m_tactical_panel->SetEstimatedBearing(estimated_bearing);
+ str.Printf(_T("%03i"), degree_normalize((int)estimated_bearing));
+ XRCCTRL(*this, "transmitter_estimated_bearing_text", wxStaticText)->SetLabel(str);
+ if (estimated_range > 99.9)
+ str.Printf(_T("%1.0f"), estimated_range);
+ else if (estimated_range > 9.99)
+ str.Printf(_T("%1.1f"), estimated_range);
+ else
+ str.Printf(_T("%1.2f"), estimated_range);
+ XRCCTRL(*this, "transmitter_estimated_range_text", wxStaticText)->SetLabel(str);
+}
+
+void HunterFrame::UpdateSearchLocation(bool display)
+{
+ wxString str;
+
+ if (!display) {
+ XRCCTRL(*this, "search_latitude_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "search_longitude_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "search_disterror_text", wxStaticText)->SetLabel(_T(""));
+ return;
+ }
+
+ Spherical estimated_location = m_search.GetEstimatedLocation();
+
+ // Calculate latitude to display
+ str.Printf(_T("%1.5f"), fabs(estimated_location.Latitude()));
+ str.Append(estimated_location.Latitude() < 0.0 ? _T("S") : _T("N"));
+ XRCCTRL(*this, "search_latitude_text", wxStaticText)->SetLabel(str);
+
+ // Calculate longitude to display
+ str.Printf(_T("%1.5f"), fabs(estimated_location.Longitude()));
+ str.Append(estimated_location.Longitude() < 0.0 ? _T("W") : _T("E"));
+ XRCCTRL(*this, "search_longitude_text", wxStaticText)->SetLabel(str);
+
+ if (m_known.IsSet()) {
+ float distance_error = range(m_search.GetEstimatedLocation(), m_known.Location());
+ if (distance_error > 99.9)
+ str.Printf(_T("%1.0f"), distance_error);
+ else if (distance_error > 9.99)
+ str.Printf(_T("%1.1f"), distance_error);
+ else
+ str.Printf(_T("%1.2f"), distance_error);
+ XRCCTRL(*this, "search_disterror_text", wxStaticText)->SetLabel(str);
+ }
+}
+
+void HunterFrame::UpdateKnownDirection()
+{
+ wxString str;
+
+ if (!m_known.IsSet() || !m_gps_started) {
+ XRCCTRL(*this, "transmitter_actual_bearing_text", wxStaticText)->SetLabel(_T(""));
+ XRCCTRL(*this, "transmitter_actual_range_text", wxStaticText)->SetLabel(_T(""));
+ if (m_tactical_panel)
+ m_tactical_panel->SetActualBearing(-1.0);
+ return;
+ }
+
+ float actual_bearing = bearing(m_sample.Location(), m_known.Location());
+ float actual_range = range(m_sample.Location(), m_known.Location());
+
+ m_tactical_panel->SetActualBearing(actual_bearing);
+
+ str.Printf(_T("%03i"), degree_normalize((int)actual_bearing));
+ XRCCTRL(*this, "transmitter_actual_bearing_text", wxStaticText)->SetLabel(str);
+ if (actual_range > 99.9)
+ str.Printf(_T("%1.0f"), actual_range);
+ else if (actual_range > 9.99)
+ str.Printf(_T("%1.1f"), actual_range);
+ else
+ str.Printf(_T("%1.2f"), actual_range);
+ XRCCTRL(*this, "transmitter_actual_range_text", wxStaticText)->SetLabel(str);
+}
+
+void HunterFrame::UpdateKnownLocation()
+{
+ wxString str;
+
+ if (!m_known.IsSet()) {
+ XRCCTRL(*this, "known_transmitter_latitude_edit", wxTextCtrl)->SetValue(_T(""));
+ XRCCTRL(*this, "known_transmitter_longitude_edit", wxTextCtrl)->SetValue(_T(""));
+ XRCCTRL(*this, "search_disterror_text", wxStaticText)->SetLabel(_T(""));
+ return;
+ }
+
+ str.Printf(_T("%1.5f"), m_known.Latitude());
+ XRCCTRL(*this, "known_transmitter_latitude_edit", wxTextCtrl)->SetValue(str);
+ str.Printf(_T("%1.5f"), m_known.Longitude());
+ XRCCTRL(*this, "known_transmitter_longitude_edit", wxTextCtrl)->SetValue(str);
+ UpdateSearchLocation(true); // to update the known error
+}
+
+void HunterFrame::OnKnownTransmitterUpdate(wxCommandEvent &event)
+{
+ // Note: this is an event handler for a calibration tab button
+ wxString lat_text = XRCCTRL(*this, "known_transmitter_latitude_edit", wxTextCtrl)->GetValue();
+ wxString lon_text = XRCCTRL(*this, "known_transmitter_longitude_edit", wxTextCtrl)->GetValue();
+
+ double lat = 0.0, lon = 0.0;
+
+ // TODO: use Spherical constructor/exceptions
+ if (!lat_text.ToDouble(&lat) || lat > 90.0 || lat < -90.0) {
+ wxMessageDialog(this, wxT("Invalid latitude entered."), wxT("Data Error"),
+ wxOK|wxICON_ERROR).ShowModal();
+ return;
+ }
+
+ if (!lon_text.ToDouble(&lon) || lon >180.0 || lon < -180.0) {
+ wxMessageDialog(this, wxT("Invalid longitude entered."), wxT("Data Error"),
+ wxOK|wxICON_ERROR).ShowModal();
+ return;
+ }
+
+ m_known.Location(Spherical(lat, lon));
+ CalcKnownStatistics();
+
+ m_settings->SetKnownTransmitterLongitude(lon);
+ m_settings->SetKnownTransmitterLatitude(lat);
+
+ UpdateKnownLocation();
+ if (m_gps_started)
+ UpdateKnownDirection();
+}
+
+void HunterFrame::OnUseKnownTransmitter(wxCommandEvent &event)
+{
+ if (event.IsChecked())
+ m_known.Location(Spherical(m_settings->GetKnownTransmitterLatitude(),
+ m_settings->GetKnownTransmitterLongitude()));
+ else
+ m_known.Clear();
+
+ m_settings->SetUseKnownTransmitter(m_known.IsSet());
+ CalcKnownStatistics();
+ UpdateKnownLocation();
+ if (m_gps_started)
+ UpdateKnownDirection();
+}
+
+void HunterFrame::UpdateGPSValidity(bool valid)
+{
+ m_sample.Valid(valid);
+
+ wxColour color = *wxBLACK;
+ if (!valid)
+ color = *wxRED;
+
+ XRCCTRL(*this, "doppler_absolute_bearing_text", wxStaticText)->SetForegroundColour(color);
+ XRCCTRL(*this, "transmitter_actual_bearing_text", wxStaticText)->SetForegroundColour(color);
+ XRCCTRL(*this, "transmitter_actual_range_text", wxStaticText)->SetForegroundColour(color);
+ XRCCTRL(*this, "gps_latitude_text", wxStaticText)->SetForegroundColour(color);
+ XRCCTRL(*this, "gps_longitude_text", wxStaticText)->SetForegroundColour(color);
+ XRCCTRL(*this, "gps_heading_text", wxStaticText)->SetForegroundColour(color);
+ XRCCTRL(*this, "gps_speed_text", wxStaticText)->SetForegroundColour(color);
+}
+
+void HunterFrame::OnFileNew(wxCommandEvent &event)
+{
+ if (m_log) {
+ wxLogDebug(_T("Not Implemented: current log is discarded without saving..."));
+ StopCapture();
+ delete m_log;
+ m_search.Reset();
+ m_log = NULL;
+ }
+
+ MakeNewLogAndSearch();
+ this->SetTitle(wxT("AE6HO Radio Location System - Unsaved Search")); // refactor into EnableSearchItems()
+ XRCCTRL(*this, "search_newsave_button", wxButton)->SetLabel(_T("Save"));
+ XRCCTRL(*this, "search_openclose_button", wxButton)->SetLabel(_T("Close"));
+ this->GetMenuBar()->FindItem(XRCID("file_new_menuitem"))->Enable(false);
+ this->GetMenuBar()->FindItem(XRCID("file_open_menuitem"))->Enable(false);
+ EnableSearchItems(true);
+}
+
+void HunterFrame::MakeNewLogAndSearch()
+{
+ m_log = new SampleLog();
+ m_search.Reset();
+}
+
+void HunterFrame::OnFileOpen(wxCommandEvent &event)
+{
+ wxString filename;
+
+ if (m_log)
+ OnFileClose(event);
+
+ wxFileDialog dlg(this, wxT("Open Log File"), _T("."), _T(""), _T("*.dat"),
+ wxOPEN|wxFILE_MUST_EXIST|wxCHANGE_DIR);
+ if (dlg.ShowModal() == wxID_OK) {
+ this->GetMenuBar()->FindItem(XRCID("file_new_menuitem"))->Enable(false);
+ this->GetMenuBar()->FindItem(XRCID("file_open_menuitem"))->Enable(false);
+ filename = dlg.GetPath();
+ MakeNewLogAndSearch();
+ m_log->Load(filename);
+ this->SetTitle(wxT("AE6HO Radio Location System - ")+filename);
+ XRCCTRL(*this, "search_newsave_button", wxButton)->SetLabel(_T("Save"));
+ XRCCTRL(*this, "search_openclose_button", wxButton)->SetLabel(_T("Close"));
+ EnableSearchItems(true);
+ CalcKnownStatistics();
+ CalcSolution();
+ }
+}
+
+void HunterFrame::OnFileSave(wxCommandEvent &event)
+{
+ wxASSERT(m_log);
+
+ wxString filename;
+ if (!m_log->HasFile()) {
+ wxFileDialog dlg(this, wxT("Save Search Data"), _T("."), _T(""), _T("*.dat"),
+ wxSAVE|wxOVERWRITE_PROMPT|wxCHANGE_DIR);
+ if (dlg.ShowModal() == wxID_OK) {
+ filename = dlg.GetPath();
+ m_log->Save(filename);
+ this->SetTitle(wxT("AE6HO Radio Location System - ")+filename);
+ }
+ }
+ else
+ m_log->Save(); // Adds additional samples since last save
+}
+
+void HunterFrame::OnFileClose(wxCommandEvent &event)
+{
+ // FIXME: ask user if they want to save changed data instead of going straight to save dialog
+ if (m_log->IsDirty())
+ OnFileSave(event);
+
+ StopCapture();
+ delete m_log;
+ m_search.Reset();
+ m_log = NULL;
+
+ this->SetTitle(wxT("AE6HO Radio Location System")); // refactor into EnableSearchItems()
+ XRCCTRL(*this, "search_newsave_button", wxButton)->SetLabel(_T("New"));
+ XRCCTRL(*this, "search_openclose_button", wxButton)->SetLabel(_T("Open"));
+ this->GetMenuBar()->FindItem(XRCID("file_new_menuitem"))->Enable(true);
+ this->GetMenuBar()->FindItem(XRCID("file_open_menuitem"))->Enable(true);
+ EnableSearchItems(false);
+ m_known.ClearStats();
+ UpdateKnownStatistics();
+}
+
+void HunterFrame::OnCalibrationSaveToFile(wxCommandEvent &event)
+{
+ wxString filename;
+ wxFileDialog dlg(this, wxT("Save Calibration Data"), _T("."), _T(""), _T("*.cal"),
+ wxSAVE|wxOVERWRITE_PROMPT|wxCHANGE_DIR);
+ if (dlg.ShowModal() == wxID_OK) {
+ filename = dlg.GetPath();
+ wxFile file(filename.c_str(), wxFile::write);
+ for (int i = 0; i < 7; i++) {
+ wxString str;
+ float offset = m_settings->GetDopplerCalibration(i);
+ str.Printf(_T("%f\n"), offset);
+ file.Write(str.c_str(), str.length());
+ }
+ }
+}
+
+void HunterFrame::OnCalibrationLoadFromFile(wxCommandEvent &event)
+{
+ wxString filename;
+ wxFileDialog dlg(this, wxT("Load Calibration Data"), _T("."), _T(""), _T("*.cal"),
+ wxOPEN|wxFILE_MUST_EXIST|wxCHANGE_DIR);
+ if (dlg.ShowModal() == wxID_OK) {
+ filename = dlg.GetPath();
+ wxFile file(filename.c_str(), wxFile::read);
+ for (int i = 0; i < 7; i++) {
+ char ch;
+ size_t count;
+ wxString line;
+ count = file.Read(&ch, 1);
+ while (count == 1 && ch != '\n') {
+ line.Append(ch);
+ count = file.Read(&ch, 1);
+ }
+ float offset = atof((char *)line.c_str());
+ m_settings->SetDopplerCalibration(i, offset);
+ m_doppler->SetCalibration(i, offset);
+ }
+ }
+}
+
+void HunterFrame::OnCalibrationSaveTransmitter(wxCommandEvent &event)
+{
+ wxString filename;
+ if (!m_known.IsSet())
+ return; // FIXME: disable menu item when no known transmitter so we can't get here
+
+ wxFileDialog dlg(this, wxT("Save Transmitter Coordinates"), _T("."), _T(""), _T("*.loc"),
+ wxSAVE|wxOVERWRITE_PROMPT|wxCHANGE_DIR);
+ if (dlg.ShowModal() == wxID_OK) {
+ filename = dlg.GetPath();
+ wxFile file(filename.c_str(), wxFile::write);
+ wxString str;
+ // TODO: use Spherical method to output strings
+ str.Printf(_T("%f\n%f\n"), m_known.Latitude(),
+ m_known.Longitude());
+ file.Write(str.c_str(), str.length());
+ }
+}
+
+void HunterFrame::OnCalibrationLoadTransmitter(wxCommandEvent &event)
+{
+ wxString filename;
+ wxFileDialog dlg(this, wxT("Load Transmitter Location"), _T("."), _T(""), _T("*.loc"),
+ wxOPEN|wxFILE_MUST_EXIST|wxCHANGE_DIR);
+ if (dlg.ShowModal() == wxID_OK) {
+ filename = dlg.GetPath();
+ wxFile file(filename.c_str(), wxFile::read);
+ wxString latitude, longitude;
+
+ for (int i = 0; i < 2; i++) {
+ char ch;
+ size_t count;
+ wxString line;
+
+ count = file.Read(&ch, 1);
+ while (count == 1 && ch != '\n') {
+ line.Append(ch);
+ count = file.Read(&ch, 1);
+ }
+ if (i == 0) {
+ latitude = line;
+ }
+ else if (i == 1) {
+ longitude = line;
+ }
+ }
+
+ m_known.Location(Spherical(latitude, longitude));
+ CalcKnownStatistics();
+
+ XRCCTRL(*this, "known_transmitter_checkbox", wxCheckBox)->SetValue(true);
+ m_settings->SetUseKnownTransmitter(true);
+ m_settings->SetKnownTransmitterLatitude(m_known.Latitude());
+ m_settings->SetKnownTransmitterLongitude(m_known.Longitude());
+
+ UpdateKnownLocation();
+ if (m_gps_started)
+ UpdateKnownDirection();
+ }
+}
+
+void HunterFrame::OnSearchNewSave(wxCommandEvent &event)
+{
+ if (!m_log)
+ OnFileNew(event);
+ else
+ OnFileSave(event);
+}
+
+void HunterFrame::OnSearchOpenClose(wxCommandEvent &event)
+{
+ if (!m_log)
+ OnFileOpen(event);
+ else
+ OnFileClose(event);
+}
+
+void HunterFrame::OnSearchToggle(wxCommandEvent &event)
+{
+ if (!m_capture)
+ StartCaptureAutomatic();
+ else
+ StopCapture();
+}
+
+void HunterFrame::StartCaptureAutomatic()
+{
+ m_capture = true;
+ m_one_shot = false;
+ XRCCTRL(*this, "search_toggle_button", wxButton)->SetLabel(_T("Stop"));
+ XRCCTRL(*this, "search_once_button", wxButton)->Enable(false);
+}
+
+void HunterFrame::StartCaptureOnce()
+{
+ m_capture = true;
+ m_one_shot = true;
+ XRCCTRL(*this, "search_once_button", wxButton)->SetLabel(_T("Cancel"));
+ XRCCTRL(*this, "search_toggle_button", wxButton)->Enable(false);
+}
+
+void HunterFrame::StopCapture()
+{
+ m_capture = false;
+ m_one_shot = false;
+ XRCCTRL(*this, "search_toggle_button", wxButton)->Enable(true);
+ XRCCTRL(*this, "search_once_button", wxButton)->Enable(true);
+ XRCCTRL(*this, "search_toggle_button", wxButton)->SetLabel(_T("Automatic"));
+ XRCCTRL(*this, "search_once_button", wxButton)->SetLabel(_T("One Shot"));
+}
+
+void HunterFrame::OnSearchOnce(wxCommandEvent &event)
+{
+ if (!m_capture)
+ StartCaptureOnce();
+ else
+ StopCapture();
+}
+
+void HunterFrame::OnDisplayOrientation(wxCommandEvent &event)
+{
+ int selection = XRCCTRL(*this, "display_orientation_radiobox", wxRadioBox)->GetSelection();
+ if (selection == 0)
+ m_tactical_panel->SetOrientation(TrackUp);
+ else
+ m_tactical_panel->SetOrientation(NorthUp);
+
+ m_settings->SetDisplayOrientation(selection);
+}
+
+void HunterFrame::OnDisplayDoppler(wxCommandEvent &event)
+{
+ bool checked = event.IsChecked();
+ m_tactical_panel->SetDisplayDoppler(checked);
+ m_settings->SetDisplayDoppler(checked);
+}
+
+void HunterFrame::OnDisplayKnown(wxCommandEvent &event)
+{
+ bool checked = event.IsChecked();
+ m_tactical_panel->SetDisplayKnown(checked);
+ m_settings->SetDisplayKnown(checked);
+}
+
+void HunterFrame::OnDisplayEstimated(wxCommandEvent &event)
+{
+ bool checked = event.IsChecked();
+ m_tactical_panel->SetDisplayEstimated(checked);
+ m_settings->SetDisplayEstimated(checked);
+}
+
+void HunterFrame::OnHistogramSourceChg(wxCommandEvent &event)
+{
+ m_histogram_source = XRCCTRL(*this, "statistics_source_radiobox", wxRadioBox)->GetSelection();
+ if (m_histogram_source == 0)
+ m_error_histogram_panel->SetData(m_search.Histogram());
+ else if (m_histogram_source == 1)
+ m_error_histogram_panel->SetData(m_known.Histogram());
+ // TODO: remember this in m_settings
+ Refresh(); // Needed?
+}
+
+void HunterFrame::OnHistogramCoordsChg(wxCommandEvent &event)
+{
+ int n = XRCCTRL(*this, "statistics_coords_radiobox", wxRadioBox)->GetSelection();
+ if (n == 0)
+ m_error_histogram_panel->SetMode(HistogramPanel::Rectangular);
+ else if (n == 1)
+ m_error_histogram_panel->SetMode(HistogramPanel::Polar);
+ // TODO: remember this in m_settings
+}
diff --git a/ezdop/src/host/hunter/src/hunter.h b/ezdop/src/host/hunter/src/hunter.h
new file mode 100644
index 000000000..7710c5824
--- /dev/null
+++ b/ezdop/src/host/hunter/src/hunter.h
@@ -0,0 +1,156 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application level includes
+#include "sample.h"
+#include "known.h"
+#include "search.h"
+
+// wxWidgets includes
+#include <wx/frame.h>
+#include <wx/spinbutt.h> // Unknown why these two aren't in wx.h
+#include <wx/splitter.h>
+
+// Forward declarations
+class EZDoppler;
+class EZDopplerUpdate;
+class GPS;
+class GPSUpdate;
+class HunterSettings;
+class TacticalPanel;
+class HistogramPanel;
+class Spherical;
+class SampleLog;
+class KnownTransmitter;
+
+class HunterFrame : public wxFrame
+{
+public:
+ // Constructor
+ HunterFrame();
+ void DoCalibrationStep(int which);
+
+private:
+ // Initialization routines
+ void InitializeSettings();
+ void InitializeWindows();
+ void InitializeDoppler();
+ void InitializeGPS();
+ void InitializeCalibration();
+ void InitializeSearch();
+ void InitializeDisplay();
+
+ // System related functions
+ void OnClose(wxCloseEvent &event);
+
+ // Status related functions
+ void OnHistogramSourceChg(wxCommandEvent &event);
+ void OnHistogramCoordsChg(wxCommandEvent &event);
+
+ // Search related functions
+ void EnableSearchItems(bool enable);
+ void MakeNewLogAndSearch();
+ void CalcSolution();
+ void StartCaptureAutomatic();
+ void StartCaptureOnce();
+ void StopCapture();
+ void UpdateSearchStatus(bool display);
+ void UpdateSearchLocation(bool display);
+ void UpdateSearchDirection(bool display);
+ void OnSearchNewSave(wxCommandEvent &event);
+ void OnSearchOpenClose(wxCommandEvent &event);
+ void OnSearchToggle(wxCommandEvent &event);
+ void OnSearchOnce(wxCommandEvent &event);
+ void OnSearchUpdate(SearchUpdate &event);
+
+ // Doppler related functions
+ void SetDopplerParams();
+ void StartDoppler();
+ void StopDoppler();
+ void UpdateDopplerStatus(bool display);
+ void OnDopplerToggle(wxCommandEvent &event);
+ void OnDopplerAutostart(wxCommandEvent &event);
+ void OnDopplerReset(wxCommandEvent &event);
+ void OnDopplerFilterChg(wxScrollEvent &event);
+ void OnDopplerRotationChg(wxCommandEvent &event);
+ void OnDopplerUpdate(EZDopplerUpdate &event);
+
+ // GPS related functions
+ void StartGPS();
+ void StopGPS();
+ void UpdateGPSStatus(bool dipslay);
+ void UpdateGPSValidity(bool valid);
+ void OnGPSToggle(wxCommandEvent &event);
+ void OnGPSAutostart(wxCommandEvent &event);
+ void OnGPSDeviceSelect(wxCommandEvent &event);
+ void OnGPSUpdate(GPSUpdate &event);
+
+ // Calibration related functions
+ void CalcKnownStatistics();
+ void UpdateKnownLocation();
+ void UpdateKnownDirection();
+ void UpdateKnownStatistics();
+ void OnCalibrationEqualize(wxCommandEvent &event);
+ void OnCalibrationZero(wxCommandEvent &event);
+ void OnCalibrationAdjustLeft(wxSpinEvent &event);
+ void OnCalibrationAdjustRight(wxSpinEvent &event);
+ void OnCalibrationAffectAllRates(wxCommandEvent &event);
+ void OnKnownTransmitterUpdate(wxCommandEvent &event);
+ void OnUseKnownTransmitter(wxCommandEvent &event);
+
+ // Tactical display related functions
+ void OnDisplayOrientation(wxCommandEvent &event);
+ void OnDisplayDoppler(wxCommandEvent &event);
+ void OnDisplayKnown(wxCommandEvent &event);
+ void OnDisplayEstimated(wxCommandEvent &event);
+
+ // Menu event handlers
+ void OnExit(wxCommandEvent &event);
+ void OnAbout(wxCommandEvent &event);
+ void OnFileNew(wxCommandEvent &event);
+ void OnFileOpen(wxCommandEvent &event);
+ void OnFileClose(wxCommandEvent &event);
+ void OnFileSave(wxCommandEvent &event);
+ void OnCalibrationSaveToFile(wxCommandEvent &event);
+ void OnCalibrationLoadFromFile(wxCommandEvent &event);
+ void OnCalibrationLoadTransmitter(wxCommandEvent &event);
+ void OnCalibrationSaveTransmitter(wxCommandEvent &event);
+
+ // Member data
+ HunterSettings *m_settings; // Configuration file
+ EZDoppler *m_doppler; // Attached Doppler device
+ GPS *m_gps; // Attached GPS device
+ SampleLog *m_log; // Accumulated sample points
+ KnownTransmitter m_known; // Identified known transmitter location for calibration
+ TransmitterSearch m_search; // Search being conducted
+
+ bool m_doppler_started; // Tracks start/stop of doppler device
+ bool m_gps_started; // Tracks start/stop of GPS device
+ bool m_capture; // Tracks start/stop of bearing capture
+ bool m_one_shot; // Tracks whether capture is one bearing only
+ int m_histogram_source; // Tracks histogram data source (0=estimated, 1=actual);
+
+ Sample m_sample; // Current sample being updated by doppler and GPS
+
+ // Child windows that need remembering
+ TacticalPanel *m_tactical_panel;
+ HistogramPanel *m_error_histogram_panel;
+
+ // This class handles events
+ DECLARE_EVENT_TABLE();
+};
diff --git a/ezdop/src/host/hunter/src/hunter.xpm b/ezdop/src/host/hunter/src/hunter.xpm
new file mode 100644
index 000000000..4d2ec23cd
--- /dev/null
+++ b/ezdop/src/host/hunter/src/hunter.xpm
@@ -0,0 +1,39 @@
+/* XPM */
+static char * hunter_xpm[] = {
+"32 32 4 1",
+" c #000000",
+". c #FF0000",
+"+ c #00FF80",
+"@ c #00FF00",
+" ..... ",
+" ............. ",
+" ..... ...... ",
+" .... + ... ",
+" ... @+@ ... ",
+" ... @+@ ... ",
+" ... @+@ ... ",
+" .. @+@ .. ",
+" .. @+@ .. ",
+" ... @+@ ... ",
+" .. @+@ .. ",
+" .. @+@ .. ",
+" .. @+@ .. ",
+".. @+@ .. ",
+".. @@@ .. ",
+".. @@@ .. ",
+".. @@@ .. ",
+".. .. ",
+" .. .. ",
+" .. .. ",
+" .. .. ",
+" ... ... ",
+" .. .. ",
+" .. .. ",
+" ... ... ",
+" ... ... ",
+" ... ... ",
+" .... .... ",
+" ..... ..... ",
+" ............. ",
+" ..... ",
+" "};
diff --git a/ezdop/src/host/hunter/src/hunter.xrc b/ezdop/src/host/hunter/src/hunter.xrc
new file mode 100644
index 000000000..078616492
--- /dev/null
+++ b/ezdop/src/host/hunter/src/hunter.xrc
@@ -0,0 +1,1020 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<resource xmlns="http://www.wxwidgets.org/wxxrc" version="2.3.0.1">
+
+<object class="wxMenuBar" name="main_menu">
+ <object class="wxMenu" name="file_menu">
+ <label>_File</label>
+ <object class="wxMenuItem" name="file_new_menuitem">
+ <label>_New Search\tCtrl-N</label>
+ <help>Create a new transmitter search.</help>
+ </object>
+ <object class="wxMenuItem" name="file_open_menuitem">
+ <label>_Open Search\tCtrl-O</label>
+ <help>Open an existing search</help>
+ </object>
+ <object class="wxMenuItem" name="file_close_menuitem">
+ <label>_Close Search\tCtrl-W</label>
+ <help>Close current search.</help>
+ </object>
+ <object class="separator"/>
+ <object class="wxMenuItem" name="file_save_menuitem">
+ <label>_Save Search\tCtrl-S</label>
+ <help>Save current search to a file.</help>
+ </object>
+ <object class="separator"/>
+ <object class="wxMenuItem" name="file_exit_menuitem">
+ <label>E_xit\tAlt-X</label>
+ <help>Exit this application</help>
+ </object>
+ </object>
+
+ <object class="wxMenu" name="doppler_menu">
+ <label>_Doppler</label>
+ <object class="wxMenuItem" name="doppler_toggle_menuitem">
+ <label>_Start</label>
+ <help>Start Doppler Rotation</help>
+ </object>
+ <object class="wxMenuItem" name="doppler_autostart_menuitem">
+ <label>_Autostart</label>
+ <checkable>1</checkable>
+ <help>Automatically start Doppler when application loads</help>
+ </object>
+ <object class="wxMenuItem" name="doppler_reset_menuitem">
+ <label>_Reset</label>
+ <help>Reset Doppler</help>
+ </object>
+ </object>
+
+ <object class="wxMenu" name="calibration_menu">
+ <label>_Calibration</label>
+ <object class="wxMenuItem" name="calibration_loadfromfile_menuitem">
+ <label>_Load Calibration File</label>
+ <help>Load system calibration values from filesystem</help>
+ </object>
+ <object class="wxMenuItem" name="calibration_savetofile_menuitem">
+ <label>_Save Calibration File</label>
+ <help>Save system calibration values to filesystem</help>
+ </object>
+ <object class="separator"/>
+ <object class="wxMenuItem" name="calibration_loadtransmitter_menuitem">
+ <label>L_oad Transmitter</label>
+ <help>Load transmitter coordinates from file</help>
+ </object>
+ <object class="wxMenuItem" name="calibration_savetransmitter_menuitem">
+ <label>S_ave Transmitter</label>
+ <help>Save transmitter coordinates to file</help>
+ </object>
+ </object>
+
+ <object class="wxMenu" name="help_menu">
+ <label>_Help</label>
+ <object class="wxMenuItem" name="about_menuitem">
+ <label>_About...</label>
+ <help>About this application</help>
+ </object>
+ </object>
+</object>
+
+<object class="wxFrame" name="main_frame"> <!-- Main window, bottom left and right panes -->
+ <title>AE6HO Radio Location System</title>
+ <object class="wxSplitterWindow" name="horiz_splitter"> <!-- Divides main window into top and bottom -->
+ <style>wxSP_3D|wxSP_LIVE_UPDATE</style>
+ <orientation>horizontal</orientation>
+ <minsize>170</minsize>
+
+ <object class="wxSplitterWindow" name="vert_splitter"> <!-- Divides top half into left and right -->
+ <style>wxSP_3D|wxSP_LIVE_UPDATE</style>
+ <orientation>vertical</orientation>
+ <minsize>240</minsize>
+ <sashpos>240</sashpos>
+ <object_ref ref="status_panel"/> <!-- Defined below -->
+ <object class="unknown" name="tactical_panel"/>
+ </object>
+
+ <object_ref ref="control_panel"/> <!-- Defined below -->
+
+ </object>
+
+</object>
+
+<object class="wxPanel" name="status_panel"> <!-- Read only display variables -->
+ <object class="wxBoxSizer"> <!-- Each item is a major status disply, doppler, GPS, etc. -->
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <flag>wxGROW|wxALL</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer"> <!-- Each item is a Doppler status control -->
+ <label>Doppler</label>
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <flag>wxGROW|wxLEFT|wxRIGHT|wxBOTTOM</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer"> <!-- Single item, doppler strength -->
+ <orient>wxVERTICAL</orient>
+ <label>Strength</label>
+ <object class="sizeritem">
+ <flag>wxGROW</flag>
+ <object class="wxGauge" name="doppler_level_gauge">
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxGROW|wxLEFT|wxRIGHT|wxBOTTOM</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer"> <!-- Single item, doppler audio level -->
+ <orient>wxVERTICAL</orient>
+ <label>Audio Level</label>
+ <object class="sizeritem">
+ <flag>wxGROW</flag>
+ <object class="wxGauge" name="doppler_audio_gauge">
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxGROW</flag>
+ <border>5</border>
+ <object class="wxBoxSizer">
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <flag>wxGROW|wxLEFT|wxRIGHT|wxBOTTOM</flag>
+ <border>5</border>
+ <option>1</option>
+ <object class="wxStaticBoxSizer"> <!-- Single item, absolute bearing -->
+ <orient>wxVERTICAL</orient>
+ <label>Abs. Bearing</label>
+ <object class="sizeritem">
+ <flag>wxGROW</flag>
+ <object class="wxStaticText" name="doppler_absolute_bearing_text">
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxGROW|wxLEFT|wxRIGHT|wxBOTTOM</flag>
+ <border>5</border>
+ <option>1</option>
+ <object class="wxStaticBoxSizer"> <!-- Single item, relative bearing -->
+ <orient>wxVERTICAL</orient>
+ <label>Rel. Bearing</label>
+ <object class="sizeritem">
+ <flag>wxGROW</flag>
+ <object class="wxStaticText" name="doppler_relative_bearing_text">
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+
+ <object class="sizeritem">
+ <flag>wxGROW|wxALL</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer"> <!-- Each item is a Transmitter status control -->
+ <label>Transmitter</label>
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <flag>wxGROW|wxLEFT|wxRIGHT|wxBOTTOM</flag>
+ <border>5</border>
+ <option>1</option>
+ <object class="wxBoxSizer">
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <flag>wxGROW|wxRIGHT</flag>
+ <border>5</border>
+ <option>1</option>
+ <object class="wxStaticBoxSizer"> <!-- Single item, estimated transmitter bearing -->
+ <orient>wxVERTICAL</orient>
+ <label>Est. Bearing</label>
+ <object class="sizeritem">
+ <flag>wxGROW</flag>
+ <object class="wxStaticText" name="transmitter_estimated_bearing_text">
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxGROW|wxLEFT</flag>
+ <border>5</border>
+ <option>1</option>
+ <object class="wxStaticBoxSizer"> <!-- Single item, estimated transmitter range -->
+ <orient>wxVERTICAL</orient>
+ <label>Est. Range</label>
+ <object class="sizeritem">
+ <flag>wxGROW</flag>
+ <object class="wxStaticText" name="transmitter_estimated_range_text">
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxGROW|wxLEFT|wxRIGHT|wxBOTTOM</flag>
+ <border>5</border>
+ <option>1</option>
+ <object class="wxBoxSizer">
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <flag>wxGROW|wxRIGHT</flag>
+ <border>5</border>
+ <option>1</option>
+ <object class="wxStaticBoxSizer"> <!-- Single item, actual transmitter bearing -->
+ <orient>wxVERTICAL</orient>
+ <label>Act. Bearing</label>
+ <object class="sizeritem">
+ <flag>wxGROW</flag>
+ <object class="wxStaticText" name="transmitter_actual_bearing_text">
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxGROW|wxLEFT</flag>
+ <border>5</border>
+ <option>1</option>
+ <object class="wxStaticBoxSizer"> <!-- Single item, actual transmitter range -->
+ <orient>wxVERTICAL</orient>
+ <label>Act. Range</label>
+ <object class="sizeritem">
+ <flag>wxGROW</flag>
+ <object class="wxStaticText" name="transmitter_actual_range_text">
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+
+ <object class="sizeritem">
+ <flag>wxGROW|wxALL</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Error Histogram</label>
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <object class="unknown" name="error_histogram_panel">
+ <bg>#000000</bg>
+ <size>220,220</size>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <object class="wxBoxSizer">
+ <style>wxHORIZONTAL</style>
+ <object class="sizeritem">
+ <flag>wxALL</flag>
+ <border>5</border>
+ <object class="wxRadioBox" name="statistics_source_radiobox">
+ <label>Source</label>
+ <style>wxRA_SPECIFY_COLS</style>
+ <dimension>1</dimension>
+ <selection>0</selection>
+ <item>Estimated</item>
+ <item>Actual</item>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxALL</flag>
+ <border>5</border>
+ <object class="wxRadioBox" name="statistics_coords_radiobox">
+ <label>Coordinates</label>
+ <style>wxRA_SPECIFY_COLS</style>
+ <dimension>1</dimension>
+ <selection>0</selection>
+ <item>Rectangular</item>
+ <item>Polar</item>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+</object>
+
+<object class="wxNotebook" name="control_panel"> <!-- all user interaction goes here -->
+ <style>wxNB_FIXEDWIDTH|wxNB_TOP</style>
+ <object class="notebookpage">
+ <label>Doppler</label>
+ <object_ref ref="doppler_control_panel"/> <!-- Attached EZ Doppler device configuration and control -->
+ </object>
+ <object class="notebookpage">
+ <label>GPS</label>
+ <object_ref ref="gps_control_panel"/> <!-- Attached GPS device configuration and control -->
+ </object>
+ <object class="notebookpage">
+ <label>Calibration</label>
+ <object_ref ref="calibration_control_panel"/> <!-- System calibration control -->
+ </object>
+ <object class="notebookpage">
+ <label>Capture</label>
+ <object_ref ref="capture_control_panel"/> <!-- Bearing capture control -->
+ </object>
+ <object class="notebookpage">
+ <label>Search</label>
+ <object_ref ref="search_control_panel"/> <!-- Search control -->
+ </object>
+ <object class="notebookpage">
+ <label>Statistics</label>
+ <object_ref ref="statistics_control_panel"/> <!-- Statistics control -->
+ </object>
+ <object class="notebookpage">
+ <label>Display</label>
+ <object_ref ref="display_control_panel"/> <!-- Tactical display control -->
+ </object>
+</object>
+
+<object class="wxPanel" name="doppler_control_panel"> <!-- Attached EZ Doppler device configuration and control -->
+ <style>wxTAB_TRAVERSAL</style>
+
+ <object class="wxBoxSizer"> <!-- Left to right controls -->
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <flag>wxTOP</flag>
+ <border>5</border>
+ <object class="wxBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem"> <!-- Start or stop doppler operation -->
+ <flag>wxLEFT|wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxButton" name="doppler_toggle_button">
+ <label>Start</label>
+ </object>
+ </object>
+ <object class="sizeritem"> <!-- Reset doppler -->
+ <flag>wxLEFT|wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxButton" name="doppler_reset_button">
+ <label>Reset</label>
+ </object>
+ </object>
+ <object class="sizeritem"> <!-- Check for autostart of doppler -->
+ <flag>wxLEFT|wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxCheckBox" name="doppler_autostart_checkbox">
+ <label>Autostart</label>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Set Filtering</label>
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem"> <!-- Set doppler filter time constant -->
+ <flag>wxALL|wxGROW</flag>
+ <border>5</border>
+ <object class="wxSlider" name="doppler_filter_slider">
+ <style>wxSL_HORIZONTAL|wxSL_LABELS</style>
+ <size>250,45</size>
+ <min>1</min>
+ <value>20</value>
+ <max>100</max>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem"> <!-- Set doppler rotation -->
+ <flag>wxTOP</flag>
+ <border>5</border>
+ <object class="wxRadioBox" name="doppler_rotation_radiobox">
+ <label>Set Rotation Rate (Hz)</label>
+ <style>wxRA_SPECIFY_ROWS</style>
+ <dimension>2</dimension>
+ <selection>5</selection>
+ <item>250</item>
+ <item>400</item>
+ <item>500</item>
+ <item>666</item>
+ <item>1000</item>
+ <item>2000</item>
+ </object>
+ </object>
+ </object>
+</object>
+
+<object class="wxPanel" name="gps_control_panel">
+ <style>wxTAB_TRAVERSAL</style>
+ <object class="wxBoxSizer"> <!-- Left to right controls -->
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <flag>wxTOP</flag>
+ <border>5</border>
+ <object class="wxBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem"> <!-- Start or stop GPS operation -->
+ <flag>wxALL</flag>
+ <border>5</border>
+ <object class="wxButton" name="gps_toggle_button">
+ <label>Start</label>
+ </object>
+ </object>
+ <object class="sizeritem"> <!-- Check for autostart of GPS -->
+ <flag>wxALL</flag>
+ <border>5</border>
+ <object class="wxCheckBox" name="gps_autostart_checkbox">
+ <label>Autostart GPS</label>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Select Port</label>
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <flag>wxALL|wxGROW</flag>
+ <border>5</border>
+ <object class="wxComboBox" name="gps_device_combobox">
+ <size>150,30</size>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <label>Location</label>
+ <object class="sizeritem">
+ <object class="wxStaticBoxSizer"> <!-- Single item, current latitude -->
+ <orient>wxVERTICAL</orient>
+ <label>Latitude</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="gps_latitude_text">
+ <size>140,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <object class="wxStaticBoxSizer"> <!-- Single item, current latitude-->
+ <orient>wxVERTICAL</orient>
+ <label>Longitude</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="gps_longitude_text">
+ <size>140,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <label>Course</label>
+ <object class="sizeritem">
+ <object class="wxStaticBoxSizer"> <!-- Single item, gps heading -->
+ <orient>wxVERTICAL</orient>
+ <label>Heading</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="gps_heading_text">
+ <size>55,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <object class="wxStaticBoxSizer"> <!-- Single item, gps speed -->
+ <orient>wxVERTICAL</orient>
+ <label>Speed</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="gps_speed_text">
+ <size>55,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+</object>
+
+<object class="wxPanel" name="calibration_control_panel">
+ <style>wxTAB_TRAVERSAL</style>
+ <object class="wxBoxSizer"> <!-- Left to right controls -->
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <flag>wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer" name="doppler_calibration_box">
+ <orient>wxVERTICAL</orient>
+ <label>Doppler</label>
+ <object class="sizeritem">
+ <object class="wxBoxSizer">
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem"> <!-- Perform doppler calibration -->
+ <flag>wxALL</flag>
+ <border>5</border>
+ <object class="wxButton" name="calibration_equalize_button">
+ <label>Equalize</label>
+ <default>0</default>
+ </object>
+ </object>
+ <object class="sizeritem"> <!-- Set calibration offset to make zero -->
+ <flag>wxALL</flag>
+ <border>5</border>
+ <object class="wxButton" name="calibration_zero_button">
+ <label>Zero</label>
+ <default>0</default>
+ </object>
+ </object>
+ </object>
+ </object>
+
+ <object class="sizeritem">
+ <object class="wxBoxSizer">
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <flag>wxALL</flag>
+ <border>5</border>
+ <object class="wxStaticText">
+ <label>Adjust Offset:</label>
+ </object>
+ </object>
+ <object class="sizeritem"> <!-- Set calibration offset to make zero -->
+ <flag>wxALL</flag>
+ <border>5</border>
+ <object class="wxSpinButton" name="calibration_adjust_spinner">
+ <style>wxSP_HORIZONTAL</style>
+ <value>500</value>
+ <min>0</min>
+ <max>1000</max>
+ </object>
+ </object>
+ </object>
+ </object>
+
+ <object class="sizeritem">
+ <flag>wxALL</flag>
+ <border>5</border>
+ <object class="wxCheckBox" name="calibration_all_checkbox">
+ <label>Affect All Rates</label>
+ </object>
+ </object>
+
+ </object>
+ </object>
+
+ <object class="sizeritem">
+ <flag>wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer" name="known_transmitter_box">
+ <orient>wxHORIZONTAL</orient>
+ <label>Known Transmitter</label>
+ <object class="sizeritem">
+ <object class="wxBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <flag>wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <label>Latitude</label>
+ <object class="sizeritem">
+ <object class="wxTextCtrl" name="known_transmitter_latitude_edit">
+ <style>wxTE_LEFT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxTOP||wxBOTTOM|wxALIGN_CENTRE</flag>
+ <border>5</border>
+ <object class="wxButton" name="known_transmitter_update_button">
+ <label>Update</label>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <object class="wxBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <object class="wxStaticBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <label>Longitude</label>
+ <object class="sizeritem">
+ <object class="wxTextCtrl" name="known_transmitter_longitude_edit">
+ <style>wxTE_LEFT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxTOP|wxALIGN_CENTRE</flag>
+ <border>10</border>
+ <object class="wxCheckBox" name="known_transmitter_checkbox">
+ <label>Use</label>
+ <default>0</default>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Bearing Error Statistics</label>
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <object class="wxBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <flag>wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Count</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="error_count_text">
+ <size>75,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Mode</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="error_mode_text">
+ <size>75,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <object class="wxBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <object class="wxStaticBoxSizer">
+ <label>Mean</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="error_mean_text">
+ <size>75,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <object class="wxStaticBoxSizer">
+ <label>Concen.</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="error_conc_text">
+ <size>75,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+</object>
+
+<object class="wxPanel" name="capture_control_panel">
+ <style>wxTAB_TRAVERSAL</style>
+ <object class="wxBoxSizer"> <!-- Left to right controls -->
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <object class="wxPanel">
+ </object>
+ </object>
+ </object>
+</object>
+
+<object class="wxPanel" name="statistics_control_panel">
+ <style>wxTAB_TRAVERSAL</style>
+ <object class="wxBoxSizer"> <!-- Left to right controls -->
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <object class="wxPanel">
+ </object>
+ </object>
+ </object>
+</object>
+
+
+
+<object class="wxDialog" name="calibration_dialog">
+ <title>Equalize Doppler Rotation Rates</title>
+ <object class="wxBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <flag>wxALL</flag>
+ <border>10</border>
+ <object class="wxStaticText" name="calibration_instructions_text">
+ <label>Please ensure the signal source remains fixed for\nthe duration of the test. The source does not have\nto be at a zero bearing.</label>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxGROW|wxALL</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <orient>wxHORIZONTAL</orient>
+ <label>Calibration Progress...</label>
+ <object class="sizeritem">
+ <style>wxGROW</style>
+ <option>1</option>
+ <object class="wxGauge" name="calibration_progress_gauge">
+ <range>6</range>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxALL</flag>
+ <border>10</border>
+ <object class="wxStaticText" name="calibration_progress_text">
+ <label>Press Start to begin, Cancel to exit...</label>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxGROW|wxALL</flag>
+ <border>5</border>
+ <object class="wxBoxSizer">
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <flag>wxALIGN_LEFT</flag>
+ <object class="wxButton" name="calibration_start_button">
+ <label>Start</label>
+ </object>
+ </object>
+ <object class="spacer">
+ <size>5</size>
+ <option>1</option>
+ </object>
+ <object class="sizeritem">
+ <flag>wxALIGN_RIGHT</flag>
+ <object class="wxButton" name="calibration_cancel_button">
+ <label>Cancel</label>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+</object>
+
+<object class="wxPanel" name="display_control_panel"> <!-- Tactical display options -->
+ <style>wxTAB_TRAVERSAL</style>
+
+ <object class="wxBoxSizer"> <!-- Left to right controls -->
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <flag>wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxRadioBox" name="display_orientation_radiobox">
+ <label>Orientation</label>
+ <style>wxRA_SPECIFY_COLS</style>
+ <dimension>1</dimension>
+ <selection>0</selection>
+ <item>Track Up</item>
+ <item>North Up</item>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxTOP|wxRIGHT</flag>
+ <border>5</border>
+
+ <object class="wxStaticBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <label>Display Pointers</label>
+ <object class="sizeritem">
+ <object class="wxCheckBox" name="display_doppler_checkbox">
+ <label>Current Doppler Bearing</label>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <object class="wxCheckBox" name="display_known_checkbox">
+ <label>Known Transmitter Bearing</label>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <object class="wxCheckBox" name="display_estimated_checkbox">
+ <label>Estimated Bearing</label>
+ </object>
+ </object>
+ </object>
+
+ </object>
+ </object>
+</object>
+
+<object class="wxPanel" name="search_control_panel"> <!-- Search control options -->
+ <style>wxTAB_TRAVERSAL</style>
+
+ <object class="wxBoxSizer"> <!-- Left to right controls -->
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <flag>wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Search Log</label>
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <flag>wxLEFT|wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxButton" name="search_newsave_button">
+ <label>New</label>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxALL</flag>
+ <border>5</border>
+ <object class="wxButton" name="search_openclose_button">
+ <label>Open</label>
+ </object>
+ </object>
+ </object>
+ </object>
+
+ <object class="sizeritem">
+ <flag>wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Capture</label>
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <flag>wxTOP|wxLEFT|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxButton" name="search_toggle_button">
+ <label>Auto</label>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxALL</flag>
+ <border>5</border>
+ <object class="wxButton" name="search_once_button">
+ <label>One Shot</label>
+ </object>
+ </object>
+ </object>
+ </object>
+
+ <object class="sizeritem">
+ <flag>wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Estimated Location</label>
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <object class="wxStaticBoxSizer">
+ <label>Longitude</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="search_longitude_text">
+ <size>150,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <object class="wxStaticBoxSizer">
+ <label>Latitude</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="search_latitude_text">
+ <size>150,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+
+ <object class="sizeritem">
+ <flag>wxTOP|wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Search Status</label>
+ <orient>wxHORIZONTAL</orient>
+ <object class="sizeritem">
+ <object class="wxBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <flag>wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Bearings</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="search_count_text">
+ <size>100,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Status</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="search_status_text">
+ <size>100,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+
+ <object class="sizeritem">
+ <object class="wxBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <flag>wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Mode</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="search_mode_text">
+ <size>100,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <flag>wxRIGHT</flag>
+ <border>5</border>
+ <object class="wxStaticBoxSizer">
+ <label>Mean</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="search_mean_text">
+ <size>100,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+
+ <object class="sizeritem">
+ <object class="wxBoxSizer">
+ <orient>wxVERTICAL</orient>
+ <object class="sizeritem">
+ <object class="wxStaticBoxSizer">
+ <label>Score</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="search_score_text">
+ <size>100,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="sizeritem">
+ <object class="wxStaticBoxSizer">
+ <label>Distance Error</label>
+ <object class="sizeritem">
+ <object class="wxStaticText" name="search_disterror_text">
+ <size>100,22</size>
+ <style>wxALIGN_RIGHT</style>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+</object>
+
+</resource>
diff --git a/ezdop/src/host/hunter/src/hunterapp.cc b/ezdop/src/host/hunter/src/hunterapp.cc
new file mode 100644
index 000000000..57113bf99
--- /dev/null
+++ b/ezdop/src/host/hunter/src/hunterapp.cc
@@ -0,0 +1,52 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application level includes
+#include "hunterapp.h"
+#include "hunter.h"
+
+// wxWidgets includes
+#include <wx/xrc/xmlres.h>
+#include <wx/log.h>
+
+// Provided in resource.cpp created from hunter.xrc by make system
+extern void InitXmlResource();
+
+bool Hunter::OnInit()
+{
+ m_logger = new wxLogStream();
+ wxLog::SetActiveTarget(m_logger);
+
+ // Get XML resources linked in via resource.cpp
+ wxXmlResource::Get()->InitAllHandlers();
+ InitXmlResource();
+
+ HunterFrame *top = new HunterFrame();
+ top->Show(true);
+
+ // Everything Zen
+ return true;
+}
+
+int Hunter::OnExit()
+{
+ return 0;
+}
+
+// Creates main() and WinMain() entry points
+IMPLEMENT_APP(Hunter)
diff --git a/ezdop/src/host/hunter/src/hunterapp.h b/ezdop/src/host/hunter/src/hunterapp.h
new file mode 100644
index 000000000..a71fc5704
--- /dev/null
+++ b/ezdop/src/host/hunter/src/hunterapp.h
@@ -0,0 +1,43 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __HUNTER_APP__
+#define __HUNTER_APP__
+
+// wxWidgets includes
+#include <wx/app.h>
+
+// Forward declarations
+class wxLog;
+
+class Hunter : public wxApp
+{
+public:
+ // Called on application startup
+ virtual bool OnInit();
+
+ // Called by system when application is closing but
+ // before wxWidgets is finished
+ virtual int OnExit();
+
+private:
+ // Active log target
+ wxLog *m_logger;
+};
+
+#endif
diff --git a/ezdop/src/host/hunter/src/known.cc b/ezdop/src/host/hunter/src/known.cc
new file mode 100644
index 000000000..ed257e014
--- /dev/null
+++ b/ezdop/src/host/hunter/src/known.cc
@@ -0,0 +1,55 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application level includes
+#include "known.h"
+#include "util.h"
+
+// wxWidget includes
+#include <wx/log.h>
+
+using namespace std;
+
+KnownTransmitter::KnownTransmitter()
+{
+ m_valid = false;
+ ClearStats();
+}
+
+void KnownTransmitter::Location(const Spherical &location)
+{
+ m_location = location;
+ m_valid = true;
+ ClearStats();
+}
+
+void KnownTransmitter::Clear()
+{
+ m_valid = false;
+ ClearStats();
+}
+
+void KnownTransmitter::ClearStats()
+{
+ m_histogram.Reset();
+}
+
+void KnownTransmitter::Calc(const std::vector<Sample> &samples)
+{
+ m_histogram.Calc(samples, m_location);
+}
diff --git a/ezdop/src/host/hunter/src/known.h b/ezdop/src/host/hunter/src/known.h
new file mode 100644
index 000000000..6f1e6bff8
--- /dev/null
+++ b/ezdop/src/host/hunter/src/known.h
@@ -0,0 +1,59 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __KNOWN_H__
+#define __KNOWN_H__
+
+// Application level includes
+#include "spherical.h"
+#include "sample.h"
+#include "histogram.h"
+
+// System level includes
+#include <vector>
+
+class KnownTransmitter
+{
+public:
+ KnownTransmitter();
+
+ void Location(const Spherical &location);
+ Spherical Location() const { return m_location; }
+
+ void Clear();
+ void ClearStats();
+ void Calc(const std::vector<Sample> &samples);
+
+ int Count() const { return m_histogram.Count(); }
+ int Mode() const { return m_histogram.Mode(); }
+ float Mean() const { return m_histogram.Mean(); }
+ float Concentration() const { return m_histogram.Concentration(); }
+
+ bool IsSet() const { return m_valid; }
+ bool HasStats() const { return m_histogram.Count() > 0; }
+ double Latitude() const { return m_location.Latitude(); }
+ double Longitude() const { return m_location.Longitude(); }
+ const ErrorHistogram &Histogram() { return m_histogram; }
+
+private:
+ bool m_valid;
+ Spherical m_location;
+ ErrorHistogram m_histogram;
+};
+
+#endif
diff --git a/ezdop/src/host/hunter/src/sample.cc b/ezdop/src/host/hunter/src/sample.cc
new file mode 100644
index 000000000..eedf5c74c
--- /dev/null
+++ b/ezdop/src/host/hunter/src/sample.cc
@@ -0,0 +1,139 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application level includes
+#include "sample.h"
+#include "util.h"
+
+// wxWidgets includes
+#include <wx/log.h>
+
+Sample::Sample()
+{
+ m_time = 0;
+ m_valid = false;
+ m_location = Spherical(0.0, 0.0);
+ m_heading = 0.0;
+ m_speed = 0.0;
+ m_in_phase = 0.0;
+ m_quadrature = 0.0;
+ m_phase = 0.0;
+ m_strength = 0.0;
+ m_volume = 0.0;
+ m_rate = 0;
+ m_filtering = 1;
+ m_error = 0.0;
+ m_ierror = 0.0;
+ m_qerror = 0.0;
+}
+
+Sample::Sample(const Sample &sample)
+{
+ m_time = sample.m_time;
+ m_valid = sample.m_valid;
+ m_location = sample.m_location;
+ m_heading = sample.m_heading;
+ m_speed = sample.m_speed;
+ m_in_phase = sample.m_in_phase;
+ m_quadrature = sample.m_quadrature;
+ m_phase = sample.m_phase;
+ m_strength = sample.m_strength;
+ m_volume = sample.m_volume;
+ m_rate = sample.m_rate;
+ m_filtering = sample.m_filtering;
+ m_error = sample.m_error;
+ m_ierror = sample.m_ierror;
+ m_qerror = sample.m_qerror;
+}
+
+Sample::Sample(wxString &line)
+{
+ char valid;
+ double lat, lon;
+
+ sscanf((char *)line.c_str(), "%c %i %lf %lf %f %f %f %f %f %f %f %f %f %f %i %i",
+ &valid,
+ &m_time,
+ &lat,
+ &lon,
+ &m_heading,
+ &m_speed,
+ &m_volume,
+ &m_strength,
+ &m_in_phase,
+ &m_quadrature,
+ &m_phase,
+ &m_error,
+ &m_ierror,
+ &m_qerror,
+ &m_rate,
+ &m_filtering);
+
+ if (valid == 'V')
+ m_valid = true;
+ else
+ m_valid = false;
+
+ m_location = Spherical(lat, lon);
+}
+
+void Sample::Dump(char *str, int len)
+{
+ // vld tim lat lon hdg speed vol stren inphs quad phase error ierr qerr rt flt
+ snprintf(str, len, "%s %10i %10.5lf %10.5lf %5.1f %6.2f %7.5f %7.5f %9.6f %9.6f %9.6f %7.2f %9.6f %9.6f %i %i",
+ m_valid ? "V":"I",
+ m_time,
+ m_location.Latitude(),
+ m_location.Longitude(),
+ m_heading,
+ m_speed,
+ m_volume,
+ m_strength,
+ m_in_phase,
+ m_quadrature,
+ m_phase,
+ m_error,
+ m_ierror,
+ m_qerror,
+ m_rate,
+ m_filtering);
+}
+
+void Sample::CalcError(const Spherical &location, float &angle, float &ierror, float &qerror) const
+{
+ float actual_bearing = bearing(m_location, location);
+ float sample_relative_bearing = degree_normalize(to_degrees(m_phase));
+ float sample_absolute_bearing = degree_normalize(sample_relative_bearing+m_heading);
+ angle = sample_absolute_bearing-actual_bearing;
+
+ if (angle < -180.0)
+ angle += 360;
+ if (angle > 180.0)
+ angle -= 360;
+
+ ierror = 0.0;
+ qerror = 0.0;
+
+ // Rotate I, Q by actual bearing
+ float i_act = cos(to_radians(-actual_bearing));
+ float q_act = sin(to_radians(-actual_bearing));
+ ierror = m_in_phase*i_act - m_quadrature*q_act;
+ qerror = m_quadrature*i_act + m_in_phase*q_act;
+
+
+}
diff --git a/ezdop/src/host/hunter/src/sample.h b/ezdop/src/host/hunter/src/sample.h
new file mode 100644
index 000000000..9ecce4857
--- /dev/null
+++ b/ezdop/src/host/hunter/src/sample.h
@@ -0,0 +1,97 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __SAMPLE_H__
+#define __SAMPLE_H__
+
+// Application level includes
+#include "spherical.h"
+
+// wxWidgets includes
+#include <wx/string.h>
+
+// System level includes
+#include <ctime>
+
+class Sample
+{
+public:
+ Sample();
+ Sample(const Sample &sample);
+ Sample(wxString &line);
+
+ void Time(time_t time) { m_time = time; }
+ void Valid(bool valid) { m_valid = valid; }
+ void Location(Spherical &location) { m_location = location; }
+ void Heading(float heading) { m_heading = heading; }
+ void Speed(float speed) { m_speed = speed; }
+ void InPhase(float in_phase) { m_in_phase = in_phase; }
+ void Quadrature(float quadrature) { m_quadrature = quadrature; }
+ void Phase(float phase) { m_phase = phase; }
+ void Strength(float strength) { m_strength = strength; }
+ void Volume(float volume) { m_volume = volume; }
+ void Rate(int rate) { m_rate = rate; }
+ void Filtering(int filtering) { m_filtering = filtering; }
+ void Error(float error) { m_error = error; }
+ void IError(float error) { m_ierror = error; }
+ void QError(float error) { m_qerror = error; }
+
+ const Spherical &Location() const { return m_location; }
+ float Latitude() const { return m_location.Latitude(); }
+ float Longitude() const { return m_location.Longitude(); }
+ float Heading() const { return m_heading; }
+ float Speed() const { return m_speed; }
+ float InPhase() const { return m_in_phase; }
+ float Quadrature() const { return m_quadrature; }
+ float Phase() const { return m_phase; }
+ float Strength() const { return m_strength; }
+ float Volume() const { return m_volume; }
+ int Filtering() const { return m_filtering; }
+ float Error() const { return m_error; }
+ float IError() const { return m_ierror; }
+ float QError() const { return m_qerror; }
+ bool Valid() const { return m_valid; }
+
+ void CalcError(const Spherical &location, float &error, float &ierror, float &qerror) const;
+
+ void Dump(char *str, int len); // TEMPORARY
+
+ operator const std::string(); // Conversion operator to std::string
+
+private:
+ // Data supplied by measuring system
+ time_t m_time; // Unix time of observation
+ bool m_valid; // GPS validity indication (NMEA "I" or "V")
+ Spherical m_location; // GPS latitude and longitude
+ float m_heading; // GPS heading in degrees 0.0 - 360.0
+ float m_speed; // GPS speed in mph
+ float m_in_phase; // Doppler I channel -1.0 to 1.0
+ float m_quadrature; // Doppler Q channel -1.0 to 1.0
+ float m_phase; // Doppler phase -M_PI to M_PI (derived)
+ float m_strength; // Doppler strength 0.0 - 1.0 (derived)
+ float m_volume; // Doppler volume 0.0 - 1.0
+ int m_rate; // Doppler rotation rate 0 - 5
+ int m_filtering; // Doppler filtering 1 - 100
+
+ // Container configured
+ float m_error; // Known transmitter bearing error
+ float m_ierror; // Known transmitter in phase error
+ float m_qerror; // Known transmitter quadrature error
+};
+
+#endif
diff --git a/ezdop/src/host/hunter/src/samplelog.cc b/ezdop/src/host/hunter/src/samplelog.cc
new file mode 100644
index 000000000..449d1bd89
--- /dev/null
+++ b/ezdop/src/host/hunter/src/samplelog.cc
@@ -0,0 +1,107 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application level includes
+#include "samplelog.h"
+
+// wxWidgets includes
+#include <wx/file.h>
+#include <wx/log.h>
+
+// System level includes
+#include <cmath>
+
+SampleLog::SampleLog()
+{
+ m_file = NULL;
+ m_save = -1;
+}
+
+void SampleLog::Load(wxString filename)
+{
+ m_file = new wxFile(filename.c_str(), wxFile::read_write);
+ wxString line;
+
+ if (m_file && m_file->IsOpened()) {
+ while (readline(line)) {
+ Sample sample(line); // can't use inline temporary in next line
+ Add(sample); // Locking is handled in Add
+ }
+
+ m_save = -1;
+ }
+}
+
+bool SampleLog::readline(wxString &line)
+{
+ char ch;
+ size_t count;
+
+ line.Empty();
+ count = m_file->Read(&ch, 1);
+ while (count == 1 && ch != '\n') {
+ line.Append(ch);
+ count = m_file->Read(&ch, 1);
+ }
+
+ return !line.IsEmpty();
+}
+
+void SampleLog::Add(Sample &sample)
+{
+ wxMutexLocker locker(m_mutex);
+
+ if (m_save < 0)
+ m_save = m_samples.size();
+
+ m_samples.push_back(sample);
+ return;
+}
+
+bool SampleLog::Save(wxString &filename)
+{
+ wxASSERT(!m_file); // Save called with filename when it already exists is an error
+ wxLogError(_T("SampleLog::Save: called with %s when file already exists"), filename.c_str());
+
+ m_filename = filename;
+ m_file = new wxFile(m_filename.c_str(), wxFile::write);
+
+ return Save();
+}
+
+bool SampleLog::Save()
+{
+ wxASSERT(m_file);
+ if (m_save < 0)
+ return false;
+
+ wxMutexLocker locker(m_mutex);
+
+ char str[256];
+ if (m_file && m_file->IsOpened()) {
+ for (int i = m_save; i < m_samples.size(); i++) {
+ m_samples[i].Dump(str, 255);
+ m_file->Write(str, strlen(str));
+ m_file->Write("\n", strlen("\n"));
+ }
+ m_save = -1;
+ return true;
+ }
+
+ return false;
+}
diff --git a/ezdop/src/host/hunter/src/samplelog.h b/ezdop/src/host/hunter/src/samplelog.h
new file mode 100644
index 000000000..71e049fc2
--- /dev/null
+++ b/ezdop/src/host/hunter/src/samplelog.h
@@ -0,0 +1,63 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __SAMPLELOG_H__
+#define __SAMPLELOG_H__
+
+// Application level includes
+#include "sample.h"
+
+// wxWidgets includes
+#include <wx/string.h>
+#include <wx/event.h>
+
+// System level includes
+#include <vector>
+
+// Forward declarations
+class wxFile;
+
+class SampleLog
+{
+public:
+ SampleLog();
+
+ // Sample access
+ void Add(Sample &sample);
+ int Count() const { return m_samples.size(); }
+ wxMutex &Mutex() { return m_mutex; }
+ std::vector<Sample>& Samples() { return m_samples; }
+
+ // File operations
+ void Load(wxString filename);
+ bool Save();
+ bool Save(wxString &filename);
+ bool HasFile() const { return (m_file != NULL); }
+ bool IsDirty() const { return m_save >= 0; }
+
+private:
+ bool readline(wxString &line);
+
+ std::vector<Sample> m_samples;
+ wxMutex m_mutex;
+ int m_save;
+ wxString m_filename;
+ wxFile *m_file;
+};
+
+#endif
diff --git a/ezdop/src/host/hunter/src/search.cc b/ezdop/src/host/hunter/src/search.cc
new file mode 100644
index 000000000..9992cb896
--- /dev/null
+++ b/ezdop/src/host/hunter/src/search.cc
@@ -0,0 +1,211 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application level includes
+#include "search.h"
+#include "sample.h"
+#include "util.h"
+
+// wxWidgets includes
+#include <wx/log.h>
+#include <wx/utils.h>
+
+// System level includes
+#include <vector>
+
+using namespace std;
+
+const wxEventType wxEVT_SEARCH_UPDATE = wxNewEventType();
+
+SearchUpdate::SearchUpdate(const wxEventType &event, bool done) :
+wxNotifyEvent(event)
+{
+ m_done = done;
+}
+
+TransmitterSearch::TransmitterSearch(wxEvtHandler *dest) :
+m_condition(m_mutex)
+{
+ Reset();
+ m_resolution = 0.0005; // 182 foot granularity
+ m_dest = dest;
+ m_log = NULL;
+ m_scale = 0.85; // Estimated from Mt. Umunhum data
+ m_busy = false;
+ Create(); // wxThreadHelper
+ GetThread()->Run();
+}
+
+TransmitterSearch::~TransmitterSearch()
+{
+ GetThread()->Delete();
+}
+
+void TransmitterSearch::Reset()
+{
+ m_iterations = 0;
+ m_initialized = false;
+ m_solution = Spherical(0.0, 0.0);
+ m_log = NULL;
+ ClearStats();
+}
+
+void TransmitterSearch::ClearStats()
+{
+ m_histogram.Reset();
+}
+
+void *TransmitterSearch::Entry()
+{
+ wxLogDebug(_T("TransmitterSearch::Entry(): entered"));
+ m_mutex.Lock();
+
+ while (!GetThread()->TestDestroy())
+ if (m_condition.WaitTimeout(1000) == wxCOND_NO_ERROR)
+ background_solve();
+
+ m_mutex.Unlock();
+ wxLogDebug(_T("TransmitterSearch::Entry(): exited"));
+}
+
+void TransmitterSearch::background_solve()
+{
+ if (!m_log)
+ return;
+
+ m_iterations = 0;
+ int count = m_log->Count();
+ ClearStats();
+
+ m_busy = true;
+ if (count == 0) // No data to solve from
+ return post_update(true);
+
+ if (!m_initialized) { // Get initial solution from first sample, but only once
+ m_solution = m_log->Samples().begin()->Location();
+ m_initialized = true;
+ }
+
+ if (count == 1)
+ return post_update(true);
+
+ while(1) {
+ m_iterations = 0;
+ while (++m_iterations <= MaxIterations) {
+ wxMutexLocker locker(m_log->Mutex());
+ if (hillclimb(m_log->Samples())) { // true return indicates solution of some sort achieved
+ m_histogram.Calc(m_log->Samples(), m_solution);
+ return post_update(true);
+ }
+ }
+
+ // Max iterations reached, send interim solution
+ m_histogram.Calc(m_log->Samples(), m_solution);
+ post_update(false);
+ }
+}
+
+void TransmitterSearch::post_update(bool done)
+{
+ SearchUpdate update(wxEVT_SEARCH_UPDATE, done);
+ wxPostEvent(m_dest, update);
+ m_busy = !done;
+}
+
+void TransmitterSearch::Solve(SampleLog *log)
+{
+ // FIXME: what if we get here while background thread is still busy?
+ if (m_log && (m_log != log))
+ wxLogError(_T("TransmitterSearch::Solve: supplied log different from current one."));
+
+ m_log = log;
+ m_condition.Signal();
+}
+
+// Return value indicates solution of some sort achieved
+bool TransmitterSearch::hillclimb(vector<Sample> &samples)
+{
+ int nx, ny;
+ int min_x = 0, min_y = 0;
+ int num;
+
+ Spherical trial;
+
+ float min_error;
+ float trial_error;
+
+ // Initialize search with current solution
+ if (calc_trial_error(samples, m_solution, min_error) == 0.0) {
+ wxLogDebug(_T("TransmitterSearch::hillclimb: no enabled samples, returning"));
+ return true; // No enabled data points, we're done
+ }
+
+ // Check if moving 'resolution' distance in one of four directions decreases error
+ for (nx = -1; nx < 2; nx++) {
+ trial.SetLongitude(m_solution.Longitude() + nx*m_resolution);
+ for (ny = -1; ny < 2; ny++) {
+ // Skip non-compass directions
+ if (nx == ny)
+ continue;
+ trial.SetLatitude(m_solution.Latitude() + ny*m_resolution);
+ calc_trial_error(samples, trial, trial_error);
+ if (trial_error < min_error) {
+ min_error = trial_error;
+ min_x = nx; min_y = ny;
+ }
+ }
+ }
+
+ // Indicate if solution achieved
+ if (min_x == 0 && min_y == 0)
+ return true;
+ else {
+ m_solution.SetLatitude(m_solution.Latitude()+min_y*m_resolution);
+ m_solution.SetLongitude(m_solution.Longitude()+min_x*m_resolution);
+ return false; // Make outer loop call us again
+ }
+}
+
+// Return value is number of enabled samples in vector
+float TransmitterSearch::calc_trial_error(const vector<Sample>&samples,
+ const Spherical &trial,
+ float &trial_error)
+{
+ float wsum = 0.0;
+ trial_error = 0.0;
+ float strength = 1.0;
+
+ for (int i = 0; i < samples.size(); i++) {
+ const Sample &sample = samples[i];
+
+ float angle, ierror, qerror;
+ sample.CalcError(trial, angle, ierror, qerror);
+
+ // Wrapped cauchy distribution
+ float p = m_scale;
+ float likelihood = (1-p*p)/(1+p*p-2*p*cos(angle*M_PI/180.0));
+
+ trial_error += -log(likelihood)*sample.Strength();
+ wsum += sample.Strength();
+ }
+
+ if (wsum > 0.0)
+ trial_error = trial_error/wsum;
+
+ return wsum;
+}
diff --git a/ezdop/src/host/hunter/src/search.h b/ezdop/src/host/hunter/src/search.h
new file mode 100644
index 000000000..b0e35d004
--- /dev/null
+++ b/ezdop/src/host/hunter/src/search.h
@@ -0,0 +1,107 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __SEARCH_H__
+#define __SEARCH_H__
+
+// Application level includes
+#include "spherical.h"
+#include "samplelog.h"
+#include "histogram.h"
+
+// wxWidgets includes
+#include <wx/event.h>
+
+// System level includes
+#include <ctime>
+
+// Forward declarations
+class wxFile;
+class Sample;
+class TransmitterSearch;
+
+class SearchUpdate : public wxNotifyEvent
+{
+public:
+ SearchUpdate(const wxEventType &event, bool done);
+ virtual wxEvent *Clone() const { return new SearchUpdate(*this); }
+ bool m_done;
+};
+
+extern const wxEventType wxEVT_SEARCH_UPDATE;
+
+typedef void(wxEvtHandler::*SearchUpdateFunction)(SearchUpdate&);
+
+#define EVT_SEARCH_UPDATE(fn) \
+ DECLARE_EVENT_TABLE_ENTRY( \
+ wxEVT_SEARCH_UPDATE, -1, -1, \
+ (wxObjectEventFunction)(wxEventFunction)(SearchUpdateFunction)&fn, \
+ (wxObject *)NULL \
+ ),
+
+class TransmitterSearch : public wxThreadHelper
+{
+public:
+ TransmitterSearch(wxEvtHandler *dest);
+ ~TransmitterSearch();
+
+ void Solve(SampleLog *log);
+ bool HasSolution() { return m_log ? m_log->Count() > 1 : false; }
+ void Reset();
+ void ClearStats();
+ bool Busy() const { return m_busy; }
+ int Mode() const { return m_histogram.Mode(); }
+ float Mean() const { return m_histogram.Mean(); }
+ float Concentration() { return m_histogram.Concentration(); }
+
+ const ErrorHistogram &Histogram() { return m_histogram; }
+ Spherical GetEstimatedLocation() { return m_solution; }
+
+ virtual void *Entry();
+
+private:
+ static const int MaxIterations = 20; // TODO: make configurable
+
+ bool hillclimb(std::vector<Sample> &samples);
+ float calc_trial_error(const std::vector<Sample> &samples, const Spherical &trial,
+ float &solution_error);
+
+ void background_solve();
+ void post_update(bool done);
+
+ // Background processing
+ wxEvtHandler *m_dest;
+ wxMutex m_mutex;
+ wxCondition m_condition;
+
+ // Solution state
+ SampleLog *m_log;
+ Spherical m_solution;
+ int m_iterations;
+ bool m_initialized;
+ bool m_busy;
+
+ // Solution options
+ float m_scale;
+ float m_resolution;
+
+ // Estimated solution histogram
+ ErrorHistogram m_histogram;
+};
+
+#endif
diff --git a/ezdop/src/host/hunter/src/serial.cc b/ezdop/src/host/hunter/src/serial.cc
new file mode 100644
index 000000000..5ace5aac1
--- /dev/null
+++ b/ezdop/src/host/hunter/src/serial.cc
@@ -0,0 +1,216 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "serial.h"
+
+#include <wx/log.h>
+
+#ifdef __WIN32__
+// I hate Windows.
+#else
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#endif
+
+wxArrayString EnumerateSerialPorts()
+{
+ wxArrayString result;
+
+#ifdef __WIN32__
+ wxString port;
+ for (int i = 1; i <= 8; i++) {
+ port.Printf("COM%i", i);
+ result.Add(port);
+ }
+#else
+ result.Add(_T("/dev/ttyS0"));
+ result.Add(_T("/dev/ttyS1"));
+ result.Add(_T("/dev/ttyS2"));
+ result.Add(_T("/dev/ttyS3"));
+ result.Add(_T("/dev/ttyUSB0"));
+ result.Add(_T("/dev/ttyUSB1"));
+#endif
+
+ return result;
+}
+
+SerialPort::SerialPort(wxString &port)
+{
+ wxLogDebug(_T("SerialPort::SerialPort(): %s"), port.c_str());
+ m_port = port;
+ m_opened = false;
+#ifdef __WIN32__
+ m_handle = INVALID_HANDLE_VALUE;
+#else
+ m_fd = -1;
+#endif
+}
+
+bool SerialPort::Open(int speed)
+{
+ wxLogDebug(_T("SerialPort::Open: %i baud"), speed);
+ if (m_opened) {
+ wxLogWarning(_T("SerialPort::Open: called on already opened object."));
+ return false;
+ }
+
+#ifdef __WIN32__
+ m_handle = CreateFile(m_port.c_str(),
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+ if (m_handle == INVALID_HANDLE_VALUE) {
+ wxLogError("SerialPort::Open: CreateFile() failed");
+ return false;
+ }
+
+ DCB dcb;
+ if (!GetCommState(m_handle, &dcb)) {
+ wxLogError("SerialPort::Open: GetCommState failed.");
+ CloseHandle(m_handle);
+ return false;
+ }
+
+ dcb.BaudRate = speed;
+ dcb.ByteSize = 8;
+ dcb.StopBits = ONESTOPBIT;
+ dcb.Parity = NOPARITY;
+ dcb.fBinary = TRUE;
+ dcb.fParity = FALSE;
+
+ if (!SetCommState(m_handle, &dcb)) {
+ wxLogError("SerialPort::Open: SetCommState failed.");
+ CloseHandle(m_handle);
+ return false;
+ }
+
+ COMMTIMEOUTS timeouts;
+ if (!GetCommTimeouts(m_handle, &timeouts)) {
+ wxLogError("SerialPort::Open: GetCommTimeouts failed.");
+ CloseHandle(m_handle);
+ return false;
+ }
+
+ timeouts.ReadIntervalTimeout = MAXDWORD;
+ timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
+ timeouts.ReadTotalTimeoutConstant = 100;
+
+ if (!SetCommTimeouts(m_handle, &timeouts)) {
+ wxLogError("SerialPort::Open: SetCommTimeouts failed.");
+ CloseHandle(m_handle);
+ return false;
+ }
+
+ m_opened = true;
+#else
+ m_fd = open((char *)m_port.c_str(), O_RDWR|O_NONBLOCK);
+ if (m_fd < 0) {
+ wxLogError(_T("SerialPort::Open: open() returned %i"), m_fd);
+ return false;
+ }
+
+ if (!isatty(m_fd)) {
+ wxLogError(_T("SerialPort::Open: device %s is not a tty"), m_port.c_str());
+ close(m_fd);
+ return false;
+ }
+
+ if (tcgetattr(m_fd, &m_ttyset_old) != 0) {
+ wxLogError(_T("SerialPort::Open: failed to get port attributes"));
+ close(m_fd);
+ return false;
+ }
+
+ memcpy(&m_ttyset_new, &m_ttyset_old, sizeof(m_ttyset_new));
+ cfsetispeed(&m_ttyset_new, (speed_t)speed);
+ cfsetospeed(&m_ttyset_new, (speed_t)speed);
+ m_ttyset_new.c_cflag &= ~(PARENB|CRTSCTS); // Disable parity and flowcontrol
+ m_ttyset_new.c_cflag |= CS8|CREAD|CLOCAL; // 8 bits, read, no modem status lines
+ m_ttyset_new.c_iflag = 0;
+ m_ttyset_new.c_oflag = ONLCR; // Convert LF to CRLF on receive
+ m_ttyset_new.c_lflag = 0;
+
+ if (tcsetattr(m_fd, TCSANOW, &m_ttyset_new) != 0) {
+ wxLogError(_T("SerialPort::Open: failed to set port attributes"));
+ close(m_fd);
+ return false;
+ }
+
+ m_opened = true;
+#endif
+ return m_opened;
+}
+
+void SerialPort::Close()
+{
+ wxLogDebug(_T("SerialPort::Close()"));
+#ifdef __WIN32__
+ CloseHandle(m_handle);
+#else
+ if (m_opened >= 0) {
+ m_ttyset_old.c_cflag |= HUPCL;
+ tcsetattr(m_fd, TCSANOW, &m_ttyset_old);
+ close(m_fd);
+ }
+#endif
+
+ m_opened = false;
+}
+
+SerialPort::~SerialPort()
+{
+ wxLogDebug(_T("SerialPort::~SerialPort()"));
+
+ if (m_opened)
+ Close();
+}
+
+int SerialPort::RxReady()
+{
+ int count = 0;
+#ifdef __WIN32__
+ return 1; // No equivalent Win32 call, use read timeouts instead
+#else
+ if (m_fd < 0 || ioctl(m_fd, FIONREAD, &count) < 0)
+ return -1;
+#endif
+ return count;
+}
+
+int SerialPort::Read(char *buffer, int len)
+{
+ wxASSERT(buffer);
+ wxASSERT(len);
+
+ if (!m_opened)
+ return -1;
+
+#ifdef __WIN32__
+ DWORD num;
+ if (ReadFile(m_handle, buffer, (DWORD)len, &num, NULL))
+ return num;
+ else
+ return -1;
+#else
+ return read(m_fd, buffer, len);
+#endif
+}
diff --git a/ezdop/src/host/hunter/src/serial.h b/ezdop/src/host/hunter/src/serial.h
new file mode 100644
index 000000000..63bfe4327
--- /dev/null
+++ b/ezdop/src/host/hunter/src/serial.h
@@ -0,0 +1,56 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __SERIAL_H__
+#define __SERIAL_H__
+
+#include <wx/arrstr.h>
+
+#ifdef __WIN32__
+#include <windows.h>
+#else
+#include <termios.h>
+#endif
+
+wxArrayString EnumerateSerialPorts();
+
+class SerialPort
+{
+public:
+ SerialPort(wxString &port);
+ ~SerialPort();
+
+ bool Open(int speed);
+ bool IsOpened() { return m_opened; }
+ void Close();
+ int RxReady();
+ int Read(char *buffer, int len);
+
+private:
+#ifdef __WIN32__
+ HANDLE m_handle;
+#else
+ int m_fd;
+ struct termios m_ttyset_old;
+ struct termios m_ttyset_new;
+#endif
+ wxString m_port;
+ bool m_opened;
+};
+
+#endif // __SERIAL_H__
diff --git a/ezdop/src/host/hunter/src/settings.cpp b/ezdop/src/host/hunter/src/settings.cpp
new file mode 100644
index 000000000..646769fc9
--- /dev/null
+++ b/ezdop/src/host/hunter/src/settings.cpp
@@ -0,0 +1,310 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application level includes
+#include "settings.h"
+
+HunterSettings::HunterSettings()
+{
+ cfg = new wxConfig(_T("TransmitterHunter"));
+}
+
+HunterSettings::~HunterSettings()
+{
+ if (cfg) {
+ cfg->Flush();
+ delete cfg;
+ }
+}
+
+wxSize HunterSettings::GetWindowSize()
+{
+ long width, height;
+ if (cfg) {
+ cfg->Read(wxT("Application/Width"), &width, 1024);
+ cfg->Read(wxT("Application/Height"), &height, 768);
+ }
+
+ return wxSize((int)width, (int)height);
+}
+
+void HunterSettings::SetWindowSize(wxSize size)
+{
+ if (cfg) {
+ cfg->Write(wxT("Application/Width"), size.GetWidth());
+ cfg->Write(wxT("Application/Height"), size.GetHeight());
+ }
+}
+
+int HunterSettings::GetWindowXPos()
+{
+ long x;
+ if (cfg)
+ cfg->Read(wxT("Application/XPos"), &x, 0);
+
+ return (int)x;
+}
+
+void HunterSettings::SetWindowXPos(int x)
+{
+ if (cfg)
+ cfg->Write(wxT("Application/XPos"), (long)x);
+}
+
+int HunterSettings::GetWindowYPos()
+{
+ long y;
+ if (cfg)
+ cfg->Read(wxT("Application/YPos"), &y, 0);
+
+ return (int)y;
+}
+
+void HunterSettings::SetWindowYPos(int y)
+{
+ if (cfg)
+ cfg->Write(wxT("Application/YPos"), (long)y);
+}
+
+bool HunterSettings::GetDopplerAutostart()
+{
+ bool start = false;
+ if (cfg)
+ cfg->Read(wxT("Doppler/Autostart"), &start, false);
+
+ return start;
+}
+
+void HunterSettings::SetDopplerAutostart(bool start)
+{
+ if (cfg)
+ cfg->Write(wxT("Doppler/Autostart"), start);
+}
+
+int HunterSettings::GetDopplerFilter()
+{
+ long filtering;
+ if (cfg)
+ cfg->Read(wxT("Doppler/FilterLevel"), &filtering, 20);
+
+ return (int)filtering;
+}
+
+void HunterSettings::SetDopplerFilter(int level)
+{
+ if (cfg)
+ cfg->Write(wxT("Doppler/FilterLevel"), (long)level);
+}
+
+int HunterSettings::GetDopplerRotation()
+{
+ long rate;
+ if (cfg)
+ cfg->Read(wxT("Doppler/Rotation"), &rate, 4);
+
+ return (int)rate;
+}
+
+void HunterSettings::SetDopplerRotation(int rate)
+{
+ if (cfg)
+ cfg->Write(wxT("Doppler/Rotation"), (long)rate);
+}
+
+float HunterSettings::GetDopplerCalibration(int rate)
+{
+ double calibration;
+ wxString key;
+
+ key.Printf(_T("Doppler/Rate%iCalibration"), rate);
+ if (cfg)
+ cfg->Read(key, &calibration, 0.0);
+ return (float)calibration;
+}
+
+void HunterSettings::SetDopplerCalibration(int rate, float offset)
+{
+ wxString key;
+ key.Printf(_T("Doppler/Rate%iCalibration"), rate);
+ if (cfg)
+ cfg->Write(key, offset);
+}
+
+bool HunterSettings::GetGPSAutostart()
+{
+ bool start = false;
+ if (cfg)
+ cfg->Read(wxT("GPS/Autostart"), &start, false);
+
+ return start;
+}
+
+void HunterSettings::SetGPSAutostart(bool start)
+{
+ if (cfg)
+ cfg->Write(wxT("GPS/Autostart"), start);
+}
+
+wxString HunterSettings::GetGPSDeviceName()
+{
+ wxString name;
+ if (cfg)
+ cfg->Read(wxT("GPS/DeviceName"), &name);
+
+ return name;
+}
+
+void HunterSettings::SetGPSDeviceName(wxString &name)
+{
+ if (cfg)
+ cfg->Write(wxT("GPS/DeviceName"), name);
+}
+
+bool HunterSettings::GetCalibrationAffectAllRates()
+{
+ bool val = false;
+ if (cfg)
+ cfg->Read(wxT("Calibration/AffectAllRates"), &val, false);
+
+ return val;
+}
+
+void HunterSettings::SetCalibrationAffectAllRates(bool val)
+{
+ if (cfg)
+ cfg->Write(wxT("Calibration/AffectAllRates"), val);
+}
+
+double HunterSettings::GetKnownTransmitterLongitude()
+{
+ double lon;
+ if (cfg)
+ cfg->Read(wxT("KnownTransmitter/Longitude"), &lon);
+
+ return lon;
+}
+
+void HunterSettings::SetKnownTransmitterLongitude(double lon)
+{
+ if (cfg)
+ cfg->Write(wxT("KnownTransmitter/Longitude"), lon);
+}
+
+double HunterSettings::GetKnownTransmitterLatitude()
+{
+ double lat;
+ if (cfg)
+ cfg->Read(wxT("KnownTransmitter/Latitude"), &lat);
+
+ return lat;
+}
+
+void HunterSettings::SetKnownTransmitterLatitude(double lat)
+{
+ if (cfg)
+ cfg->Write(wxT("KnownTransmitter/Latitude"), lat);
+}
+
+bool HunterSettings::GetUseKnownTransmitter()
+{
+ bool use = false;
+ if (cfg)
+ cfg->Read(wxT("KnownTransmitter/Use"), &use, false);
+
+ return use;
+}
+
+void HunterSettings::SetUseKnownTransmitter(bool use)
+{
+ if (cfg)
+ cfg->Write(wxT("KnownTransmitter/Use"), use);
+}
+
+int HunterSettings::GetDisplayOrientation()
+{
+ long x;
+ if (cfg)
+ cfg->Read(wxT("Display/Orientation"), &x, 0);
+
+ return (int)x;
+}
+
+void HunterSettings::SetDisplayOrientation(int orientation)
+{
+ if (cfg)
+ cfg->Write(wxT("Display/Orientation"), (long)orientation);
+}
+
+bool HunterSettings::GetDisplayDoppler()
+{
+ bool val = false;
+ if (cfg)
+ cfg->Read(wxT("Display/DopplerPointer"), &val, false);
+
+ return val;
+}
+
+void HunterSettings::SetDisplayDoppler(bool val)
+{
+ if (cfg)
+ cfg->Write(wxT("Display/DopplerPointer"), val);
+}
+
+bool HunterSettings::GetDisplayKnown()
+{
+ bool val = false;
+ if (cfg)
+ cfg->Read(wxT("Display/KnownPointer"), &val, false);
+
+ return val;
+}
+
+void HunterSettings::SetDisplayKnown(bool val)
+{
+ if (cfg)
+ cfg->Write(wxT("Display/KnownPointer"), val);
+}
+
+bool HunterSettings::GetDisplayEstimated()
+{
+ bool val = false;
+ if (cfg)
+ cfg->Read(wxT("Display/EstimatedPointer"), &val, false);
+
+ return val;
+}
+
+void HunterSettings::SetDisplayEstimated(bool val)
+{
+ if (cfg)
+ cfg->Write(wxT("Display/EstimatedPointer"), val);
+}
+
+wxString HunterSettings::GetWorkingDirectory()
+{
+ wxString str(_T("."));
+ if (cfg)
+ return cfg->Read(wxT("Application/WorkingDirectory"), str);
+ return str;
+}
+
+void HunterSettings::SetWorkingDirectory(const wxString &dir)
+{
+ if (cfg)
+ cfg->Write(wxT("Application/WorkingDirectory"), dir);
+}
diff --git a/ezdop/src/host/hunter/src/settings.h b/ezdop/src/host/hunter/src/settings.h
new file mode 100644
index 000000000..66e03775b
--- /dev/null
+++ b/ezdop/src/host/hunter/src/settings.h
@@ -0,0 +1,101 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __SETTINGS_H__
+#define __SETTINGS_H__
+
+// wxWidgets includes
+#include <wx/config.h>
+#include <wx/gdicmn.h>
+
+class HunterSettings
+{
+public:
+ HunterSettings();
+ ~HunterSettings();
+
+ // Main window size
+ wxSize GetWindowSize();
+ void SetWindowSize(wxSize size);
+
+ // Main window position
+ int GetWindowXPos();
+ void SetWindowXPos(int x);
+ int GetWindowYPos();
+ void SetWindowYPos(int y);
+
+ // Autostart doppler on application bringup
+ bool GetDopplerAutostart();
+ void SetDopplerAutostart(bool val);
+
+ // Doppler filter value
+ int GetDopplerFilter();
+ void SetDopplerFilter(int level);
+
+ // Doppler filter value
+ int GetDopplerRotation();
+ void SetDopplerRotation(int rate);
+
+ // Doppler calibration values
+ float GetDopplerCalibration(int rate);
+ void SetDopplerCalibration(int rate, float offset);
+
+ // Autostart GPS on application bringup
+ bool GetGPSAutostart();
+ void SetGPSAutostart(bool val);
+
+ // GPS interface device
+ wxString GetGPSDeviceName();
+ void SetGPSDeviceName(wxString &name);
+
+ // Calibration adjust affects all rates
+ bool GetCalibrationAffectAllRates();
+ void SetCalibrationAffectAllRates(bool val);
+
+ // Known transmitter location
+ double GetKnownTransmitterLongitude();
+ void SetKnownTransmitterLongitude(double lon);
+ double GetKnownTransmitterLatitude();
+ void SetKnownTransmitterLatitude(double lat);
+ bool GetUseKnownTransmitter();
+ void SetUseKnownTransmitter(bool use);
+
+ // Display Orientation
+ int GetDisplayOrientation();
+ void SetDisplayOrientation(int orientation);
+
+ // Display Doppler Bearing
+ bool GetDisplayDoppler();
+ void SetDisplayDoppler(bool val);
+
+ // Display Known Bearing
+ bool GetDisplayKnown();
+ void SetDisplayKnown(bool val);
+
+ // Display Estimated Bearing
+ bool GetDisplayEstimated();
+ void SetDisplayEstimated(bool val);
+
+ wxString GetWorkingDirectory();
+ void SetWorkingDirectory(const wxString &dir);
+
+private:
+ wxConfig *cfg;
+};
+
+#endif // __SETTINGS_H__
diff --git a/ezdop/src/host/hunter/src/spherical.cc b/ezdop/src/host/hunter/src/spherical.cc
new file mode 100644
index 000000000..2af34aa8e
--- /dev/null
+++ b/ezdop/src/host/hunter/src/spherical.cc
@@ -0,0 +1,76 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application level includes
+#include "spherical.h"
+#include "util.h"
+
+Spherical::Spherical()
+{
+ m_latitude = 0.0;
+ m_longitude = 0.0;
+}
+
+Spherical::Spherical(double latitude, double longitude)
+{
+ m_latitude = latitude;
+ m_longitude = longitude;
+}
+
+Spherical::Spherical(wxString latitude, wxString longitude)
+{
+ latitude.ToDouble(&m_latitude);
+ longitude.ToDouble(&m_longitude);
+}
+
+void Spherical::SetLatitude(double latitude)
+{
+ m_latitude = latitude; // TODO: error handle
+}
+
+void Spherical::SetLongitude(double longitude)
+{
+ m_longitude = longitude;
+}
+
+double range(const Spherical &from, const Spherical &to)
+{
+ double lat1 = to_radians(from.m_latitude);
+ double lon1 = to_radians(from.m_longitude);
+
+ double lat2 = to_radians(to.m_latitude);
+ double lon2 = to_radians(to.m_longitude);
+
+ double n1 = sin((lat1-lat2)/2);
+ double n2 = sin((lon1-lon2)/2);
+ double distance = 2*asin(sqrt(n1*n1 + cos(lat1)*cos(lat2)*n2*n2));
+
+ return to_degrees(distance)*60.0*1.15077945; // Conversion to statute miles
+
+}
+
+double bearing(const Spherical &from, const Spherical &to)
+{
+ double lat1 = to_radians(from.m_latitude);
+ double lon1 = to_radians(from.m_longitude);
+ double lat2 = to_radians(to.m_latitude);
+ double lon2 = to_radians(to.m_longitude);
+
+ double bearing = atan2(-sin(lon1-lon2)*cos(lat2), cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(lon1-lon2));
+ return degree_normalize(to_degrees(bearing));
+}
diff --git a/ezdop/src/host/hunter/src/spherical.h b/ezdop/src/host/hunter/src/spherical.h
new file mode 100644
index 000000000..f450db769
--- /dev/null
+++ b/ezdop/src/host/hunter/src/spherical.h
@@ -0,0 +1,45 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __SPHERICAL_H__
+#define __SPHERICAL_H__
+
+// wxWidgets includes
+#include <wx/string.h>
+
+class Spherical
+{
+public:
+ Spherical();
+ Spherical(double latitude, double longitude);
+ Spherical(wxString latitude, wxString longitude);
+
+ double Latitude() const { return m_latitude; }
+ double Longitude() const { return m_longitude; }
+ void SetLatitude(double latitude);
+ void SetLongitude(double longitude);
+
+private:
+ double m_latitude;
+ double m_longitude;
+
+ friend double range(const Spherical &from, const Spherical &to);
+ friend double bearing(const Spherical &from, const Spherical &to);
+};
+
+#endif // __SPHERICAL_H__
diff --git a/ezdop/src/host/hunter/src/tactical.cc b/ezdop/src/host/hunter/src/tactical.cc
new file mode 100644
index 000000000..4489357a9
--- /dev/null
+++ b/ezdop/src/host/hunter/src/tactical.cc
@@ -0,0 +1,142 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Application level includes
+#include "tactical.h"
+
+// wxWidgets includes
+#include <wx/dc.h>
+#include <wx/dcclient.h>
+#include <wx/log.h>
+
+// System level includes
+#include <cmath>
+
+// Event table for TacticalPanel
+BEGIN_EVENT_TABLE(TacticalPanel, wxPanel)
+ EVT_PAINT(TacticalPanel::OnPaint)
+ EVT_SIZE(TacticalPanel::OnSize)
+END_EVENT_TABLE()
+
+TacticalPanel::TacticalPanel(wxWindow *parent) :
+wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
+{
+ m_orientation = TrackUp;
+ m_display_doppler = false;
+ m_display_known = false;
+ m_display_estimated = false;
+
+ m_heading = 0.0;
+ m_doppler_bearing = -1.0;
+ m_estimated_bearing = -1.0;
+ m_actual_bearing = -1.0;
+
+ SetBackgroundColour(*wxBLACK);
+}
+
+void TacticalPanel::OnPaint(wxPaintEvent &event)
+{
+ wxPaintDC dc(this);
+ drawPanel(dc);
+}
+
+void TacticalPanel::drawPanel(wxDC &dc)
+{
+ float radians;
+ float brg = 0;
+
+ // Draw circle
+ dc.SetPen(wxPen(*wxRED, 2, wxSOLID));
+ dc.SetBrush(wxBrush(*wxBLACK, wxTRANSPARENT));
+ dc.DrawCircle(m_center, m_radius);
+
+ // Calculate end of doppler bearing line
+ // Doppler bearings are relative and must be adjusted for north up display
+ wxPoint doppler_tip = m_center;
+ if (m_doppler_bearing >= 0.0) {
+ brg = m_doppler_bearing;
+ if (m_orientation == NorthUp) {
+ brg += m_heading;
+ if (brg >= 360.0)
+ brg -= 360.0;
+ }
+ radians = brg*M_PI/180.0;
+ doppler_tip = wxPoint((int)(m_center.x+sin(radians)*m_radius*0.95),
+ (int)(m_height-(m_center.y+cos(radians)*m_radius*0.95)));
+ }
+
+ // Calculate end of actual bearing line
+ // Actual bearings are absolute and must be adjusted for track up display
+ wxPoint actual_tip = m_center;
+ if (m_actual_bearing >= 0.0) {
+ brg = m_actual_bearing;
+ if (m_orientation == TrackUp) {
+ brg -= m_heading;
+ if (brg < 0.0)
+ brg += 360.0;
+ }
+ radians = brg*M_PI/180.0;
+ actual_tip = wxPoint((int)(m_center.x+sin(radians)*m_radius*0.95),
+ (int)(m_height-(m_center.y+cos(radians)*m_radius*0.95)));
+ }
+
+ // Calculate end of estimated bearing line
+ // Estimated bearings are absolute and must be adjusted for track up display
+ wxPoint estimated_tip = m_center;
+ if (m_estimated_bearing >= 0.0) {
+ brg = m_estimated_bearing;
+ if (m_orientation == TrackUp) {
+ brg -= m_heading;
+ if (brg < 0.0)
+ brg += 360.0;
+ }
+ radians = brg*M_PI/180.0;
+ estimated_tip = wxPoint((int)(m_center.x+sin(radians)*m_radius*0.95),
+ (int)(m_height-(m_center.y+cos(radians)*m_radius*0.95)));
+ }
+
+ if (m_display_known) {
+ dc.SetPen(wxPen(*wxBLUE, 10, wxSOLID));
+ dc.DrawLine(m_center, actual_tip);
+ }
+
+ if (m_display_estimated) {
+ dc.SetPen(wxPen(*wxWHITE, 10, wxSOLID));
+ dc.DrawLine(m_center, estimated_tip);
+ }
+
+ if (m_display_doppler) {
+ dc.SetPen(wxPen(*wxGREEN, 10, wxSOLID));
+ dc.DrawLine(m_center, doppler_tip);
+ }
+}
+
+void TacticalPanel::OnSize(wxSizeEvent &event)
+{
+ GetClientSize(&m_width, &m_height);
+ m_center = wxPoint(m_width/2, m_height/2);
+
+ // Circle at 95% of window size
+ if (m_width > m_height)
+ m_radius = m_height/2;
+ else
+ m_radius = m_width/2;
+ m_radius = (int)(m_radius*0.95);
+
+ Refresh();
+}
diff --git a/ezdop/src/host/hunter/src/tactical.h b/ezdop/src/host/hunter/src/tactical.h
new file mode 100644
index 000000000..4c26495b5
--- /dev/null
+++ b/ezdop/src/host/hunter/src/tactical.h
@@ -0,0 +1,73 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __TACTICAL_H__
+#define __TACTICAL_H__
+
+// wxWidgets includes
+#include <wx/panel.h>
+
+enum Orientation {
+ TrackUp,
+ NorthUp
+};
+
+class TacticalPanel : public wxPanel
+{
+public:
+ TacticalPanel(wxWindow *parent);
+
+ // Event handlers
+ void OnPaint(wxPaintEvent &event);
+ void OnSize(wxSizeEvent &event);
+
+ // Configuration
+ void SetOrientation(Orientation orientation) { m_orientation = orientation; Refresh(); }
+ void SetDisplayDoppler(bool display) { m_display_doppler = display; Refresh(); }
+ void SetDisplayKnown(bool display) { m_display_known = display; Refresh(); }
+ void SetDisplayEstimated(bool display) { m_display_estimated = display; Refresh(); }
+
+ // State updates
+ void SetHeading(float heading) { m_heading = heading; Refresh(); }
+ void SetDopplerBearing(float bearing) { m_doppler_bearing = bearing; Refresh(); }
+ void SetEstimatedBearing(float bearing) { m_estimated_bearing = bearing; Refresh(); }
+ void SetActualBearing(float bearing) { m_actual_bearing = bearing; Refresh(); }
+
+private:
+ Orientation m_orientation;
+ bool m_display_doppler;
+ bool m_display_known;
+ bool m_display_estimated;
+
+ float m_heading;
+ float m_doppler_bearing;
+ float m_estimated_bearing;
+ float m_actual_bearing;
+
+ void drawPanel(wxDC &dc);
+
+ // Window size derived parameters
+ wxPoint m_center;
+ int m_width;
+ int m_height;
+ int m_radius;
+
+ DECLARE_EVENT_TABLE();
+};
+
+#endif // __TACTICAL_H__
diff --git a/ezdop/src/host/hunter/src/util.h b/ezdop/src/host/hunter/src/util.h
new file mode 100644
index 000000000..53e0f39d6
--- /dev/null
+++ b/ezdop/src/host/hunter/src/util.h
@@ -0,0 +1,79 @@
+/*
+ Copyright 2006 Johnathan Corgan.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radio; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+// System level includes
+#include <cmath>
+
+inline double limit(double x, double lower, double upper)
+{
+ if (x < lower)
+ return lower;
+ else if (x > upper)
+ return upper;
+ else
+ return x;
+}
+
+inline int limit(int x, int lower, int upper)
+{
+ if (x < lower)
+ return lower;
+ else if (x > upper)
+ return upper;
+ else
+ return x;
+}
+
+inline double degree_normalize(double degrees)
+{
+ if (degrees >= 360.0)
+ return degrees - 360.0;
+ else if (degrees < 0.0)
+ return degrees + 360.0;
+ else
+ return degrees;
+}
+
+inline int degree_normalize(int degrees)
+{
+ if (degrees >= 360)
+ return degrees - 360;
+ else if (degrees < 0)
+ return degrees + 360;
+ else
+ return degrees;
+}
+
+inline double to_radians(double degrees)
+{
+ return degrees/180.0*M_PI;
+}
+
+inline double to_degrees(double radians)
+{
+ return radians/M_PI*180.0;
+}
+
+#define LOGFUNCTION wxLogDebug("%s", __PRETTY_FUNCTION__)
+#define LOGFUNCTIONENTRY wxLogDebug("%s: entered", __PRETTY_FUNCTION__)
+#define LOGFUNCTIONEXIT wxLogDebug("%s: exited", __PRETTY_FUNCTION__)
+
+#endif // __UTIL_H__
diff --git a/ezdop/src/host/tests/Makefile.am b/ezdop/src/host/tests/Makefile.am
new file mode 100644
index 000000000..fcf19525d
--- /dev/null
+++ b/ezdop/src/host/tests/Makefile.am
@@ -0,0 +1,31 @@
+# Copyright 2001,2002,2003,2004,2005,2006 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+include $(top_srcdir)/Makefile.common
+
+INCLUDES=$(EZDOP_INCLUDES)
+
+noinst_PROGRAMS = \
+ dopper
+
+dopper_SOURCES = \
+ dopper.cc
+
+dopper_LDADD = \
+ $(top_builddir)/ezdop/src/host/ezdop/libezdop.la
diff --git a/ezdop/src/host/tests/dopper.cc b/ezdop/src/host/tests/dopper.cc
new file mode 100644
index 000000000..a7e79c53d
--- /dev/null
+++ b/ezdop/src/host/tests/dopper.cc
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <cstdio>
+#include <ezdop.h>
+
+int chunks = 3600;
+const static int samples = 250;
+int rate = 250;
+
+static complex<float> buffer[samples];
+
+int main(int argc, char *argv)
+{
+ ezdop *dop = new ezdop();
+
+ printf("Initializing EZDOP...");
+ if (dop->init())
+ printf("done.\n");
+ else
+ printf("failed.\n");
+ printf("EZDOP reports %s.\n", dop->is_online() ? "online" : "offline");
+
+ printf("Setting EZDOP rate...");
+ if (dop->set_rate(rate))
+ printf("done.\n");
+ else
+ printf("failed.\n");
+
+ printf("Telling EZDOP to rotate...");
+ if (dop->rotate())
+ printf("done.\n");
+ else
+ printf("failed.\n");
+
+ printf("Telling EZDOP to stream...");
+ if (dop->stream())
+ printf("done.\n");
+ else
+ printf("failed.\n");
+
+ for (int i = 0; i < chunks; i++) {
+ printf("Asking EZDOP for %i samples...", samples);
+ int rd = dop->read_iq(buffer, samples);
+ printf("got %i --- ", rd);
+ if (rd != samples)
+ printf("*****\n");
+
+ complex<float> average = complex<float>(0.0, 0.0);
+ for (int j = 0; j < rd; j++) {
+ average += buffer[j];
+ }
+
+ float I = average.real()/rd;
+ float Q = average.imag()/rd;
+ float M = std::sqrt(I*I+Q*Q);
+ float dbfs = 20*std::log(M/1.4142135);
+
+ printf("I=%f Q=%f M=%f dbfs=%f\n", I, Q, M, dbfs);
+ }
+
+ printf("Telling EZDOP to stop streaming...");
+ if (dop->stop_streaming())
+ printf("done.\n");
+ else
+ printf("failed.\n");
+
+ printf("Telling EZDOP to stop stop rotating...");
+ if (dop->stop_rotating())
+ printf("done.\n");
+ else
+ printf("failed.\n");
+
+ printf("Releasing EZDOP...");
+ if (dop->finish())
+ printf("done.\n");
+ else
+ printf("failed.\n");
+
+ delete dop;
+}