{"id":4219,"date":"2022-12-22T12:00:45","date_gmt":"2022-12-22T20:00:45","guid":{"rendered":"https:\/\/www.cloudacm.com\/?p=4219"},"modified":"2025-03-09T09:47:31","modified_gmt":"2025-03-09T16:47:31","slug":"gps-mapping-with-viking-using-esp-now-and-gpsd","status":"publish","type":"post","link":"https:\/\/www.cloudacm.com\/?p=4219","title":{"rendered":"GPS Mapping with Viking using ESP-Now and GPSd"},"content":{"rendered":"<p>The last post (<a href=\"https:\/\/www.cloudacm.com\/?p=4185\">https:\/\/www.cloudacm.com\/?p=4185<\/a>) ended with a video titled &#8220;Decoding GPS using an RTL SDR Receiver&#8221; that was posted August of 2020. The 2 software requirements for this method are RTKLIB (<a href=\"https:\/\/www.rtklib.com\/rtklib_tutorial.htm\">https:\/\/www.rtklib.com\/rtklib_tutorial.htm<\/a>) and GNSS-SDRLIB (<a href=\"https:\/\/github.com\/taroz\/GNSS-SDRLIB\">https:\/\/github.com\/taroz\/GNSS-SDRLIB<\/a>). The video mentioned that Google Maps didn&#8217;t display, however there was mention in comments about an API requirement. The video did introduce the option to port the output to another mapping program, one of which is Viking.<\/p>\n<p>On a side note, I found this video about Viking navigation to be both fascinating and enlightening. It stands to reason the heritage of the Viking explorers inspired the name of the mapping software covered in this post.<\/p>\n<p><iframe loading=\"lazy\" title=\"The secrets of Viking navigation - An interview with a captain of a Viking ship replica\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/YzDvSY7xf9I?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>The mapping software mentioned earlier called Viking can be ran on Linux or Windows. However, real-time GPS positioning is only available in Linux at the time of this writing. More details can be found here, <a href=\"https:\/\/sourceforge.net\/projects\/viking\/\">https:\/\/sourceforge.net\/projects\/viking\/<\/a> as well as here, <a href=\"https:\/\/wiki.openstreetmap.org\/wiki\/Viking\">https:\/\/wiki.openstreetmap.org\/wiki\/Viking<\/a>. The manual can be downloaded here, <a href=\"https:\/\/sourceforge.net\/projects\/viking\/files\/viking.pdf\">https:\/\/sourceforge.net\/projects\/viking\/files\/viking.pdf<\/a><\/p>\n<p>Viking uses layers, similar to GIMP or Photoshop. Each layer contains a specific type of information. The fundamental layer to<br \/>\nintroduce here is the map layer. Viking has several map types to choose from, I have found OpenStreetMap Mapnik the most<br \/>\nsuitable for my needs, see <a href=\"https:\/\/mapnik.org\/\">https:\/\/mapnik.org\/<\/a> for more details. Open Viking then Add new layer, Map &gt; Map Type OpenStreetMap (Mapnik) at 129 alpha. I like to have the layer semitransparent before I finalize the display. Viking can display GPS data in its raw NMEA format, either from file or real-time. This is extremely useful when gathering GPS data from simple devices, such as the Goouuu Tech GT-U7 ublox GPS module. Here is a video demonstration of the Viking interface.<\/p>\n<p><iframe loading=\"lazy\" title=\"Viking Interface Demo - Real-time GPS\" src=\"https:\/\/player.vimeo.com\/video\/783342066?dnt=1&amp;app_id=122963\" width=\"640\" height=\"537\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media\"><\/iframe><\/p>\n<p>The Goouuu Tech GT-U7 ublox GPS module can be purchased for less than $15. The datasheet can be found here, <a href=\"https:\/\/manuals.plus\/goouuu\/goouuu-tech-gt-u7-gps-modules.pdf\">https:\/\/manuals.plus\/goouuu\/goouuu-tech-gt-u7-gps-modules.pdf<\/a>. Here is a video demonstrating its use with an Arduino.<\/p>\n<p><iframe loading=\"lazy\" title=\"How to use the GT U7 GPS module\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/7zw2ULu73DY?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>The video above demonstrates the processed results from the GT-U7 using the TinyGPS++ library. Natively, the module outputs NMEA sentences, a sanitized example is below. Information about NMEA sentences can be found here, <a href=\"https:\/\/gpsd.gitlab.io\/gpsd\/NMEA.html\">https:\/\/gpsd.gitlab.io\/gpsd\/NMEA.html<\/a><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">$GPRMC,......00,A,.....,1.313,,.....,,,A*67\r\n$GPVTG,,T,,M,1.313,N,2.433,K,A*25\r\n$GPGGA,.......00,......,1,03,4.00,58.0,M,-18.8,M,,*5D\r\n$GPGSA,A,2,14,30,17,,,,,,,,,,4.12,4.00,1.00*01\r\n$GPGSV,3,1,09,05,03,258,,08,,,23,13,53,285,,14,84,294,25*48\r\n$GPGSV,3,2,09,15,27,307,22,17,39,173,20,19,,,14,21,18,072,25*4A\r\n$GPGSV,3,3,09,30,71,113,27*43\r\n$GPGLL,......,.......00,A,A*7E<\/pre>\n<p>Viewing real-time NMEA source data in Viking will require GPSd. This is because Viking does not support native NMEA sources through a serial connection. The NMEA sources are only supported through a network pipe, which GPSd provides. Details about GPSd can be found here, <a href=\"https:\/\/gpsd.io\/\">https:\/\/gpsd.io\/<\/a><\/p>\n<p>This video provides install and config steps to get GPSd running.<\/p>\n<p><iframe loading=\"lazy\" title=\"Mobile Wireless Recon: Raspberry Pi v3 GPSd Install &amp; Config - WiFi Hacking Series #10\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/82z5JNsir6Y?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>It may not be suitable to have the GT-U7 module directly attached to a Linux host due to environmental limits. Capturing GPS data can be done with a ESP32-Cam module. The serial stream is monitored by the ESP32-Cam module and any inbound serial data is then written to the microSD media. This code can be used to do just that.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\/** \r\n\r\nSerialStreamToMicroSD_Ver1.ino\r\nCaptures serial stream on inbound serial pin and stores on micro-SD\r\nJuly 24th, 2022 - 18:00\r\n\r\nThanks\r\nhttps:\/\/stackoverflow.com\/questions\/38278755\/write-from-serial-port-to-sd-card\r\nhttps:\/\/stackoverflow.com\/questions\/5697047\/convert-serial-read-into-a-useable-string-using-arduino\r\n\r\nESP32-CAM connections required for standalone mode\r\nVCC = 5V DC\r\nGND = Ground\r\nRXD = TXD of GPS module\r\n\r\nOptional connection when using serial output \r\nTXD = RXD of FTDI USB module\r\n\r\n**\/\r\n\r\n\r\n\/\/ Libraries\r\n#include \"Arduino.h\"\r\n#include \"FS.h\"     \/\/ SD Card ESP32\r\n#include \"SD_MMC.h\"     \/\/ SD Card ESP32\r\n\r\n\/\/ ESP32-CAM GPIOs\r\n#define RXD 3\r\n#define TXD 0     \/\/ Not used when not connected to serial output\r\n\r\n\r\n\/\/ Variables and Constants\r\n\r\nString inData;\r\nconst char* GPSReadings = \"\/GPS_NMEA_Data.txt\";\r\n\r\n\/\/ Routines and Subroutines\r\n\r\n\r\n\/\/ Hardware Initialization Routine\r\nvoid HardwareInit()\r\n{\r\n  \/\/ Note the format for setting a serial port is as follows: \r\n  \/\/ Serial.begin(baud-rate, protocol, RX pin, TX pin);\r\n  \/\/ initialize serial:\r\n  Serial.begin(9600, SERIAL_8N1, RXD, TXD);\r\n  delay(250);  \/\/ Just a little delay \r\n} \r\n\r\n    \r\nvoid StartupMicrSD()\r\n{\r\n   if(!SD_MMC.begin()){\r\n        return;\r\n    }\r\n    uint8_t cardType = SD_MMC.cardType();\r\n\r\n    if(cardType == CARD_NONE){\r\n        return;\r\n    }\r\n\r\n}  \r\n\r\n\/\/Append to the end of file in SD card\r\nvoid appendFile(fs::FS &amp;fs, const char * path, const char * message){\r\n\r\n    File = fs.open(path, FILE_APPEND);\r\n    if(!file){\r\n        return;\r\n    }\r\n    if(file.print(message)){\r\n    } else {\r\n    }\r\n}\r\n\r\n\r\n\/\/ Inbound Serial Data Processing\r\nvoid InboundSerialData()\r\n{\r\n    if(Serial.available())                                   \/\/ if there is data coming\r\n  {\r\n    String inputString = Serial.readStringUntil('\\n');         \/\/ read string until meet newline character  \r\n    Serial.println(inputString);                  \/\/ send action to Serial Monitor\r\n    appendFile(SD_MMC, GPSReadings, inputString.c_str()); \r\n    appendFile(SD_MMC, GPSReadings, \"\\n\");\r\n  }\r\n}\r\n\r\n\r\nvoid setup()\r\n{\r\n  HardwareInit();  \r\n  StartupMicrSD();\r\n}\r\n\r\n\r\nvoid loop()\r\n{\r\n  InboundSerialData();\r\n}<\/pre>\n<p>Although this method will capture the GPS stream, real-time processing is not available. One solution to this is to link two ESP32-CAM modules together using a proprietary protocol that is native to Espressif ESP modules. This protocol is called ESP-Now. Essentially, ESP-Now will provide a tethered connection from the GT-U7 module to the Linux computer running GPSd and Viking using the 2 ESP32-CAM modules. The ESP32-Cam module that has the GT-U7 module will be called the Sender. The ESP32-Cam module that is attached to the Linux host will be called the Receiver. The Linux computer will for all intents and purposes appear to have the GT-U7 directly attached. The manual for ESP-Now can be found here, <a href=\"https:\/\/www.espressif.com\/sites\/default\/files\/documentation\/esp-now_user_guide_en.pdf\">https:\/\/www.espressif.com\/sites\/default\/files\/documentation\/esp-now_user_guide_en.pdf<\/a>. This video provides an intro and use case for ESP-Now.<\/p>\n<p><iframe loading=\"lazy\" title=\"ESP-NOW with ESP32 EXPLAINED: Easiest Wireless Communication Between Boards (ESP8266 Compatible)\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/qxwXwNS3Avw?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>Here is the code base used for the Sender.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\/*\r\n  Rui Santos\r\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp-now-esp32-arduino-ide\/\r\n  \r\n  Permission is hereby granted, free of charge, to any person obtaining a copy\r\n  of this software and associated documentation files.\r\n  \r\n  The above copyright notice and this permission notice shall be included in all\r\n  copies or substantial portions of the Software.\r\n\r\n  Info on encryption \r\n  https:\/\/www.electrosoftcloud.com\/en\/security-on-your-esp32-with-esp-now\/\r\n\r\n  This might be a better approach.\r\n  https:\/\/www.survivingwithandroid.com\/esp-now-esp32-esp8266\/\r\n  Reason - this code base uses the library extensively and much of the inner workings\r\n  are hidden\r\n*\/\r\n\r\n\/\/ Declarations and Variables\r\n\r\n#include \"Arduino.h\"\r\n#include \"FS.h\"                \/\/ SD Card ESP32\r\n#include \"SD_MMC.h\"            \/\/ SD Card ESP32\r\nconst char* GPSReadings = \"\/GPS_Sender.txt\";\r\n\r\n#include &lt;esp_now.h&gt;\r\n#include &lt;WiFi.h&gt;\r\n\/\/ ESP32-CAM GPIOs\r\n#define RXD 3\r\n#define TXD 0\r\n\r\n\/\/ REPLACE WITH YOUR RECEIVER MAC Address\r\n\/\/ Receiver\r\n\/\/ BOARD1\r\n\/\/ R labelled Board - Connected to Laptop for Receiving GPS ESP-Now Messages\r\n\/\/ ESP Board MAC Address:  FF:FF:FF:FF:FF:FF\r\n\/\/ {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}\r\nuint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};\r\n\r\n\/\/ Structure example to send data\r\n\/\/ Must match the receiver structure\r\ntypedef struct struct_message {\r\n  char GpsData[128];\r\n} struct_message;\r\n\r\n\/\/ Create a struct_message called myData\r\nstruct_message myData;\r\n\r\nesp_now_peer_info_t peerInfo;\r\n\r\n\/\/ callback when data is sent\r\nvoid OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {\r\n}\r\n \r\n\r\nvoid HardwareSetup()\r\n{\r\n  \/\/ Note the format for setting a serial port is as follows: \r\n  \/\/ Serial.begin(baud-rate, protocol, RX pin, TX pin);\r\n  \/\/ initialize serial:\r\n  Serial.begin(9600, SERIAL_8N1, RXD, TXD);\r\n \r\n  \/\/ Set device as a Wi-Fi Station\r\n  WiFi.mode(WIFI_STA);\r\n\r\n  \/\/ Init ESP-NOW\r\n  if (esp_now_init() != ESP_OK) {\r\n    Serial.println(\"Falied initializing ESP-NOW\");\r\n    return;\r\n  }\r\n\r\n  \/\/ Once ESPNow is successfully Init, we will register for Send CB to\r\n  \/\/ get the status of Transmitted packet\r\n  esp_now_register_send_cb(OnDataSent);\r\n  \r\n  \/\/ Register peer\r\n  memcpy(peerInfo.peer_addr, broadcastAddress, 6);\r\n  peerInfo.channel = 0;  \r\n  peerInfo.encrypt = false;\r\n  \r\n  \/\/ Add peer        \r\n  if (esp_now_add_peer(&amp;peerInfo) != ESP_OK){\r\n    Serial.println(\"Falied to add peer\");\r\n    return;\r\n  }\r\n}\r\n \r\nvoid StartupMicrSD()\r\n{\r\n   if(!SD_MMC.begin()){\r\n        return;\r\n    }\r\n    uint8_t cardType = SD_MMC.cardType();\r\n\r\n    if(cardType == CARD_NONE){\r\n        return;\r\n    }\r\n\r\n}\r\n\r\n\r\n\r\n\/\/Append to the end of file in SD card\r\nvoid appendFile(fs::FS &amp;fs, const char * path, const char * message){\r\n\r\n    File = fs.open(path, FILE_APPEND);\r\n    if(!file){\r\n        return;\r\n    }\r\n    if(file.print(message)){\r\n    } else {\r\n    }\r\n}\r\n\r\n\r\nvoid setup() {\r\nStartupMicrSD();\r\nHardwareSetup();\r\n} \r\n \r\nvoid loop() {\r\n  \r\n    if(Serial.available())                                   \/\/ if there is data coming\r\n  {\r\n    String inputString = Serial.readStringUntil('\\n');         \/\/ read string until meet newline character  \r\n    Serial.println(inputString);                  \/\/ send action to Serial Monitor\r\n    appendFile(SD_MMC, GPSReadings, inputString.c_str()); \r\n    appendFile(SD_MMC, GPSReadings, \"\\n\");\r\n    inputString.toCharArray(myData.GpsData, 128);\r\n    esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &amp;myData, sizeof(myData));\r\n    \r\n  }\r\n}\r\n\r\n\/*\r\n  Some Note\r\n*\/<\/pre>\n<p>Here is the code base used for the Receiver.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\/*\r\n  Rui Santos\r\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp-now-esp32-arduino-ide\/\r\n  \r\n  Permission is hereby granted, free of charge, to any person obtaining a copy\r\n  of this software and associated documentation files.\r\n  \r\n  The above copyright notice and this permission notice shall be included in all\r\n  copies or substantial portions of the Software.\r\n*\/\r\n\r\n\/\/ Declarations and Variables\r\n\r\n#include \"Arduino.h\"\r\n#include \"FS.h\"                \/\/ SD Card ESP32\r\n#include \"SD_MMC.h\"            \/\/ SD Card ESP32\r\nconst char* GPSReadings = \"\/GPS_Receiver.txt\";\r\n\r\n#include &lt;esp_now.h&gt;\r\n#include &lt;WiFi.h&gt;\r\n\/\/ ESP32-CAM GPIOs\r\n#define RXD 3\r\n#define TXD 0\r\nString inputString = \"\";\r\n\r\n\/\/ Structure example to receive data\r\n\/\/ Must match the sender structure\r\ntypedef struct struct_message {\r\n    char GpsData[128];\r\n} struct_message;\r\n\r\n\/\/ Create a struct_message called myData\r\nstruct_message myData;\r\n\r\n\/\/ callback function that will be executed when data is received\r\nvoid OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {\r\n  memcpy(&amp;myData, incomingData, sizeof(myData));\r\n  String inputString = String(myData.GpsData);\r\n  Serial.println(inputString);\r\n  appendFile(SD_MMC, GPSReadings, inputString.c_str()); \r\n  appendFile(SD_MMC, GPSReadings, \"\\n\");\r\n}\r\n\r\nvoid HardwareSetup()\r\n{\r\n  \/\/ Note the format for setting a serial port is as follows: \r\n  \/\/ Serial.begin(baud-rate, protocol, RX pin, TX pin);\r\n  \/\/ initialize serial:\r\n  Serial.begin(9600, SERIAL_8N1, RXD, TXD);\r\n  \r\n  \/\/ Set device as a Wi-Fi Station\r\n  WiFi.mode(WIFI_STA);\r\n\r\n  \/\/ Init ESP-NOW\r\n  if (esp_now_init() != ESP_OK) {\r\n    Serial.println(\"Falied initializing ESP-NOW\");\r\n    return;\r\n  }\r\n  \r\n  \/\/ Once ESPNow is successfully Init, we will register for recv CB to\r\n  \/\/ get recv packer info\r\n  esp_now_register_recv_cb(OnDataRecv);\r\n}\r\n\r\n\r\nvoid StartupMicrSD()\r\n{\r\n   if(!SD_MMC.begin()){\r\n        return;\r\n    }\r\n    uint8_t cardType = SD_MMC.cardType();\r\n\r\n    if(cardType == CARD_NONE){\r\n        return;\r\n    }\r\n\r\n}\r\n\r\n\r\n\/\/Append to the end of file in SD card\r\nvoid appendFile(fs::FS &amp;fs, const char * path, const char * message){\r\n\r\n    File = fs.open(path, FILE_APPEND);\r\n    if(!file){\r\n        return;\r\n    }\r\n    if(file.print(message)){\r\n    } else {\r\n    }\r\n}\r\n\r\n \r\nvoid setup() {\r\nStartupMicrSD();\r\nHardwareSetup();\r\n}\r\n \r\nvoid loop() {\r\n\r\n}<\/pre>\n<p>Initially there were some issues when using the ESP32-Cam USB programing board. The correct wiring should be limited to Ground, 5V, RXD, and TXD between the ESP32-Cam module and the USB programing board. Under normal operation, when both the Sender and Receiver are powered on and in range of each other, the GPS stream should be captured on both module&#8217;s microSD cards. With the Receiver attached to the Linux host, GPSd can be configured to use the Receiver as its GPS NMEA source. Viking can now be configured to connect to GPSd for live plotting. To do so, following these steps.<\/p>\n<p>Open Viking<\/p>\n<p>Add all new layers to Top Layer<\/p>\n<p>Add new layer, GPS &gt; Realtime Tracking Mode tab<br \/>\nCheck the following items:<br \/>\nRecording tracks<br \/>\nJump to current position on start<br \/>\nMoving Map Method &#8211; Keep vehicle on screen<br \/>\nUpdate Statusbar<br \/>\nAuto Connect<br \/>\nGpsd Host: localhost<br \/>\nGpsd Port: 2947<br \/>\nGpsd Retry Interval (seconds) 10<\/p>\n<p>Add new layer, Map &gt; Map Type OpenStreetMap (Mapnik) at 128 alpha<\/p>\n<p><iframe loading=\"lazy\" title=\"Viking Interface Demo - Real-time GPS\" src=\"https:\/\/player.vimeo.com\/video\/783342066?dnt=1&amp;app_id=122963\" width=\"640\" height=\"537\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media\"><\/iframe><\/p>\n<p>The GPS stream can also be validated with GPSMon. GPSMon is part of GPSd and can be ran from the command line on the Linux host. Here is a video of the GPSd config and GPSMon interface.<\/p>\n<p><iframe loading=\"lazy\" title=\"GPSd and GPSMon - Interface and Config\" src=\"https:\/\/player.vimeo.com\/video\/783347433?dnt=1&amp;app_id=122963\" width=\"640\" height=\"316\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media\"><\/iframe><\/p>\n<p>The GSV section is interesting because it contains $GPGSV sentences which are GPS satellite data location and SN ratio levels. Here is an example of that window.<\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/12\/GPS_GSV_Example1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-4225\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/12\/GPS_GSV_Example1.png\" alt=\"\" width=\"179\" height=\"269\" \/><\/a><\/p>\n<p>These values can be plotted with Python using MatPlot and NumPy to create a polar projection of the satellite location and signal strength. Here is an example.<\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/12\/GPS_SatPlot_Demo_Scatter_Test1b.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-4227\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/12\/GPS_SatPlot_Demo_Scatter_Test1b.png\" alt=\"\" width=\"640\" height=\"480\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/12\/GPS_SatPlot_Demo_Scatter_Test1b.png 640w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/12\/GPS_SatPlot_Demo_Scatter_Test1b-300x225.png 300w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2022\/12\/GPS_SatPlot_Demo_Scatter_Test1b-360x270.png 360w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/a><\/p>\n<p>Here is the python code used to create the plot above.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">\"\"\"\r\nDemo of scatter plot on a polar axis.\r\n\r\nSize increases radially in this example and color increases with angle (just to\r\nverify the symbols are being scattered correctly).\r\n\"\"\"\r\nimport numpy as np\r\nimport matplotlib.pyplot as plt\r\n\r\nAzimuth = [0, 283, 24, 349, 44, 126, 306, 197, 68, 69, 233, 183, 185]\r\nElevation = [0, 41, 78, 0, 0, 20, 21, 24, 46, 23, 44, 53, 39]\r\nSignalNoise = [0, 31, 33, 0, 0, 17, 27, 16, 41, 23, 26, 32, 26]\r\n\r\nElevation = (np.array(Elevation))\r\nAzimuth = np.radians(np.array(Azimuth))\r\nSignalNoise = np.array(SignalNoise)\r\ncolors = SignalNoise\r\n\r\nplt.figure(facecolor='#0f0f0f') # Set background color of the outer\r\ncolor = '#555555'\r\nplt.rcParams['text.color'] = color\r\nplt.rcParams['axes.labelcolor'] = color\r\nplt.rcParams['xtick.color'] = color\r\nplt.rcParams['ytick.color'] = color\r\n\r\nax = plt.subplot(111, polar=True) # Set as polar plot\r\nax.set_ylim(90,0) # Set range of Elevation and direction of scale\r\nax.set_theta_zero_location('N') # Set Azimuth 0 to top of plot\r\nax.set_theta_direction(-1) # Set Azimuth direction of N, E, S, W in clockwise direction\r\nc = plt.scatter(Azimuth, Elevation, c=colors, s=SignalNoise, cmap=plt.cm.hot) # Set polar plot type to scatter and set color\r\nc.set_alpha(0.85)\r\nax.set_facecolor('#0b0b0b')\r\ncbar = plt.colorbar()\r\n\r\n# plt.show()\r\nplt.savefig(\"GPS_SatPlot_Demo_Scatter_Test1b.png\", facecolor='#0f0f0f')<\/pre>\n<p>The plots can be combined with FFMpeg to create a video that reveals the motion of the satellites over time, along changes in signal strength. Another python tool available is gpsclient, available here <a href=\"https:\/\/pypi.org\/project\/gpsdclient\/\">https:\/\/pypi.org\/project\/gpsdclient\/<\/a>. I won&#8217;t cover this here since it is more a side note.<\/p>\n<p>Another program that runs on Linux that has been discussed in earlier posts is GPredict. This program provides an approximate view of where satellites are located. More details about the program can be found here, <a href=\"https:\/\/sourceforge.net\/projects\/gpredict\/files\/Gpredict\/1.3\/gpredict-user-manual-1.3.pdf\">https:\/\/sourceforge.net\/projects\/gpredict\/files\/Gpredict\/1.3\/gpredict-user-manual-1.3.pdf<\/a>. The following video was created to demonstrate how GPredict plots satellite motion over time. These are the FFMpeg screen capture commands used for the video below.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"bash\">ffmpeg -video_size 1920x1080 -framerate .033 -f x11grab -i :0.0+1920,0 GPredict_1.mp4\r\nffmpeg -i GPredict_1.mp4 -r 15 -filter:v \"setpts=0.03*PTS\" GPredict_2.mp4\r\nffmpeg -i GPredict_2.mp4 -r 15 -filter:v \"setpts=0.03*PTS\" GPredict_3.mp4<\/pre>\n<p><iframe loading=\"lazy\" title=\"GPredict GPS Timelapse\" src=\"https:\/\/player.vimeo.com\/video\/783329160?dnt=1&amp;app_id=122963\" width=\"640\" height=\"360\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media\"><\/iframe><\/p>\n<p>Here is a video demonstrating some basic configurations of GPredict and viewing options. I used FFMpeg to trim the length and size from an original desktop capture with these commands.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"bash\">ffmpeg -video_size 1920x1080 -framerate 15 -f x11grab -i :0.0+1920,0 GPredict_Demo_15FPS.mp4\r\nffmpeg -ss 00:00:13 -to 00:05:22 -i GPredict_Demo_15FPS.mp4 -filter:v \"crop=1391:893:140:123\" GPredict_Demo1_15FPS.mp4<\/pre>\n<p><iframe loading=\"lazy\" title=\"GPredict Interface - GPS Cluster\" src=\"https:\/\/player.vimeo.com\/video\/783335025?dnt=1&amp;app_id=122963\" width=\"640\" height=\"411\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media\"><\/iframe><\/p>\n<p>This video demonstrates using SDR and Viking to capture and plot Weather balloon Radiosonde GPS data.<\/p>\n<p><iframe loading=\"lazy\" title=\"Radiosonde \/ Viking GPS\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/MAODAyWTn0c?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>The demonstration above used these process commands below, with some description of each. However, I wasn&#8217;t able to source all of them, so those details are lacking. It would take more research to replicate, which is outside the scope of this post.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">socat -d -d pty,raw,echo=0 pty,raw,b4800,echo=0\r\n\/\/    Socat is a flexible, multi-purpose relay tool. Its purpose is to establish a relationship between two data sources, where each data source can be a file, a Unix socket, UDP, TCP, or standard input.\r\n\r\ngpsd -D2 -b -n -N \/dev\/pts\/19\r\ngpspipe -r localhost:2947\r\nsox -t oss \/dev\/dsp -t wav - lowpass 2400 highpass 20 2&gt;\/dev\/null | .\/dfm06 -i --ecc -vv | .\/pos2nmea.pl &gt; \/dev\/pts\/11<\/pre>\n<p>pos2nmea.pl &#8211; <a href=\"https:\/\/github.com\/happysat\/RS-Decoder-for-Radiosondes-in-Ubuntu-Linux\">https:\/\/github.com\/happysat\/RS-Decoder-for-Radiosondes-in-Ubuntu-Linux<\/a> (the dreaded 404 not found message)<br \/>\ndfm06 &#8211; No github page<br \/>\ngpspipe &#8211; <a href=\"https:\/\/gpsd.io\/gpspipe.html\">https:\/\/gpsd.io\/gpspipe.html<\/a>, part of GPSd<br \/>\nsox &#8211; <a href=\"https:\/\/sox.sourceforge.net\/soxformat.html\">https:\/\/sox.sourceforge.net\/soxformat.html<\/a><\/p>\n<p>Here is yet another example of inexpensive hardware leveraging open-source software and public standards to give the learned a richer view of the world. We all have a nonprofit association composed of manufacturers, distributors, dealers, educational institutions, and others interested in peripheral marine electronics occupations to thank for the basis of commercial GPS, which is NMEA (The National Marine and Electronics Association). Here is a link to that organization\u2019s website, <a href=\"https:\/\/www.nmea.org\/\">https:\/\/www.nmea.org\/<\/a><\/p>\n<p>Another public standard worth mentioning is ADS-B (Automatic Dependent Surveillance\u2013Broadcast). Here is a video that introduces the basics of its use.<\/p>\n<p><iframe loading=\"lazy\" title=\"How Does ADS-B Work?\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/F-v54MlxMIo?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The last post (https:\/\/www.cloudacm.com\/?p=4185) ended with a video titled &#8220;Decoding GPS using an RTL SDR Receiver&#8221; that was posted August of 2020. The 2 software requirements for this method are RTKLIB (https:\/\/www.rtklib.com\/rtklib_tutorial.htm) and GNSS-SDRLIB (https:\/\/github.com\/taroz\/GNSS-SDRLIB). The video mentioned that Google Maps didn&#8217;t display, however there was mention in comments about an API requirement. The video did introduce the option to port the output to another mapping program, one of which is Viking. On a side note, I found this video&#8230;<\/p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/www.cloudacm.com\/?p=4219\"> Read More<span class=\"screen-reader-text\">  Read More<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[],"class_list":["post-4219","post","type-post","status-publish","format-standard","hentry","category-esp32-cam"],"_links":{"self":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/4219","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4219"}],"version-history":[{"count":9,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/4219\/revisions"}],"predecessor-version":[{"id":4830,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/4219\/revisions\/4830"}],"wp:attachment":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4219"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4219"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4219"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}