diff --git a/Aircraft.cpp b/Aircraft.cpp new file mode 100644 index 0000000..ce1a1ad --- /dev/null +++ b/Aircraft.cpp @@ -0,0 +1,33 @@ +#include "Aircraft.h" + + + +Aircraft::Aircraft(struct aircraft *a) { + addr = a->addr; + created = 0; + oldIdx = 0; + prev_seen = 0; + + x = 0; + y = 0; + cx = 0; + cy = 0; + + ox = 0; + oy = 0; + dox = 0; + doy = 0; + ddox = 0; + ddoy = 0; + + memset(oldLon, 0, sizeof(oldLon)); + memset(oldLat, 0, sizeof(oldLat)); + memset(oldHeading, 0, sizeof(oldHeading)); +} + + +Aircraft::~Aircraft() { + free(oldLat); + free(oldLon); + free(oldHeading); +} \ No newline at end of file diff --git a/Aircraft.h b/Aircraft.h new file mode 100644 index 0000000..4eb0b5c --- /dev/null +++ b/Aircraft.h @@ -0,0 +1,43 @@ + #include +#include "defs.h" +#include + +class Aircraft { +public: + uint32_t addr; // ICAO address + char flight[16]; // Flight number + unsigned char signalLevel[8]; // Last 8 Signal Amplitudes + double messageRate; + int altitude; // Altitude + int speed; // Velocity + int track; // Angle of flight + int vert_rate; // Vertical rate. + time_t seen; // Time at which the last packet was received + time_t seenLatLon; // Time at which the last packet was received + time_t prev_seen; + double lat, lon; // Coordinated obtained from CPR encoded data + + //history + float oldLon[TRAIL_LENGTH]; + float oldLat[TRAIL_LENGTH]; + float oldHeading[TRAIL_LENGTH]; + time_t oldSeen[TRAIL_LENGTH]; + uint8_t oldIdx; + uint64_t created; + uint64_t msSeen; + uint64_t msSeenLatLon; + int live; + + struct Aircraft *next; // Next aircraft in our linked list + +//// label stuff -> should go to aircraft icon class + + int x, y, cx, cy, w, h; + float ox, oy, dox, doy, ddox, ddoy; + float pressure; + +/// methods + + Aircraft(struct aircraft *a); + ~Aircraft(); +}; \ No newline at end of file diff --git a/AircraftData.cpp b/AircraftData.cpp new file mode 100644 index 0000000..3073b1d --- /dev/null +++ b/AircraftData.cpp @@ -0,0 +1,131 @@ +#include "AircraftData.h" + +// +//carried over from view1090.c +// + + +int AircraftData::setupConnection(struct client *c) { + int fd; + + // Try to connect to the selected ip address and port. We only support *ONE* input connection which we initiate.here. + if ((fd = anetTcpConnect(modes.aneterr, View1090.net_input_beast_ipaddr, modes.net_input_beast_port)) != ANET_ERR) { + anetNonBlock(modes.aneterr, fd); + // + // Setup a service callback client structure for a beast binary input (from dump1090) + // This is a bit dodgy under Windows. The fd parameter is a handle to the internet + // socket on which we are receiving data. Under Linux, these seem to start at 0 and + // count upwards. However, Windows uses "HANDLES" and these don't nececeriy start at 0. + // dump1090 limits fd to values less than 1024, and then uses the fd parameter to + // index into an array of clients. This is ok-ish if handles are allocated up from 0. + // However, there is no gaurantee that Windows will behave like this, and if Windows + // allocates a handle greater than 1024, then dump1090 won't like it. On my test machine, + // the first Windows handle is usually in the 0x54 (84 decimal) region. + + c->next = NULL; + c->buflen = 0; + c->fd = + c->service = + modes.bis = fd; + modes.clients = c; + } + return fd; +} + +// +// end view1090.c +// + + +void AircraftData::initialize() { + // Allocate the various buffers used by Modes + if ( NULL == (modes.icao_cache = (uint32_t *) malloc(sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2))) + { + fprintf(stderr, "Out of memory allocating data buffer.\n"); + exit(1); + } + + // Clear the buffers that have just been allocated, just in-case + memset(modes.icao_cache, 0, sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2); + + // Prepare error correction tables + modesInitErrorInfo(&(modes)); +} + + +void AircraftData::connect() { + // Try to connect to the selected ip address and port. We only support *ONE* input connection which we initiate.here. + c = (struct client *) malloc(sizeof(*c)); + while(1) { + if ((fd = setupConnection(c)) == ANET_ERR) { + fprintf(stderr, "Waiting on %s:%d\n", View1090.net_input_beast_ipaddr, modes.net_input_beast_port); + sleep(1); + } else { + break; + } + } + +} + + +void AircraftData::disconnect() { + if (fd != ANET_ERR) + {close(fd);} +} + + +void AircraftData::update() { + if ((fd == ANET_ERR) || (recv(c->fd, pk_buf, sizeof(pk_buf), MSG_PEEK | MSG_DONTWAIT) == 0)) { + free(c); + usleep(1000000); + c = (struct client *) malloc(sizeof(*c)); + fd = setupConnection(c); + return; + } + modesReadFromClient(&modes, c,"",decodeBinMessage); + + interactiveRemoveStaleAircrafts(&modes); + + aircraftList.update(); +} + +AircraftData::AircraftData(){ + // Default everything to zero/NULL + memset(&modes, 0, sizeof(Modes)); + memset(&View1090, 0, sizeof(View1090)); + + // Now initialise things that should not be 0/NULL to their defaults + modes.check_crc = 1; + strcpy(View1090.net_input_beast_ipaddr,VIEW1090_NET_OUTPUT_IP_ADDRESS); + modes.net_input_beast_port = MODES_NET_OUTPUT_BEAST_PORT; + modes.interactive_rows = MODES_INTERACTIVE_ROWS; + modes.interactive_delete_ttl = MODES_INTERACTIVE_DELETE_TTL; + modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL; + modes.fUserLat = MODES_USER_LATITUDE_DFLT; + modes.fUserLon = MODES_USER_LONGITUDE_DFLT; + + modes.interactive = 0; + modes.quiet = 1; + + // Map options + appData.maxDist = 25.0; + appData.centerLon = modes.fUserLon; + appData.centerLat = modes.fUserLat; + + // Display options + appData.screen_uiscale = 1; + appData.screen_width = 0; + appData.screen_height = 0; + appData.screen_depth = 32; + appData.fullscreen = 0; + + // Initialize status + Status.msgRate = 0; + Status.avgSig = 0; + Status.numPlanes = 0; + Status.numVisiblePlanes = 0; + Status.maxDist = 0; + + selectedAircraft = NULL; +} + diff --git a/AircraftData.h b/AircraftData.h new file mode 100644 index 0000000..cb35012 --- /dev/null +++ b/AircraftData.h @@ -0,0 +1,34 @@ +#ifndef AIRCRAFTDATA_H +#define AIRCRAFTDATA_H + +#include "dump1090.h" +#include "view1090.h" +#include "structs.h" + +#include "AircraftList.h" + +class AircraftData { + private: + //from view1090.c + + int setupConnection(struct client *c); + + // + + struct client *c; + int fd; + char pk_buf[8]; + + public: + void initialize(); + void connect(); + void disconnect(); + void update(); + AircraftData(); + + AircraftList aircraftList; + Aircraft *selectedAircraft; + Modes modes; +}; + +#endif \ No newline at end of file diff --git a/planeObj.c b/AircraftList.cpp similarity index 64% rename from planeObj.c rename to AircraftList.cpp index 0f7740e..300e836 100644 --- a/planeObj.c +++ b/AircraftList.cpp @@ -1,4 +1,4 @@ -#include "structs.h" +#include "AircraftList.h" #include "dump1090.h" static uint64_t mstime(void) { @@ -11,8 +11,8 @@ static uint64_t mstime(void) { return mst; } -PlaneObj *findPlaneObj(uint32_t addr) { - PlaneObj *p = appData.planes; +Aircraft *AircraftList::find(uint32_t addr) { + Aircraft *p = head; while(p) { if (p->addr == addr) return (p); @@ -20,40 +20,13 @@ PlaneObj *findPlaneObj(uint32_t addr) { } return (NULL); } - -PlaneObj *createPlaneObj(struct aircraft *a) { - PlaneObj *p = (PlaneObj *) malloc(sizeof(*p)); - - memset(p, 0, sizeof(*p)); - - p->addr = a->addr; - p->created = 0; - p->oldIdx = 0; - p->prev_seen = 0; - - p->x = 0; - p->y = 0; - p->cx = 0; - p->cy = 0; - - p->ox = 0; - p->oy = 0; - p->dox = 0; - p->doy = 0; - p->ddox = 0; - p->ddoy = 0; - - memset(p->oldLon, 0, sizeof(p->oldLon)); - memset(p->oldLat, 0, sizeof(p->oldLat)); - memset(p->oldHeading, 0, sizeof(p->oldHeading)); - - return (p); -} - -void updatePlanes() { + +void AircraftList::update +() { + //instead of this, net_io should call this class directly to update info struct aircraft *a = modes.aircrafts; - PlaneObj *p = appData.planes; + Aircraft *p = head; while(p) { p->live = 0; @@ -62,11 +35,12 @@ void updatePlanes() { while(a) { - p = findPlaneObj(a->addr); + p = find(a->addr); if (!p) { - p = createPlaneObj(a); - p->next = appData.planes; - appData.planes = p; + //p = createPlaneObj(a); + p = new Aircraft(a); + p->next = head; + head = p; } else { p->prev_seen = p->seen; } @@ -113,18 +87,18 @@ void updatePlanes() { a = a->next; } - p = appData.planes; - PlaneObj *prev = NULL; + p = head; + Aircraft *prev = NULL; while(p) { if(!p->live) { if (!prev) { - appData.planes = p->next; - free(p); - p = appData.planes; + head = p->next; + delete(p); + p = head; } else { prev->next = p->next; - free(p); + delete(p); p = prev->next; } } else { @@ -132,4 +106,16 @@ void updatePlanes() { p = p->next; } } +} + +AircraftList::AircraftList() { + head = NULL; +} + +AircraftList::~AircraftList() { + while(head != NULL) { + Aircraft *temp = head; + head = head->next; + delete(temp); + } } \ No newline at end of file diff --git a/AircraftList.h b/AircraftList.h new file mode 100644 index 0000000..26dd452 --- /dev/null +++ b/AircraftList.h @@ -0,0 +1,12 @@ +#include "Aircraft.h" + +class AircraftList { + public: + Aircraft *head; + + Aircraft *find(uint32_t addr); + void update(); + + AircraftList(); + ~AircraftList(); +}; diff --git a/input.c b/Input.cpp similarity index 88% rename from input.c rename to Input.cpp index 7489e64..30288a2 100644 --- a/input.c +++ b/Input.cpp @@ -1,6 +1,8 @@ #include "structs.h" #include "view1090.h" +#include "Input.h" + static uint64_t mstime(void) { struct timeval tv; uint64_t mst; @@ -11,7 +13,7 @@ static uint64_t mstime(void) { return mst; } -void getInput() +void Input::getInput() { SDL_Event event; @@ -60,7 +62,7 @@ void getInput() appData.touchy = event.motion.y; appData.tapCount = event.button.clicks; - registerClick(); + view->registerClick(); appData.isDragging = 0; break; @@ -72,9 +74,13 @@ void getInput() if (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(SDL_BUTTON_LEFT)) { appData.isDragging = 1; - moveCenterRelative(event.motion.xrel, event.motion.yrel); + view->moveCenterRelative(event.motion.xrel, event.motion.yrel); } break; } } } + +Input::Input(View *view) { + this->view = view; +} diff --git a/Input.h b/Input.h new file mode 100644 index 0000000..36b269f --- /dev/null +++ b/Input.h @@ -0,0 +1,17 @@ +#ifndef INPUT_H +#define INPUT + +#include "AircraftData.h" +#include "View.h" + +class Input { +public: + void getInput(); + + //should input know about view? + Input(View *view); + + View *view; +}; + +#endif \ No newline at end of file diff --git a/Makefile b/Makefile index f8ca163..b04db4a 100644 --- a/Makefile +++ b/Makefile @@ -2,19 +2,18 @@ # When building a package or installing otherwise in the system, make # sure that the variable PREFIX is defined, e.g. make PREFIX=/usr/local # -PROGNAME=view1090 CFLAGS=-O2 -g -Wall -W LIBS=-lm -lSDL2 -lSDL2_ttf -lSDL2_gfx CC=g++ -all: view1090 +all: map1090 -%.o: %.c +%.o: %.c %.cpp $(CC) $(CFLAGS) $(EXTRACFLAGS) -c $< -view1090: view1090.o anet.o interactive.o planeObj.o mode_ac.o mode_s.o net_io.o input.o draw.o font.o init.o mapdata.o status.o parula.o monokai.o - $(CC) -g -o view1090 view1090.o anet.o interactive.o planeObj.o mode_ac.o mode_s.o net_io.o input.o draw.o font.o init.o mapdata.o status.o parula.o monokai.o $(LIBS) $(LDFLAGS) +map1090: map1090.o AircraftData.o AircraftList.o Aircraft.o anet.o interactive.o mode_ac.o mode_s.o net_io.o Input.o View.o font.o init.o mapdata.o status.o parula.o monokai.o + $(CC) -g -o map1090 map1090.o AircraftData.o AircraftList.o Aircraft.o anet.o interactive.o mode_ac.o mode_s.o net_io.o Input.o View.o font.o init.o mapdata.o status.o parula.o monokai.o $(LIBS) $(LDFLAGS) clean: - rm -f *.o view1090 + rm -f *.o map1090 diff --git a/draw.c b/View.cpp similarity index 94% rename from draw.c rename to View.cpp index 6602458..156e65b 100644 --- a/draw.c +++ b/View.cpp @@ -7,6 +7,8 @@ #include "parula.h" #include "monokai.h" +#include "View.h" + static uint64_t mstime(void) { struct timeval tv; uint64_t mst; @@ -17,11 +19,11 @@ static uint64_t mstime(void) { return mst; } -float sign(float x) { +static float sign(float x) { return (x > 0) - (x < 0); } -float clamp(float in, float min, float max) { +static float clamp(float in, float min, float max) { float out = in; if(in < min) { @@ -35,7 +37,7 @@ float clamp(float in, float min, float max) { return out; } -void CROSSVP(float *v, float *u, float *w) +static void CROSSVP(float *v, float *u, float *w) { v[0] = u[1]*w[2] - u[2]*(w)[1]; v[1] = u[2]*w[0] - u[0]*(w)[2]; @@ -141,7 +143,7 @@ SDL_Color hsv2SDLColor(float h, float s, float v) return out; } -int screenDist(float d) { +int View::screenDist(float d) { float scale_factor = (appData.screen_width > appData.screen_height) ? appData.screen_width : appData.screen_height; return round(0.95 * scale_factor * 0.5 * fabs(d) / appData.maxDist); } @@ -157,7 +159,7 @@ void pxFromLonLat(float *dx, float *dy, float lon, float lat) { *dy = 6371.0 * (lat - appData.centerLat) * M_PI / 180.0f; } -void latLonFromScreenCoords(float *lat, float *lon, int x, int y) { +void View::latLonFromScreenCoords(float *lat, float *lon, int x, int y) { float scale_factor = (appData.screen_width > appData.screen_height) ? appData.screen_width : appData.screen_height; float dx = appData.maxDist * (x - (appData.screen_width>>1)) / (0.95 * scale_factor * 0.5 ); @@ -168,12 +170,12 @@ void latLonFromScreenCoords(float *lat, float *lon, int x, int y) { } -void screenCoords(int *outX, int *outY, float dx, float dy) { +void View::screenCoords(int *outX, int *outY, float dx, float dy) { *outX = (appData.screen_width>>1) + ((dx>0) ? 1 : -1) * screenDist(dx); *outY = (appData.screen_height * CENTEROFFSET) + ((dy>0) ? -1 : 1) * screenDist(dy); } -int outOfBounds(int x, int y) { +int View::outOfBounds(int x, int y) { if(x < 0 || x >= appData.screen_width || y < 0 || y >= appData.screen_height ) { return 1; } else { @@ -181,7 +183,7 @@ int outOfBounds(int x, int y) { } } -void drawPlaneOffMap(int x, int y, int *returnx, int *returny, SDL_Color planeColor) { +void View::drawPlaneOffMap(int x, int y, int *returnx, int *returny, SDL_Color planeColor) { float arrowWidth = 6.0 * appData.screen_uiscale; @@ -239,7 +241,7 @@ void drawPlaneOffMap(int x, int y, int *returnx, int *returny, SDL_Color planeCo *returny = y3; } -void drawPlaneIcon(int x, int y, float heading, SDL_Color planeColor) +void View::drawPlaneIcon(int x, int y, float heading, SDL_Color planeColor) { float body = 8.0 * appData.screen_uiscale; float wing = 6.0 * appData.screen_uiscale; @@ -286,7 +288,7 @@ void drawPlaneIcon(int x, int y, float heading, SDL_Color planeColor) filledTrigonRGBA (appData.renderer, x1, y1, x2, y2, x+round(-body*.5*vec[0]), y+round(-body*.5*vec[1]),planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); } -void drawTrail(float *oldDx, float *oldDy, float *oldHeading, time_t * oldSeen, int idx) { +void View::drawTrail(float *oldDx, float *oldDy, float *oldHeading, time_t * oldSeen, int idx) { int currentIdx, prevIdx; @@ -377,7 +379,7 @@ void drawTrail(float *oldDx, float *oldDy, float *oldHeading, time_t * oldSeen, } } -void drawScaleBars() +void View::drawScaleBars() { int scalePower = 0; int scaleBarDist = screenDist((float)pow(10,scalePower)); @@ -407,7 +409,7 @@ void drawScaleBars() lineRGBA(appData.renderer,10,10+5*appData.screen_uiscale,10+scaleBarDist,10+5*appData.screen_uiscale, style.scaleBarColor.r, style.scaleBarColor.g, style. scaleBarColor.b, 255); } -void drawPolys(QuadTree *tree, float screen_lat_min, float screen_lat_max, float screen_lon_min, float screen_lon_max) { +void View::drawPolys(QuadTree *tree, float screen_lat_min, float screen_lat_max, float screen_lon_min, float screen_lon_max) { if(tree == NULL) { return; } @@ -498,7 +500,7 @@ void drawPolys(QuadTree *tree, float screen_lat_min, float screen_lat_max, float } } -void drawGeography() { +void View::drawGeography() { float screen_lat_min, screen_lat_max, screen_lon_min, screen_lon_max; latLonFromScreenCoords(&screen_lat_min, &screen_lon_min, 0, appData.screen_height * -0.2); @@ -507,7 +509,7 @@ void drawGeography() { drawPolys(&(appData.root), screen_lat_min, screen_lat_max, screen_lon_min, screen_lon_max); } -void drawSignalMarks(PlaneObj *p, int x, int y) { +void View::drawSignalMarks(Aircraft *p, int x, int y) { unsigned char * pSig = p->signalLevel; unsigned int signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] + pSig[4] + pSig[5] + pSig[6] + pSig[7] + 3) >> 3; @@ -525,7 +527,7 @@ void drawSignalMarks(PlaneObj *p, int x, int y) { } -void drawPlaneText(PlaneObj *p) { +void View::drawPlaneText(Aircraft *p) { int maxCharCount = 0; int currentCharCount; @@ -599,7 +601,7 @@ void drawPlaneText(PlaneObj *p) { p->h = currentLine * appData.mapFontHeight; } -void drawSelectedPlaneText(PlaneObj *p) { +void View::drawSelectedAircraftText(Aircraft *p) { if(p == NULL) { return; } @@ -653,12 +655,12 @@ void drawSelectedPlaneText(PlaneObj *p) { } } -void resolveLabelConflicts() { - PlaneObj *p = appData.planes; +void View::resolveLabelConflicts() { + Aircraft *p = aircraftData->aircraftList.head; while(p) { - PlaneObj *check_p = appData.planes; + Aircraft *check_p = aircraftData->aircraftList.head; int p_left = p->x - 10 * appData.screen_uiscale; int p_right = p->x + p->w + 10 * appData.screen_uiscale; @@ -747,7 +749,7 @@ void resolveLabelConflicts() { check_p = check_p -> next; } - check_p = appData.planes; + check_p = aircraftData->aircraftList.head; //check against plane icons (include self) @@ -803,7 +805,7 @@ void resolveLabelConflicts() { //update - p = appData.planes; + p = aircraftData->aircraftList.head; while(p) { //incorporate acceleration from label conflict resolution @@ -843,8 +845,8 @@ void resolveLabelConflicts() { } -void drawPlanes() { - PlaneObj *p = appData.planes; +void View::drawPlanes() { + Aircraft *p = aircraftData->aircraftList.head; time_t now = time(NULL); SDL_Color planeColor; @@ -858,12 +860,12 @@ void drawPlanes() { p = p->next; } - if(appData.selectedPlane) { - appData.mapTargetLon = appData.selectedPlane->lon; - appData.mapTargetLat = appData.selectedPlane->lat; + if(aircraftData->selectedAircraft) { + appData.mapTargetLon = aircraftData->selectedAircraft->lon; + appData.mapTargetLat = aircraftData->selectedAircraft->lat; } - p = appData.planes; + p = aircraftData->aircraftList.head; while(p) { if ((now - p->seen) < modes.interactive_display_ttl) { @@ -900,7 +902,7 @@ void drawPlanes() { usey = y + (mstime() - p->msSeenLatLon) * vely; } - if(p == appData.selectedPlane) { + if(p == aircraftData->selectedAircraft) { // this logic should be in input, register a callback for click? float elapsed = mstime() - appData.touchDownTime; @@ -935,7 +937,7 @@ void drawPlanes() { p->cy = usey; } - if(p != appData.selectedPlane) { + if(p != aircraftData->selectedAircraft) { drawPlaneText(p); } @@ -946,7 +948,7 @@ void drawPlanes() { p = p->next; } - drawSelectedPlaneText(appData.selectedPlane); + drawSelectedAircraftText(aircraftData->selectedAircraft); if(appData.touchx && appData.touchy) { @@ -963,7 +965,7 @@ void drawPlanes() { } } -void animateCenterAbsolute(float x, float y) { +void View::animateCenterAbsolute(float x, float y) { float scale_factor = (appData.screen_width > appData.screen_height) ? appData.screen_width : appData.screen_height; float dx = -1.0 * (0.75*(double)appData.screen_width / (double)appData.screen_height) * (x - appData.screen_width/2) * appData.maxDist / (0.95 * scale_factor * 0.5); @@ -985,7 +987,7 @@ void animateCenterAbsolute(float x, float y) { } -void moveCenterAbsolute(float x, float y) { +void View::moveCenterAbsolute(float x, float y) { float scale_factor = (appData.screen_width > appData.screen_height) ? appData.screen_width : appData.screen_height; float dx = -1.0 * (0.75*(double)appData.screen_width / (double)appData.screen_height) * (x - appData.screen_width/2) * appData.maxDist / (0.95 * scale_factor * 0.5); @@ -1007,7 +1009,7 @@ void moveCenterAbsolute(float x, float y) { appData.mapMoved = 1; } -void moveCenterRelative(float dx, float dy) { +void View::moveCenterRelative(float dx, float dy) { // // need to make lonlat to screen conversion class - this is just the inverse of the stuff in draw.c, without offsets // @@ -1033,7 +1035,7 @@ void moveCenterRelative(float dx, float dy) { appData.mapMoved = 1; } -void zoomMapToTarget() { +void View::zoomMapToTarget() { if(appData.mapTargetMaxDist) { if(fabs(appData.mapTargetMaxDist - appData.maxDist) > 0.0001) { appData.maxDist += 0.1 * (appData.mapTargetMaxDist - appData.maxDist); @@ -1043,7 +1045,7 @@ void zoomMapToTarget() { } } -void moveMapToTarget() { +void View::moveMapToTarget() { if(appData.mapTargetLon && appData.mapTargetLat) { if(fabs(appData.mapTargetLon - appData.centerLon) > 0.0001 || fabs(appData.mapTargetLat - appData.centerLat) > 0.0001) { appData.centerLon += 0.1 * (appData.mapTargetLon- appData.centerLon); @@ -1057,7 +1059,7 @@ void moveMapToTarget() { } } -void drawMouse() { +void View::drawMouse() { if(appData.mouseMovedTime == 0 || (mstime() - appData.mouseMovedTime) > 1000) { appData.mouseMovedTime = 0; return; @@ -1069,10 +1071,10 @@ void drawMouse() { lineRGBA(appData.renderer, appData.mousex, appData.mousey - 10 * appData.screen_uiscale, appData.mousex, appData.mousey + 10 * appData.screen_uiscale, white.r, white.g, white.b, alpha); } -void registerClick() { +void View::registerClick() { if(appData.tapCount == 1 && appData.isDragging == 0) { - PlaneObj *p = appData.planes; - PlaneObj *selection = NULL; + Aircraft *p = aircraftData->aircraftList.head; + Aircraft *selection = NULL; while(p) { if(appData.touchx && appData.touchy) { @@ -1091,8 +1093,8 @@ void registerClick() { p = p->next; } - //if(appData.selectedPlane == NULL) { - appData.selectedPlane = selection; + //if(aircraftData->selectedAircraft == NULL) { + aircraftData->selectedAircraft = selection; //} } else if(appData.tapCount == 2) { appData.mapTargetMaxDist = 0.25 * appData.maxDist; @@ -1104,13 +1106,13 @@ void registerClick() { // // -void draw() { +void View::draw() { uint64_t drawStartTime = mstime(); moveMapToTarget(); zoomMapToTarget(); - updatePlanes(); + //updatePlanes(); updateStatus(); @@ -1156,4 +1158,8 @@ void draw() { if ((mstime() - drawStartTime) < FRAMETIME) { usleep(1000 * (FRAMETIME - (mstime() - drawStartTime))); } +} + +View::View(AircraftData *aircraftData){ + this->aircraftData = aircraftData; } \ No newline at end of file diff --git a/View.h b/View.h new file mode 100644 index 0000000..d924020 --- /dev/null +++ b/View.h @@ -0,0 +1,39 @@ +#ifndef VIEW_H +#define VIEW_H + +#include "AircraftData.h" + +class View { + +AircraftData *aircraftData; + +public: + int screenDist(float d); + void pxFromLonLat(float *dx, float *dy, float lon, float lat); + void latLonFromScreenCoords(float *lat, float *lon, int x, int y); + void screenCoords(int *outX, int *outY, float dx, float dy); + int outOfBounds(int x, int y); + void drawPlaneOffMap(int x, int y, int *returnx, int *returny, SDL_Color planeColor); + void drawPlaneIcon(int x, int y, float heading, SDL_Color planeColor); + void drawTrail(float *oldDx, float *oldDy, float *oldHeading, time_t * oldSeen, int idx); + void drawScaleBars(); + void drawPolys(QuadTree *tree, float screen_lat_min, float screen_lat_max, float screen_lon_min, float screen_lon_max); + void drawGeography(); + void drawSignalMarks(Aircraft *p, int x, int y); + void drawPlaneText(Aircraft *p); + void drawSelectedAircraftText(Aircraft *p); + void resolveLabelConflicts(); + void drawPlanes(); + void animateCenterAbsolute(float x, float y); + void moveCenterAbsolute(float x, float y); + void moveCenterRelative(float dx, float dy); + void zoomMapToTarget(); + void moveMapToTarget(); + void drawMouse(); + void registerClick(); + void draw(); + + View(AircraftData *aircraftData); +}; + +#endif \ No newline at end of file diff --git a/dump1090.h b/dump1090.h index 2de8b0d..5e5c21d 100644 --- a/dump1090.h +++ b/dump1090.h @@ -428,27 +428,27 @@ extern "C" { // Functions exported from mode_ac.c // int detectModeA (uint16_t *m, struct modesMessage *mm); -void decodeModeAMessage(struct modesMessage *mm, int ModeA); +void decodeModeAMessage(Modes *modes, struct modesMessage *mm, int ModeA); int ModeAToModeC (unsigned int ModeA); // // Functions exported from mode_s.c // void detectModeS (uint16_t *m, uint32_t mlen); -void decodeModesMessage (struct modesMessage *mm, unsigned char *msg); +void decodeModesMessage (Modes *modes, struct modesMessage *mm, unsigned char *msg); void displayModesMessage(struct modesMessage *mm); -void useModesMessage (struct modesMessage *mm); +void useModesMessage (Modes* modes, struct modesMessage *mm); void computeMagnitudeVector(uint16_t *pData); -int decodeCPR (struct aircraft *a, int fflag, int surface); -int decodeCPRrelative (struct aircraft *a, int fflag, int surface); -void modesInitErrorInfo (); +int decodeCPR (Modes *modes, struct aircraft *a, int fflag, int surface); +int decodeCPRrelative (Modes *modes, struct aircraft *a, int fflag, int surface); +void modesInitErrorInfo (Modes *modes); // // Functions exported from interactive.c // -struct aircraft* interactiveReceiveData(struct modesMessage *mm); +struct aircraft* interactiveReceiveData(Modes *modes, struct modesMessage *mm); void interactiveShowData(void); -void interactiveRemoveStaleAircrafts(void); -int decodeBinMessage (struct client *c, char *p); +void interactiveRemoveStaleAircrafts(Modes *modes); +int decodeBinMessage (Modes *modes, struct client *c, char *p); struct aircraft *interactiveFindAircraft(uint32_t addr); struct stDF *interactiveFindDF (uint32_t addr); @@ -456,10 +456,10 @@ struct stDF *interactiveFindDF (uint32_t addr); // Functions exported from net_io.c // void modesInitNet (void); -void modesReadFromClients (void); -void modesSendAllClients (int service, void *msg, int len); -void modesQueueOutput (struct modesMessage *mm); -void modesReadFromClient(struct client *c, char *sep, int(*handler)(struct client *, char *)); +//void modesReadFromClients (void); +//void modesSendAllClients (int service, void *msg, int len); +//void modesQueueOutput (struct modesMessage *mm); +void modesReadFromClient (Modes *modes, struct client *c, char *sep, int(*handler)(Modes *modes, struct client *, char *)); #ifdef __cplusplus } diff --git a/init.c b/init.c index 103ad61..1eccded 100644 --- a/init.c +++ b/init.c @@ -48,7 +48,6 @@ void init(char *title) { appData.mapTargetLat = 0; appData.mapTargetMaxDist = 0; appData.isDragging = 0; - appData.selectedPlane = NULL; if(appData.fullscreen) { //SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // make the scaled rendering look smoother. diff --git a/interactive.c b/interactive.c index 5884a33..1ce879b 100644 --- a/interactive.c +++ b/interactive.c @@ -46,7 +46,7 @@ static uint64_t mstime(void) { // // Add a new DF structure to the interactive mode linked list // -void interactiveCreateDF(struct aircraft *a, struct modesMessage *mm) { +void interactiveCreateDF(Modes *modes, struct aircraft *a, struct modesMessage *mm) { struct stDF *pDF = (struct stDF *) malloc(sizeof(*pDF)); if (pDF) { @@ -60,60 +60,34 @@ void interactiveCreateDF(struct aircraft *a, struct modesMessage *mm) { pDF->pAircraft = a; memcpy(pDF->msg, mm->msg, MODES_LONG_MSG_BYTES); - // if (!pthread_mutex_lock(&modes.pDF_mutex)) { - // if ((pDF->pNext = modes.pDF)) { - // modes.pDF->pPrev = pDF; + // if (!pthread_mutex_lock(&modes->pDF_mutex)) { + // if ((pDF->pNext = modes->pDF)) { + // modes->pDF->pPrev = pDF; // } - // modes.pDF = pDF; - // pthread_mutex_unlock(&modes.pDF_mutex); + // modes->pDF = pDF; + // pthread_mutex_unlock(&modes->pDF_mutex); // } else { // free(pDF); // } - if ((pDF->pNext = modes.pDF)) { - modes.pDF->pPrev = pDF; + if ((pDF->pNext = modes->pDF)) { + modes->pDF->pPrev = pDF; } - modes.pDF = pDF; + modes->pDF = pDF; } } // // Remove stale DF's from the interactive mode linked list // -void interactiveRemoveStaleDF(time_t now) { +void interactiveRemoveStaleDF( Modes *modes, time_t now) { struct stDF *pDF = NULL; struct stDF *prev = NULL; - // Only fiddle with the DF list if we gain possession of the mutex - // If we fail to get the mutex we'll get another chance to tidy the - // DF list in a second or so. - // if (!pthread_mutex_trylock(&modes.pDF_mutex)) { - // pDF = modes.pDF; - // while(pDF) { - // if ((now - pDF->seen) > modes.interactive_delete_ttl) { - // if (modes.pDF == pDF) { - // modes.pDF = NULL; - // } else { - // prev->pNext = NULL; - // } - - // // All DF's in the list from here onwards will be time - // // expired, so delete them all - // while (pDF) { - // prev = pDF; pDF = pDF->pNext; - // free(prev); - // } - - // } else { - // prev = pDF; pDF = pDF->pNext; - // } - // } - // pthread_mutex_unlock (&modes.pDF_mutex); - // } - pDF = modes.pDF; + pDF = modes->pDF; while(pDF) { - if ((now - pDF->seen) > modes.interactive_delete_ttl) { - if (modes.pDF == pDF) { - modes.pDF = NULL; + if ((now - pDF->seen) > modes->interactive_delete_ttl) { + if (modes->pDF == pDF) { + modes->pDF = NULL; } else { prev->pNext = NULL; } @@ -131,32 +105,20 @@ void interactiveRemoveStaleDF(time_t now) { } } -struct stDF *interactiveFindDF(uint32_t addr) { - struct stDF *pDF = NULL; +// struct stDF *interactiveFindDF(Modes *modes, uint32_t addr) { +// struct stDF *pDF = NULL; - // if (!pthread_mutex_lock(&modes.pDF_mutex)) { - // pDF = modes.pDF; - // while(pDF) { - // if (pDF->addr == addr) { - // pthread_mutex_unlock (&modes.pDF_mutex); - // return (pDF); - // } - // pDF = pDF->pNext; - // } - // pthread_mutex_unlock (&modes.pDF_mutex); - // } - - pDF = modes.pDF; - while(pDF) { - if (pDF->addr == addr) { - return (pDF); - } - pDF = pDF->pNext; - } +// pDF = modes->pDF; +// while(pDF) { +// if (pDF->addr == addr) { +// return (pDF); +// } +// pDF = pDF->pNext; +// } - return (NULL); -} +// return (NULL); +// } // //========================= Interactive mode =============================== // @@ -196,8 +158,8 @@ struct aircraft *interactiveCreateAircraft(struct modesMessage *mm) { // Return the aircraft with the specified address, or NULL if no aircraft // exists with this address. // -struct aircraft *interactiveFindAircraft(uint32_t addr) { - struct aircraft *a = modes.aircrafts; +struct aircraft *interactiveFindAircraft(Modes *modes, uint32_t addr) { + struct aircraft *a = modes->aircrafts; while(a) { if (a->addr == addr) return (a); @@ -205,105 +167,25 @@ struct aircraft *interactiveFindAircraft(uint32_t addr) { } return (NULL); } -// -//========================================================================= -// -// We have received a Mode A or C response. -// -// Search through the list of known Mode-S aircraft and tag them if this Mode A/C -// matches their known Mode S Squawks or Altitudes(+/- 50feet). -// -// A Mode S equipped aircraft may also respond to Mode A and Mode C SSR interrogations. -// We can't tell if this is a Mode A or C, so scan through the entire aircraft list -// looking for matches on Mode A (squawk) and Mode C (altitude). Flag in the Mode S -// records that we have had a potential Mode A or Mode C response from this aircraft. -// -// If an aircraft responds to Mode A then it's highly likely to be responding to mode C -// too, and vice verca. Therefore, once the mode S record is tagged with both a Mode A -// and a Mode C flag, we can be fairly confident that this Mode A/C frame relates to that -// Mode S aircraft. -// -// Mode C's are more likely to clash than Mode A's; There could be several aircraft -// cruising at FL370, but it's less likely (though not impossible) that there are two -// aircraft on the same squawk. Therefore, give precidence to Mode A record matches -// -// Note : It's theoretically possible for an aircraft to have the same value for Mode A -// and Mode C. Therefore we have to check BOTH A AND C for EVERY S. -// -void interactiveUpdateAircraftModeA(struct aircraft *a) { - struct aircraft *b = modes.aircrafts; - while(b) { - if ((b->modeACflags & MODEAC_MSG_FLAG) == 0) {// skip any fudged ICAO records - - // If both (a) and (b) have valid squawks... - if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_SQUAWK_VALID) { - // ...check for Mode-A == Mode-S Squawk matches - if (a->modeA == b->modeA) { // If a 'real' Mode-S ICAO exists using this Mode-A Squawk - b->modeAcount = a->messages; - b->modeACflags |= MODEAC_MSG_MODEA_HIT; - a->modeACflags |= MODEAC_MSG_MODEA_HIT; - if ( (b->modeAcount > 0) && - ( (b->modeCcount > 1) - || (a->modeACflags & MODEAC_MSG_MODEA_ONLY)) ) // Allow Mode-A only matches if this Mode-A is invalid Mode-C - {a->modeACflags |= MODEAC_MSG_MODES_HIT;} // flag this ModeA/C probably belongs to a known Mode S - } - } - - // If both (a) and (b) have valid altitudes... - if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_ALTITUDE_VALID) { - // ... check for Mode-C == Mode-S Altitude matches - if ( (a->modeC == b->modeC ) // If a 'real' Mode-S ICAO exists at this Mode-C Altitude - || (a->modeC == b->modeC + 1) // or this Mode-C - 100 ft - || (a->modeC + 1 == b->modeC ) ) { // or this Mode-C + 100 ft - b->modeCcount = a->messages; - b->modeACflags |= MODEAC_MSG_MODEC_HIT; - a->modeACflags |= MODEAC_MSG_MODEC_HIT; - if ( (b->modeAcount > 0) && - (b->modeCcount > 1) ) - {a->modeACflags |= (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEC_OLD);} // flag this ModeA/C probably belongs to a known Mode S - } - } - } - b = b->next; - } -} -// -//========================================================================= -// -void interactiveUpdateAircraftModeS() { - struct aircraft *a = modes.aircrafts; - - while(a) { - int flags = a->modeACflags; - if (flags & MODEAC_MSG_FLAG) { // find any fudged ICAO records - - // clear the current A,C and S hit bits ready for this attempt - a->modeACflags = flags & ~(MODEAC_MSG_MODEA_HIT | MODEAC_MSG_MODEC_HIT | MODEAC_MSG_MODES_HIT); - - interactiveUpdateAircraftModeA(a); // and attempt to match them with Mode-S - } - a = a->next; - } -} // //========================================================================= // // Receive new messages and populate the interactive mode with more info // -struct aircraft *interactiveReceiveData(struct modesMessage *mm) { +struct aircraft *interactiveReceiveData(Modes *modes, struct modesMessage *mm) { struct aircraft *a, *aux; // Return if (checking crc) AND (not crcok) AND (not fixed) - if (modes.check_crc && (mm->crcok == 0) && (mm->correctedbits == 0)) + if (modes->check_crc && (mm->crcok == 0) && (mm->correctedbits == 0)) return NULL; // Lookup our aircraft or create a new one - a = interactiveFindAircraft(mm->addr); + a = interactiveFindAircraft(modes, mm->addr); if (!a) { // If it's a currently unknown aircraft.... a = interactiveCreateAircraft(mm); // ., create a new record for it, - a->next = modes.aircrafts; // .. and put it at the head of the list - modes.aircrafts = a; + a->next = modes->aircrafts; // .. and put it at the head of the list + modes->aircrafts = a; } else { /* If it is an already known aircraft, move it on head * so we keep aircrafts ordered by received message time. @@ -312,14 +194,14 @@ struct aircraft *interactiveReceiveData(struct modesMessage *mm) { * since the aircraft that is currently on head sent a message, * othewise with multiple aircrafts at the same time we have an * useless shuffle of positions on the screen. */ - if (0 && modes.aircrafts != a && (time(NULL) - a->seen) >= 1) { - aux = modes.aircrafts; + if (0 && modes->aircrafts != a && (time(NULL) - a->seen) >= 1) { + aux = modes->aircrafts; while(aux->next != a) aux = aux->next; /* Now we are a node before the aircraft to remove. */ aux->next = aux->next->next; /* removed. */ /* Add on head */ - a->next = modes.aircrafts; - modes.aircrafts = a; + a->next = modes->aircrafts; + modes->aircrafts = a; } } @@ -392,13 +274,13 @@ struct aircraft *interactiveReceiveData(struct modesMessage *mm) { // If we have enough recent data, try global CPR if (((mm->bFlags | a->bFlags) & MODES_ACFLAGS_LLEITHER_VALID) == MODES_ACFLAGS_LLBOTH_VALID && abs((int)(a->even_cprtime - a->odd_cprtime)) <= 10000) { - if (decodeCPR(a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG)) == 0) { + if (decodeCPR(modes, a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG)) == 0) { location_ok = 1; } } // Otherwise try relative CPR. - if (!location_ok && decodeCPRrelative(a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG)) == 0) { + if (!location_ok && decodeCPRrelative(modes, a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG)) == 0) { location_ok = 1; } @@ -432,8 +314,8 @@ struct aircraft *interactiveReceiveData(struct modesMessage *mm) { } // If we are Logging DF's, and it's not a Mode A/C - if ((modes.bEnableDFLogging) && (mm->msgtype < 32)) { - interactiveCreateDF(a,mm); + if ((modes->bEnableDFLogging) && (mm->msgtype < 32)) { + interactiveCreateDF(modes,a,mm); } return (a); @@ -445,23 +327,23 @@ struct aircraft *interactiveReceiveData(struct modesMessage *mm) { // When in interactive mode If we don't receive new nessages within // MODES_INTERACTIVE_DELETE_TTL seconds we remove the aircraft from the list. // -void interactiveRemoveStaleAircrafts(void) { - struct aircraft *a = modes.aircrafts; +void interactiveRemoveStaleAircrafts(Modes *modes) { + struct aircraft *a = modes->aircrafts; struct aircraft *prev = NULL; time_t now = time(NULL); // Only do cleanup once per second - if (modes.last_cleanup_time != now) { - modes.last_cleanup_time = now; + if (modes->last_cleanup_time != now) { + modes->last_cleanup_time = now; - interactiveRemoveStaleDF(now); + interactiveRemoveStaleDF(modes,now); while(a) { - if ((now - a->seen) > modes.interactive_delete_ttl) { + if ((now - a->seen) > modes->interactive_delete_ttl) { // Remove the element from the linked list, with care // if we are removing the first element if (!prev) { - modes.aircrafts = a->next; free(a); a = modes.aircrafts; + modes->aircrafts = a->next; free(a); a = modes->aircrafts; } else { prev->next = a->next; free(a); a = prev->next; } @@ -473,4 +355,4 @@ void interactiveRemoveStaleAircrafts(void) { } // //========================================================================= -// +// \ No newline at end of file diff --git a/map1090.cpp b/map1090.cpp new file mode 100644 index 0000000..e37c383 --- /dev/null +++ b/map1090.cpp @@ -0,0 +1,152 @@ +// view1090, a Mode S messages viewer for dump1090 devices. +// +// Copyright (C) 2014 by Malcolm Robb +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "view1090.h" +#include "structs.h" +#include "AircraftData.h" +#include "View.h" +#include "Input.h" + +//time utility, might change to std::chrono +uint64_t mstime(void) { + struct timeval tv; + uint64_t mst; + + gettimeofday(&tv, NULL); + mst = ((uint64_t)tv.tv_sec)*1000; + mst += tv.tv_usec/1000; + return mst; +} + + +int go = 1; + + +AppData appData; +Style style; + +// +// ================================ Main ==================================== +// +void showHelp(void) { + printf( +"-----------------------------------------------------------------------------\n" +"| view1090 dump1090 Viewer Ver : " MODES_DUMP1090_VERSION " |\n" +"-----------------------------------------------------------------------------\n" + "--server TCP Beast output listen IPv4 (default: 127.0.0.1)\n" + "--port TCP Beast output listen port (default: 30005)\n" + "--lat Reference/receiver latitide for surface posn (opt)\n" + "--lon Reference/receiver longitude for surface posn (opt)\n" + "--metric Use metric units (meters, km/h, ...)\n" + "--help Show this help\n" + "--uiscale UI global scaling (default: 1)\n" + "--screensize Set frame buffer resolution (default: screen resolution)\n" + "--fullscreen Start fullscreen\n" + ); +} + + +// +//========================================================================= +// + +int main(int argc, char **argv) { + int j; + + AircraftData aircraftData; + View view(&aircraftData); + Input input(&view); + + signal(SIGINT, SIG_DFL); // reset signal handler - bit extra safety + + aircraftData.initialize(); + + // Parse the command line options + for (j = 1; j < argc; j++) { + int more = ((j + 1) < argc); // There are more arguments + + if (!strcmp(argv[j],"--net-bo-port") && more) { + aircraftData.modes.net_input_beast_port = atoi(argv[++j]); + } else if (!strcmp(argv[j],"--port") && more) { + aircraftData.modes.net_input_beast_port = atoi(argv[++j]); + } else if (!strcmp(argv[j],"--net-bo-ipaddr") && more) { + strcpy(View1090.net_input_beast_ipaddr, argv[++j]); + } else if (!strcmp(argv[j],"--server") && more) { + strcpy(View1090.net_input_beast_ipaddr, argv[++j]); + } else if (!strcmp(argv[j],"--lat") && more) { + aircraftData.modes.fUserLat = atof(argv[++j]); + appData.centerLat = aircraftData.modes.fUserLat; + } else if (!strcmp(argv[j],"--lon") && more) { + aircraftData.modes.fUserLon = atof(argv[++j]); + appData.centerLon = aircraftData.modes.fUserLon; + } else if (!strcmp(argv[j],"--metric")) { + aircraftData.modes.metric = 1; + } else if (!strcmp(argv[j],"--fullscreen")) { + appData.fullscreen = 1; + } else if (!strcmp(argv[j],"--uiscale") && more) { + appData.screen_uiscale = atoi(argv[++j]); + } else if (!strcmp(argv[j],"--screensize") && more) { + appData.screen_width = atoi(argv[++j]); + appData.screen_height = atoi(argv[++j]); + } else if (!strcmp(argv[j],"--help")) { + showHelp(); + exit(0); + } else { + fprintf(stderr, "Unknown or not enough arguments for option '%s'.\n\n", argv[j]); + showHelp(); + exit(1); + } + } + + + int go; + + aircraftData.connect(); + + init("sdl1090"); + + atexit(cleanup); + + go = 1; + + while (go == 1) + { + input.getInput(); + aircraftData.update(); + view.draw(); + } + + aircraftData.disconnect(); + + return (0); +} +// +//========================================================================= +// diff --git a/mode_s.c b/mode_s.c new file mode 100644 index 0000000..16fc27b --- /dev/null +++ b/mode_s.c @@ -0,0 +1,1162 @@ +// dump1090, a Mode S messages decoder for RTLSDR devices. +// +// Copyright (C) 2012 by Salvatore Sanfilippo +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "dump1090.h" +// +// ===================== Mode S detection and decoding =================== +// +// Parity table for MODE S Messages. +// The table contains 112 elements, every element corresponds to a bit set +// in the message, starting from the first bit of actual data after the +// preamble. +// +// For messages of 112 bit, the whole table is used. +// For messages of 56 bits only the last 56 elements are used. +// +// The algorithm is as simple as xoring all the elements in this table +// for which the corresponding bit on the message is set to 1. +// +// The latest 24 elements in this table are set to 0 as the checksum at the +// end of the message should not affect the computation. +// +// Note: this function can be used with DF11 and DF17, other modes have +// the CRC xored with the sender address as they are reply to interrogations, +// but a casual listener can't split the address from the checksum. +// +uint32_t modes_checksum_table[112] = { +0x3935ea, 0x1c9af5, 0xf1b77e, 0x78dbbf, 0xc397db, 0x9e31e9, 0xb0e2f0, 0x587178, +0x2c38bc, 0x161c5e, 0x0b0e2f, 0xfa7d13, 0x82c48d, 0xbe9842, 0x5f4c21, 0xd05c14, +0x682e0a, 0x341705, 0xe5f186, 0x72f8c3, 0xc68665, 0x9cb936, 0x4e5c9b, 0xd8d449, +0x939020, 0x49c810, 0x24e408, 0x127204, 0x093902, 0x049c81, 0xfdb444, 0x7eda22, +0x3f6d11, 0xe04c8c, 0x702646, 0x381323, 0xe3f395, 0x8e03ce, 0x4701e7, 0xdc7af7, +0x91c77f, 0xb719bb, 0xa476d9, 0xadc168, 0x56e0b4, 0x2b705a, 0x15b82d, 0xf52612, +0x7a9309, 0xc2b380, 0x6159c0, 0x30ace0, 0x185670, 0x0c2b38, 0x06159c, 0x030ace, +0x018567, 0xff38b7, 0x80665f, 0xbfc92b, 0xa01e91, 0xaff54c, 0x57faa6, 0x2bfd53, +0xea04ad, 0x8af852, 0x457c29, 0xdd4410, 0x6ea208, 0x375104, 0x1ba882, 0x0dd441, +0xf91024, 0x7c8812, 0x3e4409, 0xe0d800, 0x706c00, 0x383600, 0x1c1b00, 0x0e0d80, +0x0706c0, 0x038360, 0x01c1b0, 0x00e0d8, 0x00706c, 0x003836, 0x001c1b, 0xfff409, +0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, +0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, +0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000 +}; + +uint32_t modesChecksum(unsigned char *msg, int bits) { + uint32_t crc = 0; + uint32_t rem = 0; + int offset = (bits == 112) ? 0 : (112-56); + uint8_t theByte = *msg; + uint32_t * pCRCTable = &modes_checksum_table[offset]; + int j; + + // We don't really need to include the checksum itself + bits -= 24; + for(j = 0; j < bits; j++) { + if ((j & 7) == 0) + theByte = *msg++; + + // If bit is set, xor with corresponding table entry. + if (theByte & 0x80) {crc ^= *pCRCTable;} + pCRCTable++; + theByte = theByte << 1; + } + + rem = (msg[0] << 16) | (msg[1] << 8) | msg[2]; // message checksum + return ((crc ^ rem) & 0x00FFFFFF); // 24 bit checksum syndrome. +} +// +//========================================================================= +// +// Given the Downlink Format (DF) of the message, return the message length in bits. +// +// All known DF's 16 or greater are long. All known DF's 15 or less are short. +// There are lots of unused codes in both category, so we can assume ICAO will stick to +// these rules, meaning that the most significant bit of the DF indicates the length. +// +int modesMessageLenByType(int type) { + return (type & 0x10) ? MODES_LONG_MSG_BITS : MODES_SHORT_MSG_BITS ; +} +// +//========================================================================= +// +// Code for introducing a less CPU-intensive method of correcting +// single bit errors. +// +// Makes use of the fact that the crc checksum is linear with respect to +// the bitwise xor operation, i.e. +// crc(m^e) = (crc(m)^crc(e) +// where m and e are the message resp. error bit vectors. +// +// Call crc(e) the syndrome. +// +// The code below works by precomputing a table of (crc(e), e) for all +// possible error vectors e (here only single bit and double bit errors), +// search for the syndrome in the table, and correct the then known error. +// The error vector e is represented by one or two bit positions that are +// changed. If a second bit position is not used, it is -1. +// +// Run-time is binary search in a sorted table, plus some constant overhead, +// instead of running through all possible bit positions (resp. pairs of +// bit positions). +// +struct errorinfo { + uint32_t syndrome; // CRC syndrome + int bits; // Number of bit positions to fix + int pos[MODES_MAX_BITERRORS]; // Bit positions corrected by this syndrome +}; + +#define NERRORINFO \ + (MODES_LONG_MSG_BITS+MODES_LONG_MSG_BITS*(MODES_LONG_MSG_BITS-1)/2) +struct errorinfo bitErrorTable[NERRORINFO]; + +// Compare function as needed for stdlib's qsort and bsearch functions +int cmpErrorInfo(const void *p0, const void *p1) { + struct errorinfo *e0 = (struct errorinfo*)p0; + struct errorinfo *e1 = (struct errorinfo*)p1; + if (e0->syndrome == e1->syndrome) { + return 0; + } else if (e0->syndrome < e1->syndrome) { + return -1; + } else { + return 1; + } +} +// +//========================================================================= +// +// Compute the table of all syndromes for 1-bit and 2-bit error vectors +void modesInitErrorInfo(Modes *modes) { + unsigned char msg[MODES_LONG_MSG_BYTES]; + int i, j, n; + uint32_t crc; + n = 0; + memset(bitErrorTable, 0, sizeof(bitErrorTable)); + memset(msg, 0, MODES_LONG_MSG_BYTES); + // Add all possible single and double bit errors + // don't include errors in first 5 bits (DF type) + for (i = 5; i < MODES_LONG_MSG_BITS; i++) { + int bytepos0 = (i >> 3); + int mask0 = 1 << (7 - (i & 7)); + msg[bytepos0] ^= mask0; // create error0 + crc = modesChecksum(msg, MODES_LONG_MSG_BITS); + bitErrorTable[n].syndrome = crc; // single bit error case + bitErrorTable[n].bits = 1; + bitErrorTable[n].pos[0] = i; + bitErrorTable[n].pos[1] = -1; + n += 1; + + if (modes->nfix_crc > 1) { + for (j = i+1; j < MODES_LONG_MSG_BITS; j++) { + int bytepos1 = (j >> 3); + int mask1 = 1 << (7 - (j & 7)); + msg[bytepos1] ^= mask1; // create error1 + crc = modesChecksum(msg, MODES_LONG_MSG_BITS); + if (n >= NERRORINFO) { + //fprintf(stderr, "Internal error, too many entries, fix NERRORINFO\n"); + break; + } + bitErrorTable[n].syndrome = crc; // two bit error case + bitErrorTable[n].bits = 2; + bitErrorTable[n].pos[0] = i; + bitErrorTable[n].pos[1] = j; + n += 1; + msg[bytepos1] ^= mask1; // revert error1 + } + } + msg[bytepos0] ^= mask0; // revert error0 + } + qsort(bitErrorTable, NERRORINFO, sizeof(struct errorinfo), cmpErrorInfo); + +} +// +//========================================================================= +// +// Search for syndrome in table and if an entry is found, flip the necessary +// bits. Make sure the indices fit into the array +// Additional parameter: fix only less than maxcorrected bits, and record +// fixed bit positions in corrected[]. This array can be NULL, otherwise +// must be of length at least maxcorrected. +// Return number of fixed bits. +// +int fixBitErrors(unsigned char *msg, int bits, int maxfix, char *fixedbits) { + struct errorinfo *pei; + struct errorinfo ei; + int bitpos, offset, res, i; + memset(&ei, 0, sizeof(struct errorinfo)); + ei.syndrome = modesChecksum(msg, bits); + pei = (errorinfo*)bsearch(&ei, bitErrorTable, NERRORINFO, + sizeof(struct errorinfo), cmpErrorInfo); + if (pei == NULL) { + return 0; // No syndrome found + } + + // Check if the syndrome fixes more bits than we allow + if (maxfix < pei->bits) { + return 0; + } + + // Check that all bit positions lie inside the message length + offset = MODES_LONG_MSG_BITS-bits; + for (i = 0; i < pei->bits; i++) { + bitpos = pei->pos[i] - offset; + if ((bitpos < 0) || (bitpos >= bits)) { + return 0; + } + } + + // Fix the bits + for (i = res = 0; i < pei->bits; i++) { + bitpos = pei->pos[i] - offset; + msg[bitpos >> 3] ^= (1 << (7 - (bitpos & 7))); + if (fixedbits) { + fixedbits[res++] = bitpos; + } + } + return res; +} + +//========================================================================= +// +// Hash the ICAO address to index our cache of MODES_ICAO_CACHE_LEN +// elements, that is assumed to be a power of two +// +uint32_t ICAOCacheHashAddress(uint32_t a) { + // The following three rounds wil make sure that every bit affects + // every output bit with ~ 50% of probability. + a = ((a >> 16) ^ a) * 0x45d9f3b; + a = ((a >> 16) ^ a) * 0x45d9f3b; + a = ((a >> 16) ^ a); + return a & (MODES_ICAO_CACHE_LEN-1); +} +// +//========================================================================= +// +// Add the specified entry to the cache of recently seen ICAO addresses. +// Note that we also add a timestamp so that we can make sure that the +// entry is only valid for MODES_ICAO_CACHE_TTL seconds. +// +void addRecentlySeenICAOAddr(Modes *modes, uint32_t addr) { + uint32_t h = ICAOCacheHashAddress(addr); + modes->icao_cache[h*2] = addr; + modes->icao_cache[h*2+1] = (uint32_t) time(NULL); +} +// +//========================================================================= +// +// Returns 1 if the specified ICAO address was seen in a DF format with +// proper checksum (not xored with address) no more than * MODES_ICAO_CACHE_TTL +// seconds ago. Otherwise returns 0. +// +int ICAOAddressWasRecentlySeen(Modes *modes, uint32_t addr) { + uint32_t h = ICAOCacheHashAddress(addr); + uint32_t a = modes->icao_cache[h*2]; + uint32_t t = modes->icao_cache[h*2+1]; + uint64_t tn = time(NULL); + + return ( (a) && (a == addr) && ( (tn - t) <= MODES_ICAO_CACHE_TTL) ); +} +// +//========================================================================= +// +// In the squawk (identity) field bits are interleaved as follows in +// (message bit 20 to bit 32): +// +// C1-A1-C2-A2-C4-A4-ZERO-B1-D1-B2-D2-B4-D4 +// +// So every group of three bits A, B, C, D represent an integer from 0 to 7. +// +// The actual meaning is just 4 octal numbers, but we convert it into a hex +// number tha happens to represent the four octal numbers. +// +// For more info: http://en.wikipedia.org/wiki/Gillham_code +// +int decodeID13Field(int ID13Field) { + int hexGillham = 0; + + if (ID13Field & 0x1000) {hexGillham |= 0x0010;} // Bit 12 = C1 + if (ID13Field & 0x0800) {hexGillham |= 0x1000;} // Bit 11 = A1 + if (ID13Field & 0x0400) {hexGillham |= 0x0020;} // Bit 10 = C2 + if (ID13Field & 0x0200) {hexGillham |= 0x2000;} // Bit 9 = A2 + if (ID13Field & 0x0100) {hexGillham |= 0x0040;} // Bit 8 = C4 + if (ID13Field & 0x0080) {hexGillham |= 0x4000;} // Bit 7 = A4 + //if (ID13Field & 0x0040) {hexGillham |= 0x0800;} // Bit 6 = X or M + if (ID13Field & 0x0020) {hexGillham |= 0x0100;} // Bit 5 = B1 + if (ID13Field & 0x0010) {hexGillham |= 0x0001;} // Bit 4 = D1 or Q + if (ID13Field & 0x0008) {hexGillham |= 0x0200;} // Bit 3 = B2 + if (ID13Field & 0x0004) {hexGillham |= 0x0002;} // Bit 2 = D2 + if (ID13Field & 0x0002) {hexGillham |= 0x0400;} // Bit 1 = B4 + if (ID13Field & 0x0001) {hexGillham |= 0x0004;} // Bit 0 = D4 + + return (hexGillham); + } +// +//========================================================================= +// +// Decode the 13 bit AC altitude field (in DF 20 and others). +// Returns the altitude, and set 'unit' to either MODES_UNIT_METERS or MDOES_UNIT_FEETS. +// +int decodeAC13Field(int AC13Field, int *unit) { + int m_bit = AC13Field & 0x0040; // set = meters, clear = feet + int q_bit = AC13Field & 0x0010; // set = 25 ft encoding, clear = Gillham Mode C encoding + + if (!m_bit) { + *unit = MODES_UNIT_FEET; + if (q_bit) { + // N is the 11 bit integer resulting from the removal of bit Q and M + int n = ((AC13Field & 0x1F80) >> 2) | + ((AC13Field & 0x0020) >> 1) | + (AC13Field & 0x000F); + // The final altitude is resulting number multiplied by 25, minus 1000. + return ((n * 25) - 1000); + } else { + // N is an 11 bit Gillham coded altitude + int n = ModeAToModeC(decodeID13Field(AC13Field)); + if (n < -12) {n = 0;} + + return (100 * n); + } + } else { + *unit = MODES_UNIT_METERS; + // TODO: Implement altitude when meter unit is selected + } + return 0; +} +// +//========================================================================= +// +// Decode the 12 bit AC altitude field (in DF 17 and others). +// +int decodeAC12Field(int AC12Field, int *unit) { + int q_bit = AC12Field & 0x10; // Bit 48 = Q + + *unit = MODES_UNIT_FEET; + if (q_bit) { + /// N is the 11 bit integer resulting from the removal of bit Q at bit 4 + int n = ((AC12Field & 0x0FE0) >> 1) | + (AC12Field & 0x000F); + // The final altitude is the resulting number multiplied by 25, minus 1000. + return ((n * 25) - 1000); + } else { + // Make N a 13 bit Gillham coded altitude by inserting M=0 at bit 6 + int n = ((AC12Field & 0x0FC0) << 1) | + (AC12Field & 0x003F); + n = ModeAToModeC(decodeID13Field(n)); + if (n < -12) {n = 0;} + + return (100 * n); + } +} +// +//========================================================================= +// +// Decode the 7 bit ground movement field PWL exponential style scale +// +int decodeMovementField(int movement) { + int gspeed; + + // Note : movement codes 0,125,126,127 are all invalid, but they are + // trapped for before this function is called. + + if (movement > 123) gspeed = 199; // > 175kt + else if (movement > 108) gspeed = ((movement - 108) * 5) + 100; + else if (movement > 93) gspeed = ((movement - 93) * 2) + 70; + else if (movement > 38) gspeed = ((movement - 38) ) + 15; + else if (movement > 12) gspeed = ((movement - 11) >> 1) + 2; + else if (movement > 8) gspeed = ((movement - 6) >> 2) + 1; + else gspeed = 0; + + return (gspeed); +} +// +//========================================================================= +// +// Capability table +char *ca_str[8] = { + /* 0 */ "Level 1 (Surveillance Only)", + /* 1 */ "Level 2 (DF0,4,5,11)", + /* 2 */ "Level 3 (DF0,4,5,11,20,21)", + /* 3 */ "Level 4 (DF0,4,5,11,20,21,24)", + /* 4 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is on ground)", + /* 5 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is airborne)", + /* 6 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7)", + /* 7 */ "Level 7 ???" +}; + +// DF 18 Control field table. +char *cf_str[8] = { + /* 0 */ "ADS-B ES/NT device with ICAO 24-bit address", + /* 1 */ "ADS-B ES/NT device with other address", + /* 2 */ "Fine format TIS-B", + /* 3 */ "Coarse format TIS-B", + /* 4 */ "TIS-B management message", + /* 5 */ "TIS-B relay of ADS-B message with other address", + /* 6 */ "ADS-B rebroadcast using DF-17 message format", + /* 7 */ "Reserved" +}; + +// Flight status table +char *fs_str[8] = { + /* 0 */ "Normal, Airborne", + /* 1 */ "Normal, On the ground", + /* 2 */ "ALERT, Airborne", + /* 3 */ "ALERT, On the ground", + /* 4 */ "ALERT & Special Position Identification. Airborne or Ground", + /* 5 */ "Special Position Identification. Airborne or Ground", + /* 6 */ "Value 6 is not assigned", + /* 7 */ "Value 7 is not assigned" +}; + +// Emergency state table +// from https://www.ll.mit.edu/mission/aviation/publications/publication-files/atc-reports/Grappel_2007_ATC-334_WW-15318.pdf +// and 1090-DO-260B_FRAC +char *es_str[8] = { + /* 0 */ "No emergency", + /* 1 */ "General emergency (squawk 7700)", + /* 2 */ "Lifeguard/Medical", + /* 3 */ "Minimum fuel", + /* 4 */ "No communications (squawk 7600)", + /* 5 */ "Unlawful interference (squawk 7500)", + /* 6 */ "Downed Aircraft", + /* 7 */ "Reserved" +}; +// +//========================================================================= +// +char *getMEDescription(int metype, int mesub) { + char *mename = "Unknown"; + + if (metype >= 1 && metype <= 4) + mename = "Aircraft Identification and Category"; + else if (metype >= 5 && metype <= 8) + mename = "Surface Position"; + else if (metype >= 9 && metype <= 18) + mename = "Airborne Position (Baro Altitude)"; + else if (metype == 19 && mesub >=1 && mesub <= 4) + mename = "Airborne Velocity"; + else if (metype >= 20 && metype <= 22) + mename = "Airborne Position (GNSS Height)"; + else if (metype == 23 && mesub == 0) + mename = "Test Message"; + else if (metype == 23 && mesub == 7) + mename = "Test Message -- Squawk"; + else if (metype == 24 && mesub == 1) + mename = "Surface System Status"; + else if (metype == 28 && mesub == 1) + mename = "Extended Squitter Aircraft Status (Emergency)"; + else if (metype == 28 && mesub == 2) + mename = "Extended Squitter Aircraft Status (1090ES TCAS RA)"; + else if (metype == 29 && (mesub == 0 || mesub == 1)) + mename = "Target State and Status Message"; + else if (metype == 31 && (mesub == 0 || mesub == 1)) + mename = "Aircraft Operational Status Message"; + return mename; +} +// +//========================================================================= +// +// Decode a raw Mode S message demodulated as a stream of bytes by detectModeS(), +// and split it into fields populating a modesMessage structure. +// +void decodeModesMessage(Modes *modes, struct modesMessage *mm, unsigned char *msg) { + char *ais_charset = "?ABCDEFGHIJKLMNOPQRSTUVWXYZ????? ???????????????0123456789??????"; + + // Work on our local copy + memcpy(mm->msg, msg, MODES_LONG_MSG_BYTES); + msg = mm->msg; + + // Get the message type ASAP as other operations depend on this + mm->msgtype = msg[0] >> 3; // Downlink Format + mm->msgbits = modesMessageLenByType(mm->msgtype); + mm->crc = modesChecksum(msg, mm->msgbits); + + if ((mm->crc) && (modes->nfix_crc) && ((mm->msgtype == 17) || (mm->msgtype == 18))) { +// if ((mm->crc) && (modes->nfix_crc) && ((mm->msgtype == 11) || (mm->msgtype == 17))) { + // + // Fixing single bit errors in DF-11 is a bit dodgy because we have no way to + // know for sure if the crc is supposed to be 0 or not - it could be any value + // less than 80. Therefore, attempting to fix DF-11 errors can result in a + // multitude of possible crc solutions, only one of which is correct. + // + // We should probably perform some sanity checks on corrected DF-11's before + // using the results. Perhaps check the ICAO against known aircraft, and check + // IID against known good IID's. That's a TODO. + // + mm->correctedbits = fixBitErrors(msg, mm->msgbits, modes->nfix_crc, mm->corrected); + + // If we correct, validate ICAO addr to help filter birthday paradox solutions. + if (mm->correctedbits) { + uint32_t ulAddr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + if (!ICAOAddressWasRecentlySeen(modes, ulAddr)) + mm->correctedbits = 0; + } + } + // + // Note that most of the other computation happens *after* we fix the + // single/two bit errors, otherwise we would need to recompute the fields again. + // + if (mm->msgtype == 11) { // DF 11 + mm->iid = mm->crc; + mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + mm->ca = (msg[0] & 0x07); // Responder capabilities + + if ((mm->crcok = (0 == mm->crc))) { + // DF 11 : if crc == 0 try to populate our ICAO addresses whitelist. + addRecentlySeenICAOAddr(modes, mm->addr); + } else if (mm->crc < 80) { + mm->crcok = ICAOAddressWasRecentlySeen(modes, mm->addr); + if (mm->crcok) { + addRecentlySeenICAOAddr(modes, mm->addr); + } + } + + } else if (mm->msgtype == 17) { // DF 17 + mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + mm->ca = (msg[0] & 0x07); // Responder capabilities + + if ((mm->crcok = (0 == mm->crc))) { + // DF 17 : if crc == 0 try to populate our ICAO addresses whitelist. + addRecentlySeenICAOAddr(modes, mm->addr); + } + + } else if (mm->msgtype == 18) { // DF 18 + mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + mm->ca = (msg[0] & 0x07); // Control Field + + if ((mm->crcok = (0 == mm->crc))) { + // DF 18 : if crc == 0 try to populate our ICAO addresses whitelist. + addRecentlySeenICAOAddr(modes, mm->addr); + } + + } else { // All other DF's + // Compare the checksum with the whitelist of recently seen ICAO + // addresses. If it matches one, then declare the message as valid + mm->crcok = ICAOAddressWasRecentlySeen(modes, mm->addr = mm->crc); + } + + // If we're checking CRC and the CRC is invalid, then we can't trust any + // of the data contents, so save time and give up now. + if ((modes->check_crc) && (!mm->crcok) && (!mm->correctedbits)) { return;} + + // Fields for DF0, DF16 + if (mm->msgtype == 0 || mm->msgtype == 16) { + if (msg[0] & 0x04) { // VS Bit + mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG; + } else { + mm->bFlags |= MODES_ACFLAGS_AOG_VALID; + } + } + + // Fields for DF11, DF17 + if (mm->msgtype == 11 || mm->msgtype == 17) { + if (mm->ca == 4) { + mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG; + } else if (mm->ca == 5) { + mm->bFlags |= MODES_ACFLAGS_AOG_VALID; + } + } + + // Fields for DF5, DF21 = Gillham encoded Squawk + if (mm->msgtype == 5 || mm->msgtype == 21) { + int ID13Field = ((msg[2] << 8) | msg[3]) & 0x1FFF; + if (ID13Field) { + mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID; + mm->modeA = decodeID13Field(ID13Field); + } + } + + // Fields for DF0, DF4, DF16, DF20 13 bit altitude + if (mm->msgtype == 0 || mm->msgtype == 4 || + mm->msgtype == 16 || mm->msgtype == 20) { + int AC13Field = ((msg[2] << 8) | msg[3]) & 0x1FFF; + if (AC13Field) { // Only attempt to decode if a valid (non zero) altitude is present + mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; + mm->altitude = decodeAC13Field(AC13Field, &mm->unit); + } + } + + // Fields for DF4, DF5, DF20, DF21 + if ((mm->msgtype == 4) || (mm->msgtype == 20) || + (mm->msgtype == 5) || (mm->msgtype == 21)) { + mm->bFlags |= MODES_ACFLAGS_FS_VALID; + mm->fs = msg[0] & 7; // Flight status for DF4,5,20,21 + if (mm->fs <= 3) { + mm->bFlags |= MODES_ACFLAGS_AOG_VALID; + if (mm->fs & 1) + {mm->bFlags |= MODES_ACFLAGS_AOG;} + } + } + + // Fields for DF17, DF18_CF0, DF18_CF1, DF18_CF6 squitters + if ( (mm->msgtype == 17) + || ((mm->msgtype == 18) && ((mm->ca == 0) || (mm->ca == 1) || (mm->ca == 6)) )) { + int metype = mm->metype = msg[4] >> 3; // Extended squitter message type + int mesub = mm->mesub = (metype == 29 ? ((msg[4]&6)>>1) : (msg[4] & 7)); // Extended squitter message subtype + + // Decode the extended squitter message + + if (metype >= 1 && metype <= 4) { // Aircraft Identification and Category + uint32_t chars; + mm->bFlags |= MODES_ACFLAGS_CALLSIGN_VALID; + + chars = (msg[5] << 16) | (msg[6] << 8) | (msg[7]); + mm->flight[3] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[2] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[1] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[0] = ais_charset[chars & 0x3F]; + + chars = (msg[8] << 16) | (msg[9] << 8) | (msg[10]); + mm->flight[7] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[6] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[5] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[4] = ais_charset[chars & 0x3F]; + + mm->flight[8] = '\0'; + + } else if (metype == 19) { // Airborne Velocity Message + + // Presumably airborne if we get an Airborne Velocity Message + mm->bFlags |= MODES_ACFLAGS_AOG_VALID; + + if ( (mesub >= 1) && (mesub <= 4) ) { + int vert_rate = ((msg[8] & 0x07) << 6) | (msg[9] >> 2); + if (vert_rate) { + --vert_rate; + if (msg[8] & 0x08) + {vert_rate = 0 - vert_rate;} + mm->vert_rate = vert_rate * 64; + mm->bFlags |= MODES_ACFLAGS_VERTRATE_VALID; + } + } + + if ((mesub == 1) || (mesub == 2)) { + int ew_raw = ((msg[5] & 0x03) << 8) | msg[6]; + int ew_vel = ew_raw - 1; + int ns_raw = ((msg[7] & 0x7F) << 3) | (msg[8] >> 5); + int ns_vel = ns_raw - 1; + + if (mesub == 2) { // If (supersonic) unit is 4 kts + ns_vel = ns_vel << 2; + ew_vel = ew_vel << 2; + } + + if (ew_raw) { // Do East/West + mm->bFlags |= MODES_ACFLAGS_EWSPEED_VALID; + if (msg[5] & 0x04) + {ew_vel = 0 - ew_vel;} + mm->ew_velocity = ew_vel; + } + + if (ns_raw) { // Do North/South + mm->bFlags |= MODES_ACFLAGS_NSSPEED_VALID; + if (msg[7] & 0x80) + {ns_vel = 0 - ns_vel;} + mm->ns_velocity = ns_vel; + } + + if (ew_raw && ns_raw) { + // Compute velocity and angle from the two speed components + mm->bFlags |= (MODES_ACFLAGS_SPEED_VALID | MODES_ACFLAGS_HEADING_VALID | MODES_ACFLAGS_NSEWSPD_VALID); + mm->velocity = (int) sqrt((ns_vel * ns_vel) + (ew_vel * ew_vel)); + + if (mm->velocity) { + mm->heading = (int) (atan2(ew_vel, ns_vel) * 180.0 / M_PI); + // We don't want negative values but a 0-360 scale + if (mm->heading < 0) mm->heading += 360; + } + } + + } else if (mesub == 3 || mesub == 4) { + int airspeed = ((msg[7] & 0x7f) << 3) | (msg[8] >> 5); + if (airspeed) { + mm->bFlags |= MODES_ACFLAGS_SPEED_VALID; + --airspeed; + if (mesub == 4) // If (supersonic) unit is 4 kts + {airspeed = airspeed << 2;} + mm->velocity = airspeed; + } + + if (msg[5] & 0x04) { + mm->bFlags |= MODES_ACFLAGS_HEADING_VALID; + mm->heading = ((((msg[5] & 0x03) << 8) | msg[6]) * 45) >> 7; + } + } + + } else if (metype >= 5 && metype <= 22) { // Position Message + mm->raw_latitude = ((msg[6] & 3) << 15) | (msg[7] << 7) | (msg[8] >> 1); + mm->raw_longitude = ((msg[8] & 1) << 16) | (msg[9] << 8) | (msg[10]); + mm->bFlags |= (mm->msg[6] & 0x04) ? MODES_ACFLAGS_LLODD_VALID + : MODES_ACFLAGS_LLEVEN_VALID; + if (metype >= 9) { // Airborne + int AC12Field = ((msg[5] << 4) | (msg[6] >> 4)) & 0x0FFF; + mm->bFlags |= MODES_ACFLAGS_AOG_VALID; + if (AC12Field) {// Only attempt to decode if a valid (non zero) altitude is present + mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; + mm->altitude = decodeAC12Field(AC12Field, &mm->unit); + } + } else { // Ground + int movement = ((msg[4] << 4) | (msg[5] >> 4)) & 0x007F; + mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG; + if ((movement) && (movement < 125)) { + mm->bFlags |= MODES_ACFLAGS_SPEED_VALID; + mm->velocity = decodeMovementField(movement); + } + + if (msg[5] & 0x08) { + mm->bFlags |= MODES_ACFLAGS_HEADING_VALID; + mm->heading = ((((msg[5] << 4) | (msg[6] >> 4)) & 0x007F) * 45) >> 4; + } + } + + } else if (metype == 23) { // Test metype squawk field + if (mesub == 7) { // (see 1090-WP-15-20) + int ID13Field = (((msg[5] << 8) | msg[6]) & 0xFFF1)>>3; + if (ID13Field) { + mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID; + mm->modeA = decodeID13Field(ID13Field); + } + } + + } else if (metype == 24) { // Reserved for Surface System Status + + } else if (metype == 28) { // Extended Squitter Aircraft Status + if (mesub == 1) { // Emergency status squawk field + int ID13Field = (((msg[5] << 8) | msg[6]) & 0x1FFF); + if (ID13Field) { + mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID; + mm->modeA = decodeID13Field(ID13Field); + } + } + + } else if (metype == 29) { // Aircraft Trajectory Intent + + } else if (metype == 30) { // Aircraft Operational Coordination + + } else if (metype == 31) { // Aircraft Operational Status + + } else { // Other metypes + + } + } + + // Fields for DF20, DF21 Comm-B + if ((mm->msgtype == 20) || (mm->msgtype == 21)){ + + if (msg[4] == 0x20) { // Aircraft Identification + uint32_t chars; + mm->bFlags |= MODES_ACFLAGS_CALLSIGN_VALID; + + chars = (msg[5] << 16) | (msg[6] << 8) | (msg[7]); + mm->flight[3] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[2] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[1] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[0] = ais_charset[chars & 0x3F]; + + chars = (msg[8] << 16) | (msg[9] << 8) | (msg[10]); + mm->flight[7] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[6] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[5] = ais_charset[chars & 0x3F]; chars = chars >> 6; + mm->flight[4] = ais_charset[chars & 0x3F]; + + mm->flight[8] = '\0'; + } else { + } + } +} + +//========================================================================= +// +// Return -1 if the message is out of fase left-side +// Return 1 if the message is out of fase right-size +// Return 0 if the message is not particularly out of phase. +// +// Note: this function will access pPreamble[-1], so the caller should make sure to +// call it only if we are not at the start of the current buffer +// +int detectOutOfPhase(uint16_t *pPreamble) { + if (pPreamble[ 3] > pPreamble[2]/3) return 1; + if (pPreamble[10] > pPreamble[9]/3) return 1; + if (pPreamble[ 6] > pPreamble[7]/3) return -1; + if (pPreamble[-1] > pPreamble[1]/3) return -1; + return 0; +} + + +uint16_t clamped_scale(uint16_t v, uint16_t scale) { + uint32_t scaled = (uint32_t)v * scale / 16384; + if (scaled > 65535) return 65535; + return (uint16_t) scaled; +} +// This function decides whether we are sampling early or late, +// and by approximately how much, by looking at the energy in +// preamble bits before and after the expected pulse locations. +// +// It then deals with one sample pair at a time, comparing samples +// to make a decision about the bit value. Based on this decision it +// modifies the sample value of the *adjacent* sample which will +// contain some of the energy from the bit we just inspected. +// +// pPayload[0] should be the start of the preamble, +// pPayload[-1 .. MODES_PREAMBLE_SAMPLES + MODES_LONG_MSG_SAMPLES - 1] should be accessible. +// pPayload[MODES_PREAMBLE_SAMPLES .. MODES_PREAMBLE_SAMPLES + MODES_LONG_MSG_SAMPLES - 1] will be updated. +void applyPhaseCorrection(uint16_t *pPayload) { + int j; + + // we expect 1 bits at 0, 2, 7, 9 + // and 0 bits at -1, 1, 3, 4, 5, 6, 8, 10, 11, 12, 13, 14 + // use bits -1,6 for early detection (bit 0/7 arrived a little early, our sample period starts after the bit phase so we include some of the next bit) + // use bits 3,10 for late detection (bit 2/9 arrived a little late, our sample period starts before the bit phase so we include some of the last bit) + + uint32_t onTime = (pPayload[0] + pPayload[2] + pPayload[7] + pPayload[9]); + uint32_t early = (pPayload[-1] + pPayload[6]) << 1; + uint32_t late = (pPayload[3] + pPayload[10]) << 1; + + if (early > late) { + // Our sample period starts late and so includes some of the next bit. + + uint16_t scaleUp = 16384 + 16384 * early / (early + onTime); // 1 + early / (early+onTime) + uint16_t scaleDown = 16384 - 16384 * early / (early + onTime); // 1 - early / (early+onTime) + + // trailing bits are 0; final data sample will be a bit low. + pPayload[MODES_PREAMBLE_SAMPLES + MODES_LONG_MSG_SAMPLES - 1] = + clamped_scale(pPayload[MODES_PREAMBLE_SAMPLES + MODES_LONG_MSG_SAMPLES - 1], scaleUp); + for (j = MODES_PREAMBLE_SAMPLES + MODES_LONG_MSG_SAMPLES - 2; j > MODES_PREAMBLE_SAMPLES; j -= 2) { + if (pPayload[j] > pPayload[j+1]) { + // x [1 0] y + // x overlapped with the "1" bit and is slightly high + pPayload[j-1] = clamped_scale(pPayload[j-1], scaleDown); + } else { + // x [0 1] y + // x overlapped with the "0" bit and is slightly low + pPayload[j-1] = clamped_scale(pPayload[j-1], scaleUp); + } + } + } else { + // Our sample period starts early and so includes some of the previous bit. + + uint16_t scaleUp = 16384 + 16384 * late / (late + onTime); // 1 + late / (late+onTime) + uint16_t scaleDown = 16384 - 16384 * late / (late + onTime); // 1 - late / (late+onTime) + + // leading bits are 0; first data sample will be a bit low. + pPayload[MODES_PREAMBLE_SAMPLES] = clamped_scale(pPayload[MODES_PREAMBLE_SAMPLES], scaleUp); + for (j = MODES_PREAMBLE_SAMPLES; j < MODES_PREAMBLE_SAMPLES + MODES_LONG_MSG_SAMPLES - 2; j += 2) { + if (pPayload[j] > pPayload[j+1]) { + // x [1 0] y + // y overlapped with the "0" bit and is slightly low + pPayload[j+2] = clamped_scale(pPayload[j+2], scaleUp); + } else { + // x [0 1] y + // y overlapped with the "1" bit and is slightly high + pPayload[j+2] = clamped_scale(pPayload[j+2], scaleDown); + } + } + } +} +// +//========================================================================= +// +// When a new message is available, because it was decoded from the RTL device, +// file, or received in the TCP input port, or any other way we can receive a +// decoded message, we call this function in order to use the message. +// +// Basically this function passes a raw message to the upper layers for further +// processing and visualization +// +void useModesMessage(Modes *modes, struct modesMessage *mm) { + if ((modes->check_crc == 0) || (mm->crcok) || (mm->correctedbits)) { // not checking, ok or fixed + + // Always track aircraft + interactiveReceiveData(modes, mm); + + // In non-interactive non-quiet mode, display messages on standard output + //if (!modes->interactive && !modes->quiet) { + // displayModesMessage(mm); + //} + + // Feed output clients + //if (modes->net) {modesQueueOutput(mm);} + + // Heartbeat not required whilst we're seeing real messages + modes->net_heartbeat_count = 0; + } +} +// +//========================================================================= +// +// Always positive MOD operation, used for CPR decoding. +// +int cprModFunction(int a, int b) { + int res = a % b; + if (res < 0) res += b; + return res; +} +// +//========================================================================= +// +// The NL function uses the precomputed table from 1090-WP-9-14 +// +int cprNLFunction(double lat) { + if (lat < 0) lat = -lat; // Table is simmetric about the equator + if (lat < 10.47047130) return 59; + if (lat < 14.82817437) return 58; + if (lat < 18.18626357) return 57; + if (lat < 21.02939493) return 56; + if (lat < 23.54504487) return 55; + if (lat < 25.82924707) return 54; + if (lat < 27.93898710) return 53; + if (lat < 29.91135686) return 52; + if (lat < 31.77209708) return 51; + if (lat < 33.53993436) return 50; + if (lat < 35.22899598) return 49; + if (lat < 36.85025108) return 48; + if (lat < 38.41241892) return 47; + if (lat < 39.92256684) return 46; + if (lat < 41.38651832) return 45; + if (lat < 42.80914012) return 44; + if (lat < 44.19454951) return 43; + if (lat < 45.54626723) return 42; + if (lat < 46.86733252) return 41; + if (lat < 48.16039128) return 40; + if (lat < 49.42776439) return 39; + if (lat < 50.67150166) return 38; + if (lat < 51.89342469) return 37; + if (lat < 53.09516153) return 36; + if (lat < 54.27817472) return 35; + if (lat < 55.44378444) return 34; + if (lat < 56.59318756) return 33; + if (lat < 57.72747354) return 32; + if (lat < 58.84763776) return 31; + if (lat < 59.95459277) return 30; + if (lat < 61.04917774) return 29; + if (lat < 62.13216659) return 28; + if (lat < 63.20427479) return 27; + if (lat < 64.26616523) return 26; + if (lat < 65.31845310) return 25; + if (lat < 66.36171008) return 24; + if (lat < 67.39646774) return 23; + if (lat < 68.42322022) return 22; + if (lat < 69.44242631) return 21; + if (lat < 70.45451075) return 20; + if (lat < 71.45986473) return 19; + if (lat < 72.45884545) return 18; + if (lat < 73.45177442) return 17; + if (lat < 74.43893416) return 16; + if (lat < 75.42056257) return 15; + if (lat < 76.39684391) return 14; + if (lat < 77.36789461) return 13; + if (lat < 78.33374083) return 12; + if (lat < 79.29428225) return 11; + if (lat < 80.24923213) return 10; + if (lat < 81.19801349) return 9; + if (lat < 82.13956981) return 8; + if (lat < 83.07199445) return 7; + if (lat < 83.99173563) return 6; + if (lat < 84.89166191) return 5; + if (lat < 85.75541621) return 4; + if (lat < 86.53536998) return 3; + if (lat < 87.00000000) return 2; + else return 1; +} +// +//========================================================================= +// +int cprNFunction(double lat, int fflag) { + int nl = cprNLFunction(lat) - (fflag ? 1 : 0); + if (nl < 1) nl = 1; + return nl; +} +// +//========================================================================= +// +double cprDlonFunction(double lat, int fflag, int surface) { + return (surface ? 90.0 : 360.0) / cprNFunction(lat, fflag); +} +// +//========================================================================= +// +// This algorithm comes from: +// http://www.lll.lu/~edward/edward/adsb/DecodingADSBposition.html. +// +// A few remarks: +// 1) 131072 is 2^17 since CPR latitude and longitude are encoded in 17 bits. +// +int decodeCPR(Modes *modes, struct aircraft *a, int fflag, int surface) { + double AirDlat0 = (surface ? 90.0 : 360.0) / 60.0; + double AirDlat1 = (surface ? 90.0 : 360.0) / 59.0; + double lat0 = a->even_cprlat; + double lat1 = a->odd_cprlat; + double lon0 = a->even_cprlon; + double lon1 = a->odd_cprlon; + + // Compute the Latitude Index "j" + int j = (int) floor(((59*lat0 - 60*lat1) / 131072) + 0.5); + double rlat0 = AirDlat0 * (cprModFunction(j,60) + lat0 / 131072); + double rlat1 = AirDlat1 * (cprModFunction(j,59) + lat1 / 131072); + + time_t now = time(NULL); + double surface_rlat = MODES_USER_LATITUDE_DFLT; + double surface_rlon = MODES_USER_LONGITUDE_DFLT; + + if (surface) { + // If we're on the ground, make sure we have a (likely) valid Lat/Lon + if ((a->bFlags & MODES_ACFLAGS_LATLON_VALID) && (((int)(now - a->seenLatLon)) < modes->interactive_display_ttl)) { + surface_rlat = a->lat; + surface_rlon = a->lon; + } else if (modes->bUserFlags & MODES_USER_LATLON_VALID) { + surface_rlat = modes->fUserLat; + surface_rlon = modes->fUserLon; + } else { + // No local reference, give up + return (-1); + } + rlat0 += floor(surface_rlat / 90.0) * 90.0; // Move from 1st quadrant to our quadrant + rlat1 += floor(surface_rlat / 90.0) * 90.0; + } else { + if (rlat0 >= 270) rlat0 -= 360; + if (rlat1 >= 270) rlat1 -= 360; + } + + // Check to see that the latitude is in range: -90 .. +90 + if (rlat0 < -90 || rlat0 > 90 || rlat1 < -90 || rlat1 > 90) + return (-1); + + // Check that both are in the same latitude zone, or abort. + if (cprNLFunction(rlat0) != cprNLFunction(rlat1)) + return (-1); + + // Compute ni and the Longitude Index "m" + if (fflag) { // Use odd packet. + int ni = cprNFunction(rlat1,1); + int m = (int) floor((((lon0 * (cprNLFunction(rlat1)-1)) - + (lon1 * cprNLFunction(rlat1))) / 131072.0) + 0.5); + a->lon = cprDlonFunction(rlat1, 1, surface) * (cprModFunction(m, ni)+lon1/131072); + a->lat = rlat1; + } else { // Use even packet. + int ni = cprNFunction(rlat0,0); + int m = (int) floor((((lon0 * (cprNLFunction(rlat0)-1)) - + (lon1 * cprNLFunction(rlat0))) / 131072) + 0.5); + a->lon = cprDlonFunction(rlat0, 0, surface) * (cprModFunction(m, ni)+lon0/131072); + a->lat = rlat0; + } + + if (surface) { + a->lon += floor(surface_rlon / 90.0) * 90.0; // Move from 1st quadrant to our quadrant + } else if (a->lon > 180) { + a->lon -= 360; + } + + a->seenLatLon = a->seen; + a->timestampLatLon = a->timestamp; + a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK); + + return 0; +} +// +//========================================================================= +// +// This algorithm comes from: +// 1090-WP29-07-Draft_CPR101 (which also defines decodeCPR() ) +// +// There is an error in this document related to CPR relative decode. +// Should use trunc() rather than the floor() function in Eq 38 and related for deltaZI. +// floor() returns integer less than argument +// trunc() returns integer closer to zero than argument. +// Note: text of document describes trunc() functionality for deltaZI calculation +// but the formulae use floor(). +// +int decodeCPRrelative(Modes *modes, struct aircraft *a, int fflag, int surface) { + double AirDlat; + double AirDlon; + double lat; + double lon; + double lonr, latr; + double rlon, rlat; + int j,m; + + if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) { // Ok to try aircraft relative first + latr = a->lat; + lonr = a->lon; + } else if (modes->bUserFlags & MODES_USER_LATLON_VALID) { // Try ground station relative next + latr = modes->fUserLat; + lonr = modes->fUserLon; + } else { + return (-1); // Exit with error - can't do relative if we don't have ref. + } + + if (fflag) { // odd + AirDlat = (surface ? 90.0 : 360.0) / 59.0; + lat = a->odd_cprlat; + lon = a->odd_cprlon; + } else { // even + AirDlat = (surface ? 90.0 : 360.0) / 60.0; + lat = a->even_cprlat; + lon = a->even_cprlon; + } + + // Compute the Latitude Index "j" + j = (int) (floor(latr/AirDlat) + + trunc(0.5 + cprModFunction((int)latr, (int)AirDlat)/AirDlat - lat/131072)); + rlat = AirDlat * (j + lat/131072); + if (rlat >= 270) rlat -= 360; + + // Check to see that the latitude is in range: -90 .. +90 + if (rlat < -90 || rlat > 90) { + a->bFlags &= ~MODES_ACFLAGS_LATLON_REL_OK; // This will cause a quick exit next time if no global has been done + return (-1); // Time to give up - Latitude error + } + + // Check to see that answer is reasonable - ie no more than 1/2 cell away + if (fabs(rlat - a->lat) > (AirDlat/2)) { + a->bFlags &= ~MODES_ACFLAGS_LATLON_REL_OK; // This will cause a quick exit next time if no global has been done + return (-1); // Time to give up - Latitude error + } + + // Compute the Longitude Index "m" + AirDlon = cprDlonFunction(rlat, fflag, surface); + m = (int) (floor(lonr/AirDlon) + + trunc(0.5 + cprModFunction((int)lonr, (int)AirDlon)/AirDlon - lon/131072)); + rlon = AirDlon * (m + lon/131072); + if (rlon > 180) rlon -= 360; + + // Check to see that answer is reasonable - ie no more than 1/2 cell away + if (fabs(rlon - a->lon) > (AirDlon/2)) { + a->bFlags &= ~MODES_ACFLAGS_LATLON_REL_OK; // This will cause a quick exit next time if no global has been done + return (-1); // Time to give up - Longitude error + } + + a->lat = rlat; + a->lon = rlon; + + a->seenLatLon = a->seen; + a->timestampLatLon = a->timestamp; + a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK); + return (0); +} +// +// ===================== Mode S detection and decoding =================== +// diff --git a/mode_s.c.REMOVED.git-id b/mode_s.c.REMOVED.git-id deleted file mode 100644 index 6035912..0000000 --- a/mode_s.c.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -aaf101fcf91be4f20921260cd579f3fa5dda321e \ No newline at end of file diff --git a/net_io.c b/net_io.c index 5b30103..0f9d460 100644 --- a/net_io.c +++ b/net_io.c @@ -55,102 +55,18 @@ struct service { struct service services[MODES_NET_SERVICES_NUM]; -void modesInitNet(void) { - int j; - - struct service svc[MODES_NET_SERVICES_NUM] = { - {"Raw TCP output", &modes.ros, modes.net_output_raw_port, 1}, - {"Raw TCP input", &modes.ris, modes.net_input_raw_port, 1}, - {"Beast TCP output", &modes.bos, modes.net_output_beast_port, 1}, - {"Beast TCP input", &modes.bis, modes.net_input_beast_port, 1}, - {"HTTP server", &modes.https, modes.net_http_port, 1}, - {"Basestation TCP output", &modes.sbsos, modes.net_output_sbs_port, 1} - }; - - memcpy(&services, &svc, sizeof(svc));//services = svc; - - modes.clients = NULL; - -#ifdef _WIN32 - if ( (!modes.wsaData.wVersion) - && (!modes.wsaData.wHighVersion) ) { - // Try to start the windows socket support - if (WSAStartup(MAKEWORD(2,1),&modes.wsaData) != 0) - { - fprintf(stderr, "WSAStartup returned Error\n"); - } - } -#endif - - for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { - services[j].enabled = (services[j].port != 0); - if (services[j].enabled) { - int s = anetTcpServer(modes.aneterr, services[j].port, modes.net_bind_address); - if (s == -1) { - fprintf(stderr, "Error opening the listening port %d (%s): %s\n", - services[j].port, services[j].descr, modes.aneterr); - exit(1); - } - anetNonBlock(modes.aneterr, s); - *services[j].socket = s; - } else { - if (modes.debug & MODES_DEBUG_NET) printf("%s port is disabled\n", services[j].descr); - } - } - -#ifndef _WIN32 - signal(SIGPIPE, SIG_IGN); -#endif -} -// -//========================================================================= -// -// This function gets called from time to time when the decoding thread is -// awakened by new data arriving. This usually happens a few times every second -// -struct client * modesAcceptClients(void) { - int fd, port; - unsigned int j; - struct client *c; - - for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { - if (services[j].enabled) { - fd = anetTcpAccept(modes.aneterr, *services[j].socket, NULL, &port); - if (fd == -1) continue; - - anetNonBlock(modes.aneterr, fd); - c = (struct client *) malloc(sizeof(*c)); - c->service = *services[j].socket; - c->next = modes.clients; - c->fd = fd; - c->buflen = 0; - modes.clients = c; - anetSetSendBuffer(modes.aneterr,fd, (MODES_NET_SNDBUF_SIZE << modes.net_sndbuf_size)); - - if (*services[j].socket == modes.sbsos) modes.stat_sbs_connections++; - if (*services[j].socket == modes.ros) modes.stat_raw_connections++; - if (*services[j].socket == modes.bos) modes.stat_beast_connections++; - - j--; // Try again with the same listening port - - if (modes.debug & MODES_DEBUG_NET) - printf("Created new client %d\n", fd); - } - } - return modes.clients; -} // //========================================================================= // // On error free the client, collect the structure, adjust maxfd if needed. // -void modesFreeClient(struct client *c) { +void modesFreeClient(Modes * modes, struct client *c) { // Unhook this client from the linked list of clients - struct client *p = modes.clients; + struct client *p = modes->clients; if (p) { if (p == c) { - modes.clients = c->next; + modes->clients = c->next; } else { while ((p) && (p->next != c)) { p = p->next; @@ -168,17 +84,17 @@ void modesFreeClient(struct client *c) { // // Close the client connection and mark it as closed // -void modesCloseClient(struct client *c) { +void modesCloseClient(Modes *modes, struct client *c) { close(c->fd); - if (c->service == modes.sbsos) { - if (modes.stat_sbs_connections) modes.stat_sbs_connections--; - } else if (c->service == modes.ros) { - if (modes.stat_raw_connections) modes.stat_raw_connections--; - } else if (c->service == modes.bos) { - if (modes.stat_beast_connections) modes.stat_beast_connections--; + if (c->service == modes->sbsos) { + if (modes->stat_sbs_connections) modes->stat_sbs_connections--; + } else if (c->service == modes->ros) { + if (modes->stat_raw_connections) modes->stat_raw_connections--; + } else if (c->service == modes->bos) { + if (modes->stat_beast_connections) modes->stat_beast_connections--; } - if (modes.debug & MODES_DEBUG_NET) + if (modes->debug & MODES_DEBUG_NET) printf("Closing client %d\n", c->fd); c->fd = -1; @@ -196,7 +112,7 @@ void modesCloseClient(struct client *c) { // The function always returns 0 (success) to the caller as there is no // case where we want broken messages here to close the client connection. // -int decodeBinMessage(struct client *c, char *p) { +int decodeBinMessage(Modes *modes, struct client *c, char *p) { int msgLen = 0; int j; char ch; @@ -209,7 +125,7 @@ int decodeBinMessage(struct client *c, char *p) { ch = *p++; /// Get the message type if (0x1A == ch) {p++;} - if ((ch == '1') && (modes.mode_ac)) { // skip ModeA/C unless user enables --modes-ac + if ((ch == '1') && (modes->mode_ac)) { // skip ModeA/C unless user enables --modes-ac msgLen = MODEAC_MSG_BYTES; } else if (ch == '2') { msgLen = MODES_SHORT_MSG_BYTES; @@ -236,13 +152,13 @@ int decodeBinMessage(struct client *c, char *p) { if (0x1A == ch) {p++;} } - if (msgLen == MODEAC_MSG_BYTES) { // ModeA or ModeC - decodeModeAMessage(&mm, ((msg[0] << 8) | msg[1])); - } else { - decodeModesMessage(&mm, msg); - } + // if (msgLen == MODEAC_MSG_BYTES) { // ModeA or ModeC + // decodeModeAMessage(modes, &mm, ((msg[0] << 8) | msg[1])); + // } else { + decodeModesMessage(modes, &mm, msg); + //} - useModesMessage(&mm); + useModesMessage(modes, &mm); } return (0); } @@ -259,91 +175,6 @@ int hexDigitVal(int c) { else return -1; } // -//========================================================================= -// -// This function decodes a string representing message in raw hex format -// like: *8D4B969699155600E87406F5B69F; The string is null-terminated. -// -// The message is passed to the higher level layers, so it feeds -// the selected screen output, the network output and so forth. -// -// If the message looks invalid it is silently discarded. -// -// The function always returns 0 (success) to the caller as there is no -// case where we want broken messages here to close the client connection. -// -int decodeHexMessage(struct client *c, char *hex) { - int l = strlen(hex), j; - unsigned char msg[MODES_LONG_MSG_BYTES]; - struct modesMessage mm; - MODES_NOTUSED(c); - memset(&mm, 0, sizeof(mm)); - - // Mark messages received over the internet as remote so that we don't try to - // pass them off as being received by this instance when forwarding them - mm.remote = 1; - mm.signalLevel = 0xFF; - - // Remove spaces on the left and on the right - while(l && isspace(hex[l-1])) { - hex[l-1] = '\0'; l--; - } - while(isspace(*hex)) { - hex++; l--; - } - - // Turn the message into binary. - // Accept *-AVR raw @-AVR/BEAST timeS+raw %-AVR timeS+raw (CRC good) <-BEAST timeS+sigL+raw - // and some AVR records that we can understand - if (hex[l-1] != ';') {return (0);} // not complete - abort - - switch(hex[0]) { - case '<': { - mm.signalLevel = (hexDigitVal(hex[13])<<4) | hexDigitVal(hex[14]); - hex += 15; l -= 16; // Skip <, timestamp and siglevel, and ; - break;} - - case '@': // No CRC check - case '%': { // CRC is OK - hex += 13; l -= 14; // Skip @,%, and timestamp, and ; - break;} - - case '*': - case ':': { - hex++; l-=2; // Skip * and ; - break;} - - default: { - return (0); // We don't know what this is, so abort - break;} - } - - if ( (l != (MODEAC_MSG_BYTES * 2)) - && (l != (MODES_SHORT_MSG_BYTES * 2)) - && (l != (MODES_LONG_MSG_BYTES * 2)) ) - {return (0);} // Too short or long message... broken - - if ( (0 == modes.mode_ac) - && (l == (MODEAC_MSG_BYTES * 2)) ) - {return (0);} // Right length for ModeA/C, but not enabled - - for (j = 0; j < l; j += 2) { - int high = hexDigitVal(hex[j]); - int low = hexDigitVal(hex[j+1]); - - if (high == -1 || low == -1) return 0; - msg[j/2] = (high << 4) | low; - } - - if (l == (MODEAC_MSG_BYTES * 2)) { // ModeA or ModeC - decodeModeAMessage(&mm, ((msg[0] << 8) | msg[1])); - } else { // Assume ModeS - decodeModesMessage(&mm, msg); - } - - useModesMessage(&mm); - return (0); -} // //========================================================================= // @@ -359,8 +190,8 @@ int decodeHexMessage(struct client *c, char *hex) { // The handler returns 0 on success, or 1 to signal this function we should // close the connection with the client in case of non-recoverable errors. // -void modesReadFromClient(struct client *c, char *sep, - int(*handler)(struct client *, char *)) { +void modesReadFromClient(Modes *modes, struct client *c, char *sep, + int(*handler)(Modes *modes, struct client *, char *)) { int left; int nread; int fullmsg; @@ -384,7 +215,7 @@ void modesReadFromClient(struct client *c, char *sep, if (nread < 0) {errno = WSAGetLastError();} #endif if (nread == 0) { - modesCloseClient(c); + modesCloseClient(modes, c); return; } @@ -397,7 +228,7 @@ void modesReadFromClient(struct client *c, char *sep, #else if ( (nread < 0) && (errno != EWOULDBLOCK)) { // Error, or end of file #endif - modesCloseClient(c); + modesCloseClient(modes, c); return; } if (nread <= 0) { @@ -412,7 +243,7 @@ void modesReadFromClient(struct client *c, char *sep, - if (c->service == modes.bis) { + if (c->service == modes->bis) { // This is the Beast Binary scanning case. // If there is a complete message still in the buffer, there must be the separator 'sep' // in the buffer, note that we full-scan the buffer at every read for simplicity. @@ -446,8 +277,8 @@ void modesReadFromClient(struct client *c, char *sep, break; } // Have a 0x1a followed by 1, 2 or 3 - pass message less 0x1a to handler. - if (handler(c, s)) { - modesCloseClient(c); + if (handler(modes, c, s)) { + modesCloseClient(modes, c); return; } fullmsg = 1; @@ -462,8 +293,8 @@ void modesReadFromClient(struct client *c, char *sep, // while ((e = strstr(s, sep)) != NULL) { // end of first message if found *e = '\0'; // The handler expects null terminated strings - if (handler(c, s)) { // Pass message to handler. - modesCloseClient(c); // Handler returns 1 on error to signal we . + if (handler(modes, c, s)) { // Pass message to handler. + modesCloseClient(modes, c); // Handler returns 1 on error to signal we . return; // should close the client connection } s = e + strlen(sep); // Move to start of next message @@ -479,28 +310,3 @@ void modesReadFromClient(struct client *c, char *sep, } } } -// -//========================================================================= -// -// Read data from clients. This function actually delegates a lower-level -// function that depends on the kind of service (raw, http, ...). -// -void modesReadFromClients(void) { - - struct client *c = modesAcceptClients(); - - while (c) { - // Read next before servicing client incase the service routine deletes the client! - struct client *next = c->next; - - if (c->fd >= 0) { - modesReadFromClient(c,"",decodeBinMessage); - } else { - modesFreeClient(c); - } - c = next; - } -} -// -// =============================== Network IO =========================== -// diff --git a/status.c b/status.c index 8d45bd7..7b4dfad 100644 --- a/status.c +++ b/status.c @@ -13,66 +13,26 @@ void updateStatus() { double sigAccumulate = 0.0; double msgRateAccumulate = 0.0; -/* - while(a) { - int flags = a->modeACflags; - int msgs = a->messages; - if ( (((flags & (MODEAC_MSG_FLAG )) == 0 ) ) - || (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEA_ONLY)) == MODEAC_MSG_MODEA_ONLY) && (msgs > 4 ) ) - || (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEC_OLD )) == 0 ) && (msgs > 127) ) - ) { + // PlaneObj *p = appData.planes; - unsigned char * pSig = a->signalLevel; - unsigned int signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] + - pSig[4] + pSig[5] + pSig[6] + pSig[7]); + // while(p) { + // unsigned char * pSig = p->signalLevel; + // unsigned int signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] + + // pSig[4] + pSig[5] + pSig[6] + pSig[7]); - sigAccumulate += signalAverage; - - if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) { - double d = sqrt(a->dx * a->dx + a->dy * a->dy); - - if(d < appData.maxDist) { - if(d > maxDist) { - maxDist = d; - } - - if(d < 4.0) { - Status.closeCall = a; - } - - numVisiblePlanes++; - } - } - totalCount++; - } - - - msgRateAccumulate += (a->messageRate[0] + a->messageRate[1] + a->messageRate[2] + a->messageRate[3] + - a->messageRate[4] + a->messageRate[5] + a->messageRate[6] + a->messageRate[7]); - - a = a->next; - } -*/ - PlaneObj *p = appData.planes; - - while(p) { - unsigned char * pSig = p->signalLevel; - unsigned int signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] + - pSig[4] + pSig[5] + pSig[6] + pSig[7]); - - sigAccumulate += signalAverage; + // sigAccumulate += signalAverage; - if (p->lon && p->lat) { - numVisiblePlanes++; - } + // if (p->lon && p->lat) { + // numVisiblePlanes++; + // } - totalCount++; + // totalCount++; - msgRateAccumulate += p->messageRate; + // msgRateAccumulate += p->messageRate; - p = p->next; - } + // p = p->next; + // } Status.msgRate = msgRateAccumulate; Status.avgSig = sigAccumulate / (double) totalCount; diff --git a/structs.h b/structs.h index 6fdb073..43be8ff 100644 --- a/structs.h +++ b/structs.h @@ -3,42 +3,7 @@ #include "defs.h" - -// mirrors aircraft struct in dump1090, separating for refactoring - -typedef struct PlaneObj { - uint32_t addr; // ICAO address - char flight[16]; // Flight number - unsigned char signalLevel[8]; // Last 8 Signal Amplitudes - double messageRate; - int altitude; // Altitude - int speed; // Velocity - int track; // Angle of flight - int vert_rate; // Vertical rate. - time_t seen; // Time at which the last packet was received - time_t seenLatLon; // Time at which the last packet was received - time_t prev_seen; - double lat, lon; // Coordinated obtained from CPR encoded data - - //history - float oldLon[TRAIL_LENGTH]; - float oldLat[TRAIL_LENGTH]; - float oldHeading[TRAIL_LENGTH]; - time_t oldSeen[TRAIL_LENGTH]; - uint8_t oldIdx; - uint64_t created; - uint64_t msSeen; - uint64_t msSeenLatLon; - int live; - - struct PlaneObj *next; // Next aircraft in our linked list - -//// label stuff - - int x, y, cx, cy, w, h; - float ox, oy, dox, doy, ddox, ddoy; - float pressure; -} PlaneObj; +#include "AircraftData.h" typedef struct AppData @@ -93,8 +58,8 @@ typedef struct AppData QuadTree root; - PlaneObj *planes; - PlaneObj *selectedPlane; + //PlaneObj *planes; + //PlaneObj *selectedPlane; uint64_t lastFrameTime; } AppData; @@ -142,28 +107,17 @@ void drawStringBG(char *, int, int, TTF_Font *, SDL_Color, SDL_Color); void init(char *); void cleanup(void); -//input.c -void getInput(void); - //mapdata.c void initMaps(); //list.c void drawList(int top); -//draw.c -void draw(); -void latLonFromScreenCoords(float *lat, float *lon, int x, int y); -void moveCenterAbsolute(float x, float y); -void moveCenterRelative(float dx, float dy); -void registerClick(); //status.c void updateStatus(); void drawStatus(); -//planeObj.c -void updatePlanes(); #ifdef __cplusplus } #endif diff --git a/view1090.c b/view1090.bak similarity index 66% rename from view1090.c rename to view1090.bak index 9b93dee..e7007a2 100644 --- a/view1090.c +++ b/view1090.bak @@ -30,6 +30,7 @@ #include "view1090.h" #include "structs.h" +#include "AircraftData.h" int go = 1; @@ -37,117 +38,26 @@ int go = 1; AppData appData; Style style; -Modes modes; - // // ============================= Utility functions ========================== // void sigintHandler(int dummy) { NOTUSED(dummy); signal(SIGINT, SIG_DFL); // reset signal handler - bit extra safety - modes.exit = 1; // Signal to threads that we are done + AircraftData.modes.exit = 1; // Signal to threads that we are done } -// -// =============================== Initialization =========================== -// -void view1090InitConfig(void) { - // Default everything to zero/NULL - memset(&modes, 0, sizeof(Modes)); - memset(&View1090, 0, sizeof(View1090)); - - // Now initialise things that should not be 0/NULL to their defaults - modes.check_crc = 1; - strcpy(View1090.net_input_beast_ipaddr,VIEW1090_NET_OUTPUT_IP_ADDRESS); - modes.net_input_beast_port = MODES_NET_OUTPUT_BEAST_PORT; - modes.interactive_rows = MODES_INTERACTIVE_ROWS; - modes.interactive_delete_ttl = MODES_INTERACTIVE_DELETE_TTL; - modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL; - modes.fUserLat = MODES_USER_LATITUDE_DFLT; - modes.fUserLon = MODES_USER_LONGITUDE_DFLT; - - modes.interactive = 0; - modes.quiet = 1; - - // Map options - appData.maxDist = 25.0; - appData.centerLon = modes.fUserLon; - appData.centerLat = modes.fUserLat; - - // Display options - appData.screen_uiscale = 1; - appData.screen_width = 0; - appData.screen_height = 0; - appData.screen_depth = 32; - appData.fullscreen = 0; - - // Initialize status - Status.msgRate = 0; - Status.avgSig = 0; - Status.numPlanes = 0; - Status.numVisiblePlanes = 0; - Status.maxDist = 0; -} // //========================================================================= // -void view1090Init(void) { - - // pthread_mutex_init(&modes.pDF_mutex,NULL); - // pthread_mutex_init(&modes.data_mutex,NULL); - // pthread_cond_init(&modes.data_cond,NULL); - -#ifdef _WIN32 - if ( (!modes.wsaData.wVersion) - && (!modes.wsaData.wHighVersion) ) { - // Try to start the windows socket support - if (WSAStartup(MAKEWORD(2,1),&modes.wsaData) != 0) - { - fprintf(stderr, "WSAStartup returned Error\n"); - } - } -#endif - - // Allocate the various buffers used by Modes - if ( NULL == (modes.icao_cache = (uint32_t *) malloc(sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2))) - { - fprintf(stderr, "Out of memory allocating data buffer.\n"); - exit(1); - } - - // Clear the buffers that have just been allocated, just in-case - memset(modes.icao_cache, 0, sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2); - - // Validate the users Lat/Lon home location inputs - if ( (modes.fUserLat > 90.0) // Latitude must be -90 to +90 - || (modes.fUserLat < -90.0) // and - || (modes.fUserLon > 360.0) // Longitude must be -180 to +360 - || (modes.fUserLon < -180.0) ) { - modes.fUserLat = modes.fUserLon = 0.0; - } else if (modes.fUserLon > 180.0) { // If Longitude is +180 to +360, make it -180 to 0 - modes.fUserLon -= 360.0; - } - // If both Lat and Lon are 0.0 then the users location is either invalid/not-set, or (s)he's in the - // Atlantic ocean off the west coast of Africa. This is unlikely to be correct. - // Set the user LatLon valid flag only if either Lat or Lon are non zero. Note the Greenwich meridian - // is at 0.0 Lon,so we must check for either fLat or fLon being non zero not both. - // Testing the flag at runtime will be much quicker than ((fLon != 0.0) || (fLat != 0.0)) - modes.bUserFlags &= ~MODES_USER_LATLON_VALID; - if ((modes.fUserLat != 0.0) || (modes.fUserLon != 0.0)) { - modes.bUserFlags |= MODES_USER_LATLON_VALID; - } - - // Prepare error correction tables - modesInitErrorInfo(); -} // Set up data connection int setupConnection(struct client *c) { int fd; // Try to connect to the selected ip address and port. We only support *ONE* input connection which we initiate.here. - if ((fd = anetTcpConnect(modes.aneterr, View1090.net_input_beast_ipaddr, modes.net_input_beast_port)) != ANET_ERR) { - anetNonBlock(modes.aneterr, fd); + if ((fd = anetTcpConnect(AircraftData.modes.aneterr, View1090.net_input_beast_ipaddr, AircraftData.modes.net_input_beast_port)) != ANET_ERR) { + anetNonBlock(AircraftData.modes.aneterr, fd); // // Setup a service callback client structure for a beast binary input (from dump1090) // This is a bit dodgy under Windows. The fd parameter is a handle to the internet @@ -163,8 +73,8 @@ int setupConnection(struct client *c) { c->buflen = 0; c->fd = c->service = - modes.bis = fd; - modes.clients = c; + AircraftData.modes.bis = fd; + AircraftData.modes.clients = c; } return fd; } @@ -232,31 +142,33 @@ int main(int argc, char **argv) { struct client *c; char pk_buf[8]; - // Set sane defaults + AircraftData aircraftData; - view1090InitConfig(); signal(SIGINT, sigintHandler); // Define Ctrl/C handler (exit program) + + aircraftData.initialize(); + // Parse the command line options for (j = 1; j < argc; j++) { int more = ((j + 1) < argc); // There are more arguments if (!strcmp(argv[j],"--net-bo-port") && more) { - modes.net_input_beast_port = atoi(argv[++j]); + AircraftData.modes.net_input_beast_port = atoi(argv[++j]); } else if (!strcmp(argv[j],"--port") && more) { - modes.net_input_beast_port = atoi(argv[++j]); + AircraftData.modes.net_input_beast_port = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-bo-ipaddr") && more) { strcpy(View1090.net_input_beast_ipaddr, argv[++j]); } else if (!strcmp(argv[j],"--server") && more) { strcpy(View1090.net_input_beast_ipaddr, argv[++j]); } else if (!strcmp(argv[j],"--lat") && more) { - modes.fUserLat = atof(argv[++j]); - appData.centerLat = modes.fUserLat; + AircraftData.modes.fUserLat = atof(argv[++j]); + appData.centerLat = AircraftData.modes.fUserLat; } else if (!strcmp(argv[j],"--lon") && more) { - modes.fUserLon = atof(argv[++j]); - appData.centerLon = modes.fUserLon; + AircraftData.modes.fUserLon = atof(argv[++j]); + appData.centerLon = AircraftData.modes.fUserLon; } else if (!strcmp(argv[j],"--metric")) { - modes.metric = 1; + AircraftData.modes.metric = 1; } else if (!strcmp(argv[j],"--fullscreen")) { appData.fullscreen = 1; } else if (!strcmp(argv[j],"--uiscale") && more) { @@ -274,14 +186,11 @@ int main(int argc, char **argv) { } } - // Initialization - view1090Init(); - // Try to connect to the selected ip address and port. We only support *ONE* input connection which we initiate.here. c = (struct client *) malloc(sizeof(*c)); while(1) { if ((fd = setupConnection(c)) == ANET_ERR) { - fprintf(stderr, "Waiting on %s:%d\n", View1090.net_input_beast_ipaddr, modes.net_input_beast_port); + fprintf(stderr, "Waiting on %s:%d\n", View1090.net_input_beast_ipaddr, AircraftData.modes.net_input_beast_port); sleep(1); } else { break;