From ef94aa388ae2ed85e970ff4f857450a5f28cf0b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=A2=9E=E8=BE=89?= <3045316072@qq.com> Date: Fri, 29 Nov 2024 23:29:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0RTPC=E7=9A=84GPU=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BaseConstVariable.h | 21 ++- BaseLibraryCPP.rar | Bin 0 -> 56013 bytes EchoDataFormat.cpp | 43 +++-- GPUTool.cu | 279 ++++++++++++++++++++++++++++ GPUTool.cuh | 382 +++++++++++++++++++++++++++++++++++++++ ImageOperatorBase.h | 36 +++- SARSimulationImageL1.cpp | 8 +- SARSimulationImageL1.h | 1 - 8 files changed, 737 insertions(+), 33 deletions(-) create mode 100644 BaseLibraryCPP.rar create mode 100644 GPUTool.cu create mode 100644 GPUTool.cuh diff --git a/BaseConstVariable.h b/BaseConstVariable.h index bb099df..941cf24 100644 --- a/BaseConstVariable.h +++ b/BaseConstVariable.h @@ -3,7 +3,7 @@ #ifndef BASECONSTVARIABLE_H #define BASECONSTVARIABLE_H #define EIGEN_USE_MKL_ALL -//#define EIGEN_NO_DEBUG +#define EIGEN_NO_DEBUG #define EIGEN_USE_BLAS @@ -12,11 +12,17 @@ //#define DEBUGSHOWDIALOG +#define __CUDANVCC___ // 瀹氫箟CUDA鍑芥暟 + + + + //#include #include #include #include + #define MATPLOTDRAWIMAGE @@ -120,16 +126,19 @@ struct SatelliteAntPos { double Pz; double Vx; double Vy; - double Vz; + double Vz; //7 double AntDirectX; double AntDirectY; double AntDirectZ; double AVx; double AVy; - double AVz; + double AVz;//13 + double ZeroAntDiectX; + double ZeroAntDiectY; + double ZeroAntDiectZ; double lon; double lat; - double ati; // 15 + double ati; // 19 }; @@ -148,8 +157,6 @@ inline void delPointer(void* p) delete p; p = nullptr; } - - - + #endif \ No newline at end of file diff --git a/BaseLibraryCPP.rar b/BaseLibraryCPP.rar new file mode 100644 index 0000000000000000000000000000000000000000..8bf6eaa698a5605ba90c1f1db1b063058b7b23fa GIT binary patch literal 56013 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"ENVI"); - std::shared_ptr poDstDS(poDriver->Create(this->GPSPointFilePath.toUtf8().constData(), 16, PluseCount, 1, GDT_Float64, NULL)); + std::shared_ptr poDstDS(poDriver->Create(this->GPSPointFilePath.toUtf8().constData(), 19, PluseCount, 1, GDT_Float64, NULL)); GDALFlushCache((GDALDatasetH)poDstDS.get()); poDstDS.reset(); omp_unset_lock(&lock); // @@ -296,22 +296,25 @@ SatelliteAntPos EchoL0Dataset::getSatelliteAntPos(long prf_id) { std::shared_ptr antpos = this->getAntPos(); SatelliteAntPos prfpos{}; - prfpos.time = antpos.get()[prf_id * 16 + 0]; - prfpos.Px = antpos.get()[prf_id * 16 + 1]; - prfpos.Py = antpos.get()[prf_id * 16 + 2]; - prfpos.Pz = antpos.get()[prf_id * 16 + 3]; - prfpos.Vx = antpos.get()[prf_id * 16 + 4]; - prfpos.Vy = antpos.get()[prf_id * 16 + 5]; - prfpos.Vz = antpos.get()[prf_id * 16 + 6]; - prfpos.AntDirectX = antpos.get()[prf_id * 16 + 7]; - prfpos.AntDirectY = antpos.get()[prf_id * 16 + 8]; - prfpos.AntDirectZ = antpos.get()[prf_id * 16 + 9]; - prfpos.AVx = antpos.get()[prf_id * 16 + 10]; - prfpos.AVy = antpos.get()[prf_id * 16 + 11]; - prfpos.AVz =antpos.get()[prf_id * 16 + 12]; - prfpos.lon =antpos.get()[prf_id * 16 + 13]; - prfpos.lat =antpos.get()[prf_id * 16 + 14]; - prfpos.ati =antpos.get()[prf_id * 16 + 15]; + prfpos.time = antpos.get()[prf_id *19 + 0]; + prfpos.Px = antpos.get()[prf_id *19 + 1]; + prfpos.Py = antpos.get()[prf_id *19 + 2]; + prfpos.Pz = antpos.get()[prf_id *19 + 3]; + prfpos.Vx = antpos.get()[prf_id *19 + 4]; + prfpos.Vy = antpos.get()[prf_id *19 + 5]; + prfpos.Vz = antpos.get()[prf_id *19 + 6]; + prfpos.AntDirectX = antpos.get()[prf_id *19 + 7]; + prfpos.AntDirectY = antpos.get()[prf_id *19 + 8]; + prfpos.AntDirectZ = antpos.get()[prf_id *19 + 9]; + prfpos.AVx = antpos.get()[prf_id *19 + 10]; + prfpos.AVy = antpos.get()[prf_id *19 + 11]; + prfpos.AVz =antpos.get()[prf_id *19 + 12]; + prfpos.ZeroAntDiectX = antpos.get()[prf_id *19 + 13]; + prfpos.ZeroAntDiectY = antpos.get()[prf_id *19 + 14]; + prfpos.ZeroAntDiectZ = antpos.get()[prf_id *19 + 15]; + prfpos.lon =antpos.get()[prf_id *19 + 16]; + prfpos.lat =antpos.get()[prf_id *19 + 17]; + prfpos.ati =antpos.get()[prf_id *19 + 18]; return prfpos; } @@ -467,8 +470,8 @@ std::shared_ptr EchoL0Dataset::getAntPos() std::shared_ptr temp = nullptr; if (gdal_datatype == GDT_Float64) { - temp=std::shared_ptr(new double[this->PluseCount * 16],delArrPtr); - demBand->RasterIO(GF_Read, 0, 0, 16, this->PluseCount, temp.get(), 16, this->PluseCount, gdal_datatype, 0, 0); + temp=std::shared_ptr(new double[this->PluseCount * 19],delArrPtr); + demBand->RasterIO(GF_Read, 0, 0, 19, this->PluseCount, temp.get(), 19, this->PluseCount, gdal_datatype, 0, 0); } else { qDebug() << QString::fromStdString(errorCode2errInfo(ErrorCode::ECHO_L0DATA_GPSFILEFORMATERROR)) ; @@ -548,7 +551,7 @@ ErrorCode EchoL0Dataset::saveAntPos(std::shared_ptr ptr) long band_num = rasterDataset->GetRasterCount(); if (gdal_datatype == GDT_Float64) { - demBand->RasterIO(GF_Write, 0, 0, 16, this->PluseCount, ptr.get(), 16, this->PluseCount, gdal_datatype, 0, 0); + demBand->RasterIO(GF_Write, 0, 0, 19, this->PluseCount, ptr.get(), 19, this->PluseCount, gdal_datatype, 0, 0); } else { qDebug() << QString::fromStdString(errorCode2errInfo(ErrorCode::ECHO_L0DATA_GPSFILEFORMATERROR)); diff --git a/GPUTool.cu b/GPUTool.cu new file mode 100644 index 0000000..81aad62 --- /dev/null +++ b/GPUTool.cu @@ -0,0 +1,279 @@ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "BaseConstVariable.h" +#include "GPUTool.cuh" + +#ifdef __CUDANVCC___ + +#define CUDAMEMORY Memory1MB*100 + +#define LAMP_CUDA_PI 3.141592653589793238462643383279 + + + +// 定义参数 + __device__ cuComplex cuCexpf(cuComplex x) +{ + float factor = exp(x.x); + return make_cuComplex(factor * cos(x.y), factor * sin(x.y)); +} + +__global__ void CUDA_DistanceAB(float* Ax, float* Ay, float* Az, float* Bx, float* By, float* Bz,float *R, long len) { + long idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < len) { + R[idx] = sqrtf(powf(Ax[idx]-Bx[idx], 2) + powf(Ay[idx] - By[idx], 2) + powf(Az[idx] - Bz[idx], 2)); + } +} + +__global__ void CUDA_B_DistanceA(float* Ax, float* Ay, float* Az, float Bx, float By, float Bz, float* R, long len) { + long idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < len) { + R[idx] = sqrtf(powf(Ax[idx] - Bx, 2) + powf(Ay[idx] - By, 2) + powf(Az[idx] - Bz, 2)); + } +} + +__global__ void CUDA_make_VectorA_B(float sX, float sY, float sZ, float* tX, float* tY, float* tZ, float* RstX, float* RstY, float* RstZ, long len) { + long idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < len) { + RstX[idx] = sX - tX[idx]; + RstY[idx] = sY - tY[idx]; + RstZ[idx] = sZ - tZ[idx]; + } +} + +__global__ void CUDA_Norm_Vector(float* Vx, float* Vy, float* Vz,float *R, long len) { + long idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < len) { + R[idx] = sqrtf(powf(Vx[idx],2)+powf(Vy[idx],2)+powf(Vz[idx], 2)); + } +} + +__global__ void CUDA_cosAngle_VA_AB(float* Ax, float* Ay, float* Az, float* Bx, float* By, float* Bz, float* anglecos,long len) { + long idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < len) { + float tAx = Ax[idx]; + float tAy = Ay[idx]; + float tAz = Az[idx]; + float tBx = Bx[idx]; + float tBy = By[idx]; + float tBz = Bz[idx]; + float AR = sqrtf(powf(tAx,2) + powf(tAy,2) + powf(tAz,2)); + float BR = sqrtf(powf(tBx,2) + powf(tBy,2) + powf(tBz,2)); + float dotAB = tAx * tBx + tAy * tBy + tAz * tBz; + float result =acosf( dotAB / (AR * BR)); + anglecos[idx] = result; + } +} + +__global__ void CUDA_SatelliteAntDirectNormal(float* RstX,float* RstY,float* RstZ, + float antXaxisX,float antXaxisY,float antXaxisZ, + float antYaxisX,float antYaxisY,float antYaxisZ, + float antZaxisX,float antZaxisY,float antZaxisZ, + float antDirectX,float antDirectY,float antDirectZ, + float* thetaAnt,float* phiAnt + , long len) { + long idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < len) { + float Xst = -1*RstX[idx]; // 卫星 --> 地面 + float Yst = -1*RstY[idx]; + float Zst = -1*RstZ[idx]; + float AntXaxisX=antXaxisX ; + float AntXaxisY=antXaxisY ; + float AntXaxisZ=antXaxisZ ; + float AntYaxisX=antYaxisX ; + float AntYaxisY=antYaxisY ; + float AntYaxisZ=antYaxisZ ; + float AntZaxisX=antZaxisX ; + float AntZaxisY=antZaxisY ; + float AntZaxisZ=antZaxisZ ; + // 天线指向在天线坐标系下的值 + float Xant = (Xst * (AntYaxisY * AntZaxisZ - AntYaxisZ * AntZaxisY) + Xst * ( AntXaxisZ * AntZaxisY - AntXaxisY * AntZaxisZ) + Xst * ( AntXaxisY * AntYaxisZ - AntXaxisZ * AntYaxisY)) / ( AntXaxisX * ( AntYaxisY * AntZaxisZ - AntZaxisY * AntYaxisZ) - AntYaxisX * ( AntXaxisY * AntZaxisZ - AntXaxisZ * AntZaxisY) + AntZaxisX * ( AntXaxisY * AntYaxisZ - AntXaxisZ * AntYaxisY)); + float Yant = (Yst * (AntYaxisZ * AntZaxisX - AntYaxisX * AntZaxisZ) + Yst * ( AntXaxisX * AntZaxisZ - AntXaxisZ * AntZaxisX) + Yst * ( AntYaxisX * AntXaxisZ - AntXaxisX * AntYaxisZ)) / ( AntXaxisX * ( AntYaxisY * AntZaxisZ - AntZaxisY * AntYaxisZ) - AntYaxisX * ( AntXaxisY * AntZaxisZ - AntXaxisZ * AntZaxisY) + AntZaxisX * ( AntXaxisY * AntYaxisZ - AntXaxisZ * AntYaxisY)); + float Zant = (Zst * (AntYaxisX * AntZaxisY - AntYaxisY * AntZaxisX) + Zst * ( AntXaxisY * AntZaxisX - AntXaxisX * AntZaxisY) + Zst * ( AntXaxisX * AntYaxisY - AntYaxisX * AntXaxisY)) / ( AntXaxisX * ( AntYaxisY * AntZaxisZ - AntZaxisY * AntYaxisZ) - AntYaxisX * ( AntXaxisY * AntZaxisZ - AntXaxisZ * AntZaxisY) + AntZaxisX * ( AntXaxisY * AntYaxisZ - AntXaxisZ * AntYaxisY)); + // 计算theta 与 phi + float Norm = sqrtf(Xant * Xant + Yant * Yant + Zant * Zant); // 计算 pho + float ThetaAnt = acosf(Zant / Norm); // theta 与 Z轴的夹角 + float YsinTheta = Yant / sinf(ThetaAnt); + float PhiAnt = (YsinTheta/abs(YsinTheta)) * acosf( Xant / (Norm * sinf(ThetaAnt))); + thetaAnt[idx] = ThetaAnt; + phiAnt[idx] = PhiAnt; + } +} + + +__global__ void CUDA_calculationEcho(float* sigma0, float* TransAnt, float* ReciveAnt, + float* localangle, float* R,float* slopeangle, + float nearRange, float Fs,float Pt,float lamda,long FreqIDmax, + cuComplex* echoArr , long* FreqID, + long len) { + long idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < len) { + float r = R[idx]; + float amp = Pt * TransAnt[idx] * ReciveAnt[idx]; + amp= amp * sigma0[idx]; + amp = amp / (powf(4* LAMP_CUDA_PI,2)*powf(r,4)); // 反射强度 + + // 处理相位 + float phi = (-4 * LAMP_CUDA_PI / lamda) * r; + cuComplex echophi = make_cuComplex(0, phi) ; + cuComplex echophiexp = cuCexpf(echophi); + + float timeR = 2 * (r - nearRange) / LIGHTSPEED * Fs; + long timeID = floorf(timeR); + if (timeID < 0 || timeID >= FreqIDmax) { + timeID = 0; + amp = 0; + } + + cuComplex echo; + echo.x = echophiexp.x * amp; + echo.y = echophiexp.y * amp; + + + echoArr[idx] = echo; + + } +} + + + +//错误提示 +void checkCudaError(cudaError_t err, const char* msg) { + if (err != cudaSuccess) { + std::cerr << "CUDA error: " << msg << " (" << cudaGetErrorString(err) << ")" << std::endl; + exit(EXIT_FAILURE); + } + +} + +// 主机参数内存声明 +extern "C" void mallocCUDAHost(void* ptr, long memsize) { + cudaMallocHost(&ptr, memsize); +} + +// 主机参数内存释放 +extern "C" void FreeCUDAHost(void* ptr) { + cudaFreeHost(ptr); +} + +// GPU参数内存声明 +extern "C" void mallocCUDADevice(void* ptr, long memsize) { + cudaMalloc(&ptr, memsize); +} + +// GPU参数内存释放 +extern "C" void FreeCUDADevice(void* ptr) { + cudaFree(ptr); +} + +// GPU 内存数据转移 +extern "C" void HostToDevice(void* hostptr, void* deviceptr, long memsize) { + cudaMemcpy(deviceptr, hostptr, memsize, cudaMemcpyHostToDevice); +} + +extern "C" void DeviceToHost(void* hostptr, void* deviceptr, long memsize) { + cudaMemcpy(hostptr, deviceptr, memsize, cudaMemcpyDeviceToHost); +} + + + +extern "C" void distanceAB(float* Ax, float* Ay, float* Az, float* Bx, float* By, float* Bz, float* R,long len) { + // 设置 CUDA 核函数的网格和块的尺寸 + int blockSize = 256; // 每个块的线程数 + int numBlocks = (len + blockSize - 1) / blockSize; // 根据 pixelcount 计算网格大小 + // 调用 CUDA 核函数 + CUDA_DistanceAB << > > ( Ax, Ay, Az, Bx, By, Bz, R, len); +} + +extern "C" void BdistanceAs(float* Ax, float* Ay, float* Az, float Bx, float By, float Bz, float* R, long len) { + // 设置 CUDA 核函数的网格和块的尺寸 + int blockSize = 256; // 每个块的线程数 + int numBlocks = (len + blockSize - 1) / blockSize; // 根据 pixelcount 计算网格大小 + // 调用 CUDA 核函数 + CUDA_B_DistanceA << > > (Ax, Ay, Az, Bx, By, Bz, R, len); + cudaDeviceSynchronize(); +} + +extern "C" void make_VectorA_B(float sX, float sY, float sZ, float* tX, float* tY, float* tZ, float* RstX, float* RstY, float* RstZ, long len) { + // 设置 CUDA 核函数的网格和块的尺寸 + int blockSize = 256; // 每个块的线程数 + int numBlocks = (len + blockSize - 1) / blockSize; // 根据 pixelcount 计算网格大小 + // 调用 CUDA 核函数 + CUDA_make_VectorA_B << > > (sX, sY, sZ,tX, tY, tZ, RstX,RstY, RstZ, len); + cudaDeviceSynchronize(); +} + +extern "C" void Norm_Vector(float* Vx, float* Vy, float* Vz, float* R, long len) { + // 设置 CUDA 核函数的网格和块的尺寸 + int blockSize = 256; // 每个块的线程数 + int numBlocks = (len + blockSize - 1) / blockSize; // 根据 pixelcount 计算网格大小 + // 调用 CUDA 核函数 + CUDA_Norm_Vector << > > (Vx,Vy,Vz,R, len); + cudaDeviceSynchronize(); +} + +extern "C" void cosAngle_VA_AB(float* Ax, float* Ay, float* Az, float* Bx, float* By, float* Bz, float* anglecos, long len) { + int blockSize = 256; // 每个块的线程数 + int numBlocks = (len + blockSize - 1) / blockSize; // 根据 pixelcount 计算网格大小 + // 调用 CUDA 核函数 + CUDA_cosAngle_VA_AB << > > (Ax, Ay, Az, Bx, By, Bz, anglecos, len); + cudaDeviceSynchronize(); +} + +extern "C" void SatelliteAntDirectNormal(float* RstX, float* RstY, float* RstZ, + float antXaxisX, float antXaxisY, float antXaxisZ, + float antYaxisX, float antYaxisY, float antYaxisZ, + float antZaxisX, float antZaxisY, float antZaxisZ, + float antDirectX, float antDirectY, float antDirectZ, + float* thetaAnt, float* phiAnt + , long len) { + + int blockSize = 256; // 每个块的线程数 + int numBlocks = (len + blockSize - 1) / blockSize; // 根据 pixelcount 计算网格大小 + // 调用 CUDA 核函数 + CUDA_SatelliteAntDirectNormal << > > ( RstX, RstY, RstZ, + antXaxisX, antXaxisY, antXaxisZ, + antYaxisX, antYaxisY, antYaxisZ, + antZaxisX, antZaxisY, antZaxisZ, + antDirectX, antDirectY, antDirectZ, + thetaAnt, phiAnt + , len); + cudaDeviceSynchronize(); + + } + + + +extern "C" void calculationEcho(float* sigma0,float* TransAnt,float* ReciveAnt, + float* localangle,float* R, float* slopeangle, + float nearRange,float Fs, float pt, float lamda, long FreqIDmax, + cuComplex* echoAmp,long* FreqID, + long len) +{ + int blockSize = 256; // 每个块的线程数 + int numBlocks = (len + blockSize - 1) / blockSize; // 根据 pixelcount 计算网格大小 + // 调用 CUDA 核函数 + CUDA_calculationEcho << > > ( sigma0, TransAnt,ReciveAnt, + localangle, R, slopeangle, + nearRange, Fs, pt, lamda, FreqIDmax, + echoAmp, FreqID, + len); + cudaDeviceSynchronize(); + +} + + + + + + +#endif \ No newline at end of file diff --git a/GPUTool.cuh b/GPUTool.cuh new file mode 100644 index 0000000..505fd09 --- /dev/null +++ b/GPUTool.cuh @@ -0,0 +1,382 @@ +#ifndef GPUTOOL_H +#define GPUTOOL_H +#ifdef __CUDANVCC___ +#include "BaseConstVariable.h" +#include +#include +#include +#include + +// 默认显存分布 + + +enum LAMPGPUDATETYPE { + LAMP_LONG, + LAMP_FLOAT, + LAMP_COMPLEXFLOAT +}; + + + + + + +// GPU 内存函数 +extern "C" void mallocCUDAHost(void* ptr, long memsize); // 主机内存声明 +extern "C" void FreeCUDAHost(void* ptr); +extern "C" void mallocCUDADevice(void* ptr, long memsize); // GPU内存声明 +extern "C" void FreeCUDADevice(void* ptr); +extern "C" void HostToDevice(void* hostptr, void* deviceptr, long memsize);//GPU 内存数据转移 设备 -> GPU +extern "C" void DeviceToHost(void* hostptr, void* deviceptr, long memsize);//GPU 内存数据转移 GPU -> 设备 + + +// 仿真所需的常用函数 +extern "C" void distanceAB(float* Ax, float* Ay, float* Az, float* Bx, float* By, float* Bz, float* R, long member); +extern "C" void BdistanceAs(float* Ax, float* Ay, float* Az, float Bx, float By, float Bz, float* R, long member); +extern "C" void make_VectorA_B(float sX, float sY, float sZ, float* tX, float* tY, float* tZ, float* RstX, float* RstY, float* RstZ, long member); +extern "C" void Norm_Vector(float* Vx, float* Vy, float* Vz, float* R, long member); +extern "C" void cosAngle_VA_AB(float* Ax, float* Ay, float* Az, float* Bx, float* By, float* Bz, float* anglecos, long len); +extern "C" void SatelliteAntDirectNormal(float* RstX, float* RstY, float* RstZ, float antXaxisX, float antXaxisY, float antXaxisZ, float antYaxisX, float antYaxisY, float antYaxisZ, float antZaxisX, float antZaxisY, float antZaxisZ, float antDirectX, float antDirectY, float antDirectZ, float* thetaAnt, float* phiAnt, long len); +extern "C" void calculationEcho(float* sigma0, float* TransAnt, float* ReciveAnt,float* localangle, float* R, float* slopeangle,float nearRange, float Fs, float pt, float lamda, long FreqIDmax,cuComplex* echoAmp, long* FreqID, long len); + +#endif +#endif + + +/** +* + + + + double* databuffer = new double[nXSize * nYSize * 2]; + poBand->RasterIO(GF_Read, start_col, start_row, cols_count, rows_count, databuffer, cols_count, + rows_count, GDT_CFloat64, 0, 0); + GDALClose((GDALDatasetH)poDataset); + Eigen::MatrixXcd rasterData(nYSize, nXSize); // 使用Eigen的MatrixXcd + for(size_t i = 0; i < nYSize; i++) { + for(size_t j = 0; j < nXSize; j++) { + rasterData(i, j) = std::complex(databuffer[i * nXSize * 2 + j * 2], + databuffer[i * nXSize * 2 + j * 2 + 1]); + } + } + + delete[] databuffer; + + + + gdalImage demxyz(demxyzPath);// 地面点坐标 + gdalImage demlandcls(this->LandCoverPath);// 地表覆盖类型 + gdalImage demsloperxyz(this->demsloperPath);// 地面坡向 + + + omp_lock_t lock; // 定义锁 + omp_init_lock(&lock); // 初始化锁 + long start_ids = 1250; + for (start_ids = 1; start_ids < demxyz.height; start_ids = start_ids + line_invert) { // 8+ 17 + 0.3 MB + QDateTime current = QDateTime::currentDateTime(); + long pluseStep = Memory1MB * 100 / 3 / PlusePoint; + if (pluseStep * num_thread * 3 > this->PluseCount) { + pluseStep = this->PluseCount / num_thread / 3; + } + pluseStep = pluseStep > 50 ? pluseStep : 50; + + + qDebug() << current.toString("yyyy-MM-dd HH:mm:ss.zzz") << " \tstart \t " << start_ids << " - " << start_ids + line_invert << "\t" << demxyz.height << "\t pluseCount:\t" << pluseStep; + // 文件读取 + Eigen::MatrixXd dem_x = demxyz.getData(start_ids - 1, 0, line_invert + 1, demxyz.width, 1); // + Eigen::MatrixXd dem_y = demxyz.getData(start_ids - 1, 0, line_invert + 1, demxyz.width, 2); // + Eigen::MatrixXd dem_z = demxyz.getData(start_ids - 1, 0, line_invert + 1, demxyz.width, 3); // + + // 地表覆盖 + std::shared_ptr dem_landcls = readDataArr(demlandcls, start_ids - 1, 0, line_invert + 1, demxyz.width, 1, GDALREADARRCOPYMETHOD::VARIABLEMETHOD); // 地表覆盖类型 + long* dem_landcls_ptr = dem_landcls.get(); + double localAngle = 30.0; + + bool sigmaNoZeroFlag = true; + for (long ii = 0; ii < dem_x.rows(); ii++) { + for (long jj = 0; jj < dem_y.cols(); jj++) { + if (0 != this->SigmaDatabasePtr->getAmp(dem_landcls_ptr[dem_x.cols() * ii + jj], localAngle, polartype)) { + sigmaNoZeroFlag = false; + break; + } + } + if (!sigmaNoZeroFlag) { + break; + } + } + if (sigmaNoZeroFlag) { + continue; + } + + //#ifdef DEBUGSHOWDIALOG + // dialog->load_double_MatrixX_data(dem_z, "dem_z"); + //#endif + + + Eigen::MatrixXd demsloper_x = demsloperxyz.getData(start_ids - 1, 0, line_invert + 1, demxyz.width, 1); // + Eigen::MatrixXd demsloper_y = demsloperxyz.getData(start_ids - 1, 0, line_invert + 1, demxyz.width, 2); // + Eigen::MatrixXd demsloper_z = demsloperxyz.getData(start_ids - 1, 0, line_invert + 1, demxyz.width, 3); // + Eigen::MatrixXd sloperAngle = demsloperxyz.getData(start_ids - 1, 0, line_invert + 1, demxyz.width, 4); // + sloperAngle = sloperAngle.array() * T180_PI; + + long dem_rows = dem_x.rows(); + long dem_cols = dem_x.cols(); + + long freqidx = 0;// + + + +#ifdef DEBUGSHOWDIALOG + ImageShowDialogClass* dialog = new ImageShowDialogClass(nullptr); + dialog->show(); + + Eigen::MatrixXd landaArr = Eigen::MatrixXd::Zero(dem_rows, dem_cols); + for (long i = 0; i < dem_rows; i++) { + for (long j = 0; j < dem_cols; j++) { + landaArr(i, j) = dem_landcls.get()[i * dem_cols + j]; + } + } + dialog->load_double_MatrixX_data(landaArr, "landCover"); +#endif + //qDebug() << " pluse bolck size :\t " << pluseStep << " all size:\t" << this->PluseCount; + long processNumber = 0; + +#pragma omp parallel for + for (long startprfidx = 0; startprfidx < this->PluseCount; startprfidx = startprfidx + pluseStep) { // 17 + 0.3 MB + long prfcount_step = startprfidx + pluseStep < this->PluseCount ? pluseStep : this->PluseCount - startprfidx; + Eigen::MatrixXcd echoPluse = Eigen::MatrixXcd::Zero(prfcount_step, PlusePoint); // 当前脉冲的回波积分情况 + // 内存预分配 + Eigen::MatrixXd Rst_x = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd Rst_y = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd Rst_z = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd R = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd localangle = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd Vst_x = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd Vst_y = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd Vst_z = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd fde = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd fr = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd Rx = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd sigam = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd echoAmp = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()).array() + Pt; + Eigen::MatrixXd Rphi = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd TimeRange = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd TransAnt = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd ReciveAnt = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + + Eigen::MatrixXd AntTheta = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + Eigen::MatrixXd AntPhi = Eigen::MatrixXd::Zero(dem_x.rows(), dem_x.cols()); + + double minR = 0, maxR = 0; + double minLocalAngle = 0, maxLocalAngle = 0; + + Vector3D Rt = { 0,0,0 }; + SatelliteOribtNode oRs = SatelliteOribtNode{ 0 };; + + Vector3D p0 = {}, slopeVector = {}, sateAntDirect = {}; + Vector3D Rs = {}, Vs = {}, Ast = {}; + SatelliteAntDirect antdirectNode = {}; + std::complex echofreq; + std::complex Imag1(0, 1); + double TAntPattern = 1; // 发射天线方向图 + double RAntPanttern = 1;// 接收天线方向图 + double maxechoAmp = 1; + double tempAmp = 1; + for (long prfidx = 0; prfidx < prfcount_step; prfidx++) + { + oRs = sateOirbtNodes[prfidx + startprfidx]; + + // 计算天线方向图 + for (long jj = 1; jj < dem_cols - 1; jj++) { + for (long ii = 1; ii < dem_rows - 1; ii++) { + p0.x = dem_x(ii, jj); + p0.y = dem_y(ii, jj); + p0.z = dem_z(ii, jj); + this->TaskSetting->getSatelliteAntDirectNormal(oRs, p0, antdirectNode); + //antdirectNode.ThetaAnt = antdirectNode.ThetaAnt * r2d; + //antdirectNode.PhiAnt = antdirectNode.PhiAnt * r2d; + AntTheta(ii, jj) = antdirectNode.ThetaAnt * r2d; + AntPhi(ii, jj) = antdirectNode.PhiAnt * r2d; + } + } + + // 计算发射天线方向图 + for (long jj = 1; jj < dem_cols - 1; jj++) { + for (long ii = 1; ii < dem_rows - 1; ii++) { + TransformPattern->getGainLinear(AntTheta(ii, jj), AntPhi(ii, jj), TransAnt(ii, jj)); + //TransAnt(ii, jj) = TAntPattern; + } + } + + // 计算接收天线方向图 + for (long jj = 1; jj < dem_cols - 1; jj++) { + for (long ii = 1; ii < dem_rows - 1; ii++) { + TransformPattern->getGainLinear(AntTheta(ii, jj), AntPhi(ii, jj), ReciveAnt(ii, jj)); + //ReciveAnt(ii, jj) = RAntPanttern; + } + } + + // 计算经过增益的能量 + echoAmp = Pt * TransAnt.array() * ReciveAnt.array(); + + maxechoAmp = echoAmp.maxCoeff(); + if (std::abs(maxechoAmp) < PRECISIONTOLERANCE) { // 这种情况下,不在合成孔径范围中 + continue; + } + + Rs.x = sateOirbtNodes[prfidx + startprfidx].Px; // 卫星位置 + Rs.y = sateOirbtNodes[prfidx + startprfidx].Py; + Rs.z = sateOirbtNodes[prfidx + startprfidx].Pz; + + Vs.x = sateOirbtNodes[prfidx + startprfidx].Vx; // 卫星速度 + Vs.y = sateOirbtNodes[prfidx + startprfidx].Vy; + Vs.z = sateOirbtNodes[prfidx + startprfidx].Vz; + + Ast.x = sateOirbtNodes[prfidx + startprfidx].AVx;// 卫星加速度 + Ast.y = sateOirbtNodes[prfidx + startprfidx].AVy; + Ast.z = sateOirbtNodes[prfidx + startprfidx].AVz; + + Rst_x = Rs.x - dem_x.array(); // Rst = Rs - Rt; + Rst_y = Rs.y - dem_y.array(); + Rst_z = Rs.z - dem_z.array(); + R = (Rst_x.array().pow(2) + Rst_y.array().pow(2) + Rst_z.array().pow(2)).array().sqrt(); // R + + minR = R.minCoeff(); + maxR = R.maxCoeff(); + //qDebug() << "minR:\t" << minR << " maxR:\t" << maxR; + if (maxRFarRange) { + continue; + } + else {} + + // getCosAngle + // double c = dot(a, b) / (getlength(a) * getlength(b)); + // return acos(c > 1 ? 1 : c < -1 ? -1 : c) * r2d; + // localangle = getCosAngle(Rst, slopeVector) * T180_PI; // 注意这个只能实时计算,因为非实时计算代价太大 + localangle = (Rst_x.array() * demsloper_x.array() + Rst_y.array() * demsloper_y.array() + Rst_z.array() * demsloper_z.array()).array(); // dot(a, b) + localangle = localangle.array() / R.array(); + localangle = localangle.array() / (demsloper_x.array().pow(2) + demsloper_y.array().pow(2) + demsloper_z.array().pow(2)).array().sqrt().array(); + localangle = localangle.array().acos(); // 弧度值 + + minLocalAngle = localangle.minCoeff(); + maxLocalAngle = localangle.maxCoeff(); + if (maxLocalAngle<0 || minLocalAngle>PI / 2) { + continue; + } + else {} + + //Vst_x = Vs.x + 1 * earthRoute * dem_y.array(); // Vst = Vs - Vt; + //Vst_y = Vs.y - 1 * earthRoute * dem_x.array(); + //Vst_z = Vs.z - Eigen::MatrixXd::Zero(dem_x.rows(), dem_y.cols()).array(); + + //// 计算多普勒中心频率 Rst, Vst : ( - 2 / lamda) * dot(Rs - Rt, Vs - Vt) / R; // 星载合成孔径雷达原始回波数据模拟研究 3.18 + //fde = (-2 / lamda) * (Rst_x.array() * Vst_x.array() + Rst_y.array() * Vst_y.array() + Rst_z.array() * Vst_z.array()).array() / (R.array()); + //// 计算多普勒频率斜率 // 星载合成孔径雷达原始回波数据模拟研究 3.19 + //// -(2/lamda)*( dot(Vs - Vt, Vs - Vt)/R + dot(Ast, Rs - Rt)/R - std::pow(dot(Vs - Vt, Rs - Rt),2 )/std::pow(R,3)); + //fr = (-2 / lamda) * + // (Vst_x.array() * Vst_x.array() + Vst_y.array() * Vst_y.array() + Vst_z.array() * Vst_z.array()).array() / (R.array()) + + // (-2 / lamda) * + // (Ast.x * Rst_x.array() + Ast.y * Rst_y.array() + Ast.z * Rst_z.array()).array() / (R.array()) - + // (-2 / lamda) * + // (Vst_x.array() * Rst_x.array() + Vst_y.array() * Rst_y.array() + Vst_z.array() * Rst_z.array()).array().pow(2) / (R.array().pow(3)); + // 计算回波 + Rx = R;// -(lamda / 2) * (fde * TRx + 0.5 * fr * TRx * TRx); // 斜距历程值 + + + // 逐点计算 this->SigmaDatabasePtr->getAmp(covercls, localangle, polartype); // 后向散射系数 HH + + for (long ii = 0; ii < dem_x.rows(); ii++) { + for (long jj = 0; jj < dem_y.cols(); jj++) { + sigam(ii, jj) = this->SigmaDatabasePtr->getAmp(dem_landcls_ptr[dem_x.cols() * ii + jj], localangle(ii, jj) * r2d, polartype); + } + } + + if (sigam.maxCoeff() > 0) {} + else { + continue; + } + // projArea = 1 / std::cos(sloperAngle) * std::sin(localangle); // 投影面积系数,单位投影面积 1m x 1m --注意这里是假设,后期再补充 + // echoAmp = projArea*TAntPattern * RAntPanttern * sigam / (4 * PI * R * R); + + echoAmp = echoAmp.array() * sigam.array() * (1 / sloperAngle.array().cos() * localangle.array().sin()); // 反射强度 + echoAmp = echoAmp.array() / (4 * PI * R.array().pow(2)); // 距离衰减 + + Rphi = -4 * PI / lamda * Rx.array();// 距离徙动相位 + // 积分 + TimeRange = ((2 * R.array() / LIGHTSPEED - TimgNearRange).array() * Fs).array(); + double localAnglepoint = -1; + long prf_freq_id = 0; + + + for (long jj = 1; jj < dem_cols - 1; jj++) { + for (long ii = 1; ii < dem_rows - 1; ii++) { + prf_freq_id = std::floor(TimeRange(ii, jj)); + if (prf_freq_id < 0 || prf_freq_id >= PlusePoint || localangle(ii, jj) < 0 || localangle(ii, jj) > PI / 2 || echoAmp(ii, jj) == 0) { + continue; + } + echofreq = echoAmp(ii, jj) * std::exp(Rphi(ii, jj) * Imag1); + echoPluse(prfidx, prf_freq_id) = echoPluse(prfidx, prf_freq_id) + echofreq; + } + } + +#ifdef DEBUGSHOWDIALOG + ImageShowDialogClass* localangledialog = new ImageShowDialogClass(dialog); + localangledialog->show(); + localangledialog->load_double_MatrixX_data(localangle.array() * r2d, "localangle"); + + + ImageShowDialogClass* sigamdialog = new ImageShowDialogClass(dialog); + sigamdialog->show(); + sigamdialog->load_double_MatrixX_data(TimeRange, "TimeRange"); + + + ImageShowDialogClass* ampdialog = new ImageShowDialogClass(dialog); + ampdialog->show(); + ampdialog->load_double_MatrixX_data(echoAmp, "echoAmp"); + + Eigen::MatrixXd echoPluseamp = echoPluse.array().abs().cast().array(); + ImageShowDialogClass* echoampdialog = new ImageShowDialogClass(dialog); + echoampdialog->show(); + echoampdialog->load_double_MatrixX_data(echoPluseamp, "echoPluseamp"); + + + dialog->exec(); +#endif + //qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz") << " end " << prfidx; + } + //qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz")<<" step "<< prfcount_step; + + omp_set_lock(&lock); // 回波整体赋值处理 + for (long prfidx = 0; prfidx < prfcount_step; prfidx++) { + for (long freqidx = 0; freqidx < PlusePoint; freqidx++) + { + //qDebug() << prfidx << " " << freqidx << " " << echoPluse(prfidx, freqidx).real() << " + " << echoPluse(prfidx, freqidx).imag() << " j"; + echo.get()[(prfidx + startprfidx) * PlusePoint + freqidx] = echo.get()[(prfidx + startprfidx) * PlusePoint + freqidx] + echoPluse(prfidx, freqidx); + } + } + //this->EchoSimulationData->saveEchoArr(echo, 0, PluseCount); + omp_unset_lock(&lock); // 解锁 + //qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz") << " step 2" << prfcount_step; + } + + omp_set_lock(&lock); // 保存文件 + processNumber = processNumber + pluseStep; + + this->EchoSimulationData->saveEchoArr(echo, 0, PluseCount); + omp_unset_lock(&lock); // 解锁 + + qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz") << " \t " << start_ids << "\t--\t " << start_ids + line_invert << "\t/\t" << demxyz.height; + + } + omp_destroy_lock(&lock); // 销毁锁 + + + + + + + + + + +*/ \ No newline at end of file diff --git a/ImageOperatorBase.h b/ImageOperatorBase.h index bc9a859..3e2d33a 100644 --- a/ImageOperatorBase.h +++ b/ImageOperatorBase.h @@ -287,7 +287,7 @@ std::shared_ptr readDataArr(gdalImage& imgds, int start_row, int start_col, i rows_count = start_row + rows_count <= imgds.height ? rows_count : imgds.height - start_row; cols_count = start_col + cols_count <= imgds.width ? cols_count : imgds.width - start_col; - Eigen::MatrixXd datamatrix(rows_count, cols_count); + //Eigen::MatrixXd datamatrix(rows_count, cols_count); if (gdal_datatype == GDT_Byte) { char* temp = new char[rows_count * cols_count]; @@ -416,6 +416,40 @@ std::shared_ptr readDataArr(gdalImage& imgds, int start_row, int start_col, i } delete[] temp; } + else if (gdal_datatype == GDT_CFloat32) { + float* temp = new float[rows_count * cols_count*2]; + demBand->RasterIO(GF_Read, start_col, start_row, cols_count, rows_count, temp, cols_count, rows_count, gdal_datatype, 0, 0); + + result = std::shared_ptr(new T[rows_count * cols_count], delArrPtr); + if (method == GDALREADARRCOPYMETHOD::MEMCPYMETHOD) { + std::memcpy(result.get(), temp, rows_count * cols_count); + } + else if (method == GDALREADARRCOPYMETHOD::VARIABLEMETHOD) { + long count = rows_count * cols_count; + for (long i = 0; i < count; i++) { + result.get()[i] = std::complex(temp[i * 2 ], + temp[i * 2 + 1]); + } + } + delete[] temp; + } + else if (gdal_datatype == GDT_CFloat64) { + double* temp = new double[rows_count * cols_count*2]; + demBand->RasterIO(GF_Read, start_col, start_row, cols_count, rows_count, temp, cols_count, rows_count, gdal_datatype, 0, 0); + + result = std::shared_ptr(new T[rows_count * cols_count], delArrPtr); + if (method == GDALREADARRCOPYMETHOD::MEMCPYMETHOD) { + std::memcpy(result.get(), temp, rows_count * cols_count); + } + else if (method == GDALREADARRCOPYMETHOD::VARIABLEMETHOD) { + long count = rows_count * cols_count; + for (long i = 0; i < count; i++) { + result.get()[i] = std::complex(temp[i * 2], + temp[i * 2 + 1]); + } + } + delete[] temp; + } else { } GDALClose((GDALDatasetH)rasterDataset); diff --git a/SARSimulationImageL1.cpp b/SARSimulationImageL1.cpp index 1e78618..4fae528 100644 --- a/SARSimulationImageL1.cpp +++ b/SARSimulationImageL1.cpp @@ -115,7 +115,7 @@ ErrorCode SARSimulationImageL1Dataset::OpenOrNew(QString folder, QString filenam CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "YES"); GDALDriver* poDriver = GetGDALDriverManager()->GetDriverByName("ENVI"); - std::shared_ptr poDstDS(poDriver->Create(this->GPSPointFilePath.toUtf8().constData(), 16, rowCount, 1, GDT_Float64, NULL)); + std::shared_ptr poDstDS(poDriver->Create(this->GPSPointFilePath.toUtf8().constData(), 19, rowCount, 1, GDT_Float64, NULL)); GDALFlushCache((GDALDatasetH)poDstDS.get()); poDstDS.reset(); omp_unset_lock(&lock); @@ -563,8 +563,8 @@ std::shared_ptr SARSimulationImageL1Dataset::getAntPos() std::shared_ptr temp = nullptr; if (gdal_datatype == GDT_Float64) { - temp = std::shared_ptr(new double[this->rowCount * 16], delArrPtr); - demBand->RasterIO(GF_Read, 0, 0, 10, this->rowCount, temp.get(), 16, this->rowCount, gdal_datatype, 0, 0); + temp = std::shared_ptr(new double[this->rowCount * 19], delArrPtr); + demBand->RasterIO(GF_Read, 0, 0, 19, this->rowCount, temp.get(), 19, this->rowCount, gdal_datatype, 0, 0); } else { qDebug() << QString::fromStdString(errorCode2errInfo(ErrorCode::ECHO_L0DATA_GPSFILEFORMATERROR)); @@ -591,7 +591,7 @@ ErrorCode SARSimulationImageL1Dataset::saveAntPos(std::shared_ptr ptr) long band_num = rasterDataset->GetRasterCount(); if (gdal_datatype == GDT_Float64) { - demBand->RasterIO(GF_Write, 0, 0, 16, this->rowCount, ptr.get(), 16, this->rowCount, gdal_datatype, 0, 0); + demBand->RasterIO(GF_Write, 0, 0, 19, this->rowCount, ptr.get(), 19, this->rowCount, gdal_datatype, 0, 0); } else { qDebug() << QString::fromStdString(errorCode2errInfo(ErrorCode::ECHO_L0DATA_GPSFILEFORMATERROR)); diff --git a/SARSimulationImageL1.h b/SARSimulationImageL1.h index c0ed6db..eb3cecf 100644 --- a/SARSimulationImageL1.h +++ b/SARSimulationImageL1.h @@ -14,7 +14,6 @@ enum RasterLevel { RasterL2 }; - class SARSimulationImageL1Dataset { public: