From 43570b63de90dacd2c5fc6500a538617fdb406ff Mon Sep 17 00:00:00 2001
From: Benjamin Vernoux <bvernoux@gmail.com>
Date: Fri, 1 Mar 2024 00:10:03 +0100
Subject: [PATCH 22/29] Add new tool airspy_calibrate fix issue
 https://github.com/airspy/airspyone_host/issues/96 Usage:  -r: Read and
 display calibration data.  -w <calibration in ppb>: Erase and Write
 calibration in ppb.

---
 airspy-tools/src/CMakeLists.txt            |   6 +-
 airspy-tools/src/airspy_calibrate.c        | 161 +++++++++++++++++++
 libairspy/vc/airspy_2019.sln               |  10 ++
 libairspy/vc/airspy_calibrate_2013.vcxproj | 177 +++++++++++++++++++++
 4 files changed, 353 insertions(+), 1 deletion(-)
 create mode 100644 airspy-tools/src/airspy_calibrate.c
 create mode 100644 libairspy/vc/airspy_calibrate_2013.vcxproj

diff --git a/airspy-tools/src/CMakeLists.txt b/airspy-tools/src/CMakeLists.txt
index 370472c..c674edd 100644
--- a/airspy-tools/src/CMakeLists.txt
+++ b/airspy-tools/src/CMakeLists.txt
@@ -1,5 +1,5 @@
 # Copyright 2012 Jared Boone
-# Copyright 2013/2014 Benjamin Vernoux
+# Copyright 2013/2014/2024 Benjamin Vernoux
 #
 # This file is part of AirSpy (based on HackRF project).
 #
@@ -47,6 +47,9 @@ install(TARGETS airspy_r820t RUNTIME DESTINATION ${INSTALL_DEFAULT_BINDIR})
 add_executable(airspy_spiflash airspy_spiflash.c)
 install(TARGETS airspy_spiflash RUNTIME DESTINATION ${INSTALL_DEFAULT_BINDIR})
 
+add_executable(airspy_calibrate airspy_calibrate.c)
+install(TARGETS airspy_calibrate RUNTIME DESTINATION ${INSTALL_DEFAULT_BINDIR})
+
 add_executable(airspy_info airspy_info.c)
 install(TARGETS airspy_info RUNTIME DESTINATION ${INSTALL_DEFAULT_BINDIR})
 
@@ -70,5 +73,6 @@ target_link_libraries(airspy_lib_version ${TOOLS_LINK_LIBS})
 target_link_libraries(airspy_si5351c ${TOOLS_LINK_LIBS})
 target_link_libraries(airspy_r820t ${TOOLS_LINK_LIBS})
 target_link_libraries(airspy_spiflash ${TOOLS_LINK_LIBS})
+target_link_libraries(airspy_calibrate ${TOOLS_LINK_LIBS})
 target_link_libraries(airspy_info ${TOOLS_LINK_LIBS})
 target_link_libraries(airspy_rx ${TOOLS_LINK_LIBS})
diff --git a/airspy-tools/src/airspy_calibrate.c b/airspy-tools/src/airspy_calibrate.c
new file mode 100644
index 0000000..c28f1a0
--- /dev/null
+++ b/airspy-tools/src/airspy_calibrate.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2024 Benjamin Vernoux <bvernoux@gmail.com>
+ *
+ * This file is part of AirSpy.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#include <airspy.h>
+ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <time.h>
+
+#ifndef bool
+typedef int bool;
+#define true 1
+#define false 0
+#endif
+
+#define AIRSPY_FLASH_CALIB_OFFSET (0x20000) /* After 128KB (Reserved for Firmware + 64KB Spare) */
+#define AIRSPY_FLASH_CALIB_HEADER (0xCA1B0001)
+
+typedef struct
+{
+	uint32_t header; /* Shall be equal to AIRSPY_FLASH_CALIB_HEADER */
+	uint32_t timestamp; /* Epoch Unix Time Stamp */
+	int32_t correction_ppb;
+} airspy_calib_t;
+
+static void usage()
+{
+	printf("Usage:\n");
+	printf("\t-r: Read and display calibration data.\n");
+	printf("\t-w <calibration in ppb>: Erase and Write calibration in ppb.\n");
+}
+
+int main(int argc, char **argv)
+{
+	int opt;
+	struct airspy_device* device = NULL;
+	int result;
+	bool read = false;
+	bool write = false;
+	int32_t calibration_ppb = 0;
+	airspy_calib_t calib;
+
+	while ((opt = getopt(argc, argv, "rw:")) != EOF)
+	{
+		switch (opt) {
+		case 'r':
+			read = true;
+			break;
+
+		case 'w':
+			write = true;
+			calibration_ppb = atoi(optarg);
+			break;
+
+		default:
+			fprintf(stderr, "opt error: %d\n", opt);
+			usage();
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (write == read) {
+		if (write == true) {
+			fprintf(stderr, "Read and write options are mutually exclusive.\n");
+		} else {
+			fprintf(stderr, "Specify either read or write option.\n");
+		}
+		usage();
+		return EXIT_FAILURE;
+	}
+
+	result = airspy_init();
+	if (result != AIRSPY_SUCCESS) {
+		fprintf(stderr, "airspy_init() failed: %s (%d)\n", airspy_error_name(result), result);
+		return EXIT_FAILURE;
+	}
+
+	result = airspy_open(&device);
+	if (result != AIRSPY_SUCCESS) {
+		fprintf(stderr, "Failed to open airspy device.\n");
+		return 1;
+	}
+
+	if (read) {
+		printf("Reading %d bytes from 0x%06x.\n", (int)sizeof(calib), AIRSPY_FLASH_CALIB_OFFSET);
+		result = airspy_spiflash_read(device, AIRSPY_FLASH_CALIB_OFFSET, (int)sizeof(calib), (uint8_t *)&calib);
+		if (result != AIRSPY_SUCCESS) {
+			fprintf(stderr, "airspy_spiflash_read() failed: %s (%d)\n", airspy_error_name(result), result);
+			return EXIT_FAILURE;
+		}
+		time_t epoch_time = calib.timestamp;
+		struct tm *local_time = localtime(&epoch_time);
+		printf("Calibration timestamp: %04d/%02d/%02d %02d:%02d:%02d\nCalibration correction in ppb: %d\n", 
+				local_time->tm_year + 1900,
+				local_time->tm_mon + 1,
+				local_time->tm_mday,
+				local_time->tm_hour,
+				local_time->tm_min,
+				local_time->tm_sec,
+				calib.correction_ppb);
+	}
+	if(write) {
+		printf("Erasing sector 2 (calibration) in SPI flash.\n");
+		result = airspy_spiflash_erase_sector(device, 2);
+		if (result != AIRSPY_SUCCESS) {
+			fprintf(stderr, "Failed to erase sector 2.\n");
+			return 1;
+		}
+
+		calib.header = AIRSPY_FLASH_CALIB_HEADER;
+		calib.timestamp = (uint32_t)time(NULL);
+		calib.correction_ppb = calibration_ppb;
+
+		printf("Writing calibration %d bytes at 0x%06x.\n", (int)sizeof(calib), AIRSPY_FLASH_CALIB_OFFSET);
+		time_t epoch_time = calib.timestamp;
+		struct tm *local_time = localtime(&epoch_time);
+		printf("Calibration timestamp: %04d/%02d/%02d %02d:%02d:%02d\nCalibration correction in ppb: %d\n", 
+				local_time->tm_year + 1900,
+				local_time->tm_mon + 1,
+				local_time->tm_mday,
+				local_time->tm_hour,
+				local_time->tm_min,
+				local_time->tm_sec,
+				calib.correction_ppb);
+		result = airspy_spiflash_write(device, AIRSPY_FLASH_CALIB_OFFSET, sizeof(calib), (uint8_t *)&calib);
+		if (result != AIRSPY_SUCCESS) {
+			fprintf(stderr, "Failed to write calibration data.\n");
+			return 1;
+		}
+	}
+
+	result = airspy_close(device);
+	if (result != AIRSPY_SUCCESS) {
+		fprintf(stderr, "airspy_close() failed: %s (%d)\n", airspy_error_name(result), result);
+		return EXIT_FAILURE;
+	}
+
+	airspy_exit();
+
+	return EXIT_SUCCESS;
+}
+
diff --git a/libairspy/vc/airspy_2019.sln b/libairspy/vc/airspy_2019.sln
index 14a2a2b..d1d1c1a 100644
--- a/libairspy/vc/airspy_2019.sln
+++ b/libairspy/vc/airspy_2019.sln
@@ -22,6 +22,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "airspy_si5351c", "airspy_si
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "airspy_spiflash", "airspy_spiflash_2013.vcxproj", "{47846DAA-BB23-4E49-9E90-31CCC9AB01A8}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "airspy_calibrate", "airspy_calibrate_2013.vcxproj", "{EF14D067-ED6A-43C9-A36B-76CA92741A6B}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -110,6 +112,14 @@ Global
 		{47846DAA-BB23-4E49-9E90-31CCC9AB01A8}.Release|Win32.Build.0 = Release|Win32
 		{47846DAA-BB23-4E49-9E90-31CCC9AB01A8}.Release|x64.ActiveCfg = Release|x64
 		{47846DAA-BB23-4E49-9E90-31CCC9AB01A8}.Release|x64.Build.0 = Release|x64
+		{EF14D067-ED6A-43C9-A36B-76CA92741A6B}.Debug|Win32.ActiveCfg = Debug|Win32
+		{EF14D067-ED6A-43C9-A36B-76CA92741A6B}.Debug|Win32.Build.0 = Debug|Win32
+		{EF14D067-ED6A-43C9-A36B-76CA92741A6B}.Debug|x64.ActiveCfg = Debug|x64
+		{EF14D067-ED6A-43C9-A36B-76CA92741A6B}.Debug|x64.Build.0 = Debug|x64
+		{EF14D067-ED6A-43C9-A36B-76CA92741A6B}.Release|Win32.ActiveCfg = Release|Win32
+		{EF14D067-ED6A-43C9-A36B-76CA92741A6B}.Release|Win32.Build.0 = Release|Win32
+		{EF14D067-ED6A-43C9-A36B-76CA92741A6B}.Release|x64.ActiveCfg = Release|x64
+		{EF14D067-ED6A-43C9-A36B-76CA92741A6B}.Release|x64.Build.0 = Release|x64
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/libairspy/vc/airspy_calibrate_2013.vcxproj b/libairspy/vc/airspy_calibrate_2013.vcxproj
new file mode 100644
index 0000000..542a861
--- /dev/null
+++ b/libairspy/vc/airspy_calibrate_2013.vcxproj
@@ -0,0 +1,177 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectName>airspy_calibrate</ProjectName>
+    <ProjectGuid>{EF14D067-ED6A-43C9-A36B-76CA92741A6B}</ProjectGuid>
+    <RootNamespace>
+    </RootNamespace>
+    <Keyword>Win32Proj</Keyword>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <PlatformToolset>v142</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v142</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <PlatformToolset>v142</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v142</PlatformToolset>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)..\$(Platform)\$(Configuration)\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)..\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)..\$(Platform)\$(Configuration)\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)..\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)..\$(Platform)\$(Configuration)\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)..\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)..\$(Platform)\$(Configuration)\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)..\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <BuildLog>
+      <Path>$(IntDir)$(ProjectName).htm</Path>
+    </BuildLog>
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\src;.\getopt;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <LanguageStandard_C>stdc11</LanguageStandard_C>
+    </ClCompile>
+    <Link>
+      <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(TargetDir)$(ProjectName).pdb</ProgramDatabaseFile>
+      <SubSystem>Console</SubSystem>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <BuildLog>
+      <Path>$(IntDir)$(ProjectName).htm</Path>
+    </BuildLog>
+    <Midl>
+      <TargetEnvironment>X64</TargetEnvironment>
+    </Midl>
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\src;.\getopt;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <LanguageStandard_C>stdc11</LanguageStandard_C>
+    </ClCompile>
+    <Link>
+      <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(TargetDir)$(ProjectName).pdb</ProgramDatabaseFile>
+      <SubSystem>Console</SubSystem>
+      <TargetMachine>MachineX64</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <BuildLog>
+      <Path>$(IntDir)$(ProjectName).htm</Path>
+    </BuildLog>
+    <ClCompile>
+      <AdditionalIncludeDirectories>..\src;.\getopt;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <LanguageStandard_C>stdc11</LanguageStandard_C>
+    </ClCompile>
+    <Link>
+      <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <ProgramDatabaseFile>$(TargetDir)$(ProjectName).pdb</ProgramDatabaseFile>
+      <SubSystem>Console</SubSystem>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <BuildLog>
+      <Path>$(IntDir)$(ProjectName).htm</Path>
+    </BuildLog>
+    <Midl>
+      <TargetEnvironment>X64</TargetEnvironment>
+    </Midl>
+    <ClCompile>
+      <AdditionalIncludeDirectories>..\src;.\getopt;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <LanguageStandard_C>stdc11</LanguageStandard_C>
+    </ClCompile>
+    <Link>
+      <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <ProgramDatabaseFile>$(TargetDir)$(ProjectName).pdb</ProgramDatabaseFile>
+      <SubSystem>Console</SubSystem>
+      <TargetMachine>MachineX64</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\airspy-tools\src\airspy_calibrate.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="airspy_2013.vcxproj">
+      <Project>{7a6c1d5c-37fc-436e-8e7b-1eb3b2b3716d}</Project>
+    </ProjectReference>
+    <ProjectReference Include="getopt_2013.vcxproj">
+      <Project>{7a6c1d5c-37fc-436e-8e7b-1eb3b2b3716d}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
-- 
2.47.3

