#!/usr/bin/env python # -*- coding: utf-8 -*- # *************************************************************************** # * Copyright (C) 2024, Paul Lutus * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU General Public License as published by * # * the Free Software Foundation; either version 2 of the License, or * # * (at your option) any later version. * # * * # * This program is distributed in the hope that it will be useful, * # * but WITHOUT ANY WARRANTY; without even the implied warranty of * # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * # * GNU General Public License for more details. * # * * # * You should have received a copy of the GNU General Public License * # * along with this program; if not, write to the * # * Free Software Foundation, Inc., * # * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * # *************************************************************************** import math, cmath, datetime, time from gnuradio import soapy from subprocess import Popen, PIPE def open_hackrf(freq_hz,amp_enable,gain_db,samplerate_hz,clock_correction_hz): proc = Popen( ["hackrf_transfer", "-t", "-", # i.e. stdin "-f", str(freq_hz), "-a", str(amp_enable), "-x", str(gain_db), "-s" , str(samplerate_hz), "-C", str(clock_correction_hz)], stdin=PIPE, stdout=PIPE, stderr=PIPE ) return proc def transmit( pipe, mode, mf, # modulation frequency Hz md, # modulation depth, 1.0 = 100% duration_sec, numsamples ): t = 0 dt = duration_sec / float(numsamples) match mode: case 'cw': for _ in range(numsamples): c = 2 * math.pi * t # generate cos(x), i sin(x) v = cmath.exp(complex(0,c)) * 128 i = int(v.real) % 256 q = int(v.imag) % 256 pipe.write( bytes([i,q])) t += dt case 'am': for _ in range(numsamples): c = 2 * math.pi * t # modulation value * modulation depth mv = ((1.0 + math.cos(c * mf) * md) * 0.5) # generate cos(x), i sin(x) v = cmath.exp(complex(0,c)) * 128 * mv i = int(v.real) % 256 q = int(v.imag) % 256 pipe.write( bytes([i,q])) t += dt case 'fm' | 'wfm': deviation = (0.2,0.012)[mode=='fm'] fmi = 0.0 # frequency modulation integral for _ in range(numsamples): c = 2 * math.pi * t # modulation value * modulation depth mv = math.cos(c * mf) * md # update FM modulation integral fmi += mv * deviation v = cmath.exp(complex(0,c + fmi)) * 128 i = int(v.real) % 256 q = int(v.imag) % 256 pipe.write( bytes([i,q])) t += dt case _: print(f'Error: no mode "{mode}".') def main(): # high_output = True : antenna connections # high_output = False : direct wired connections high_output = False if high_output: amp_enable=1 gain_db=45 else: amp_enable=0 gain_db=0 samplerate_hz = 2000000 # 2 MHz # tested 2024.03.14, closest integer clock_correction_hz=-6 duration_sec = 2 # seconds numsamples = int(samplerate_hz * duration_sec) freq_mhz = 3.0 # base frequency MHz freq_hz = freq_mhz * 1e6 mode = 'am' mf = 440 # modulation frequency md = 1.0 # modulation depth, 1.0 = 100% start = datetime.datetime.now() proc = open_hackrf(freq_hz,amp_enable,gain_db,samplerate_hz,clock_correction_hz) pipe = proc.stdin transmit(pipe,mode,mf,md,duration_sec,numsamples) # must terminate hackrf process proc.terminate() end = datetime.datetime.now() print(f"Generation time: {end-start}") if __name__ == '__main__': main()