├── README.md ├── pocsag_generator.py ├── pocsag_sender.py └── pocsagsend.grc /README.md: -------------------------------------------------------------------------------- 1 | # gr-pocsag: a gnuradio embedded python block for sending pocsag messages 2 | 3 | ## Description: 4 | This is the alpha test release of gr-pocsag, a embedded python block for generating and 5 | sending pocsag message. 6 | 7 | The gnu-radio companion example uses a hackRF to send a pocsag-message 8 | 9 | The block generates the bitpattern for a POCSAG-message, broadcasts it once and then going into a loop 10 | to shut down the transmitter module of the HackRF. 11 | 12 | The python code itself is located inside the pocsag_generator.py. 13 | 14 | ## Install: 15 | 16 | 1. Install GNU Radio like : https://wiki.gnuradio.org/index.php/InstallingGR 17 | 18 | 2. Install dependencies 19 | ``` 20 | pip install numpy 21 | pip install bitstring 22 | ``` 23 | 24 | ## Usage: 25 | 26 | 1. By import the pocsagsend.grc in GNU Radio. 27 | 2. By call ./pocsag_sender.py --RIC %RIC --SUBRIC %SUBRIC% --TEST %TEXT% (Please edit frequency in script) 28 | 29 | ## Release-information: 30 | 31 | 32 | + Version: 0.0.1 (20180826) 33 | + Version: 0.0.2 (20180828) 34 | + Version: 0.0.3 (20181006) --> 35 | + Version: 0.0.4 (20181022) --> Add some Improvments + Changed Parameters and Varaibles + Remove WXGUI 36 | 37 | 38 | 39 | 40 | (C) Tauebenuss 41 | (C) Kristoff Bonne - ON1ARF 42 | This code is released under the GPL v3 license. 43 | -------------------------------------------------------------------------------- /pocsag_generator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Embedded Python Blocks: 3 | 4 | Each this file is saved, GRC will instantiate the first class it finds to get 5 | ports and parameters of your block. The arguments to __init__ will be the 6 | parameters. All of them are required to have default values! 7 | 8 | 9 | """ 10 | #gr-pocsag: a gnuradio embedded python block for sending pocsag messages 11 | #----------------------------------------------------------------------- 12 | # 13 | # 14 | #This is the alpha test release of gr-pocsag, a embedded python block for generating and 15 | #sending pocsag message. 16 | # 17 | #The gnu-radio companion example uses a hackRF to send a pocsag-message on 433.900 Mhz. 18 | # 19 | # 20 | #The block generates the bitpattern for a POCSAG-message, broadcasts it once and then going into a loop 21 | #to shut down the transmitter module of the HackRF. 22 | # 23 | # 24 | #The python code itself is located inside the "pocsag generator" epy_block and as a seperate file "gr-pocsagsend.py". 25 | # 26 | # 27 | # (C) Kristoff Bonne, ON1ARF 28 | # GPL v3 29 | # 30 | #Release-information: 31 | #Version: 0.0.1 (20180826) 32 | #Version: 0.0.2 (20180828) 33 | #Version: 0.0.3 (20181006): correction idle pattern and changing "20 bits padding" pattern 34 | # 35 | # 73s . kristoff - ON1ARF 36 | 37 | 38 | import numpy as np 39 | from gnuradio import gr 40 | 41 | from bitstring import BitArray 42 | 43 | import time 44 | 45 | 46 | 47 | class pocsagsender(gr.sync_block): 48 | def __CalculateCRCandParity(self,datatype,data): 49 | cw=data<<11 50 | 51 | # make leftmost bit "1" for datatype 1 (text) 52 | if datatype == 1: 53 | cw |= 0x80000000 54 | #end if 55 | 56 | local_cw=cw 57 | 58 | for i in range(21): 59 | if (cw & 0x80000000) > 0: 60 | cw ^= 0xED200000 61 | #end if 62 | cw <<= 1 63 | #end for 64 | 65 | local_cw |= (cw >> 21) 66 | 67 | 68 | parity=0 69 | cw=local_cw 70 | for i in range(32): 71 | if (cw & 0x80000000) > 0: 72 | parity += 1 73 | #end if 74 | cw <<= 1 75 | #end for 76 | 77 | # make even parity 78 | local_cw += (parity % 2) 79 | 80 | return(local_cw) 81 | # end Calculate CRC and Pairy 82 | 83 | 84 | def __createpocsagmsg(self,address,source,txt): 85 | 86 | # checking input 87 | if not (0 < address <= 0x1fffff): 88 | raise ValueError(address) 89 | #end if 90 | 91 | if not (0 <= source <= 3): 92 | raise ValueError(source) 93 | #nd if 94 | 95 | 96 | if len(txt) == 0: 97 | raise ValueError(txt) 98 | #end if 99 | 100 | 101 | # if len(txt) >= 40: 102 | # txt=txt[:39] 103 | # print("Warning, text truncated to 39 characters: {txt}".format(txt=txt)) 104 | #end if 105 | 106 | # init pocsag message 107 | # init data 108 | # 2 batches 109 | # 1 batch = sync codeword + 8 frames 110 | # 1 frame = 1 codeword 111 | 112 | syncpattern = 0xAAAAAAAA 113 | synccodeword = 0x7cd215d8 114 | 115 | idlepattern = 0x7ac9c197 116 | 117 | # init all codewords with idle pattern: 2 batches = 16 frames = 32 codewords 118 | codeword = [idlepattern for i in range(32)] 119 | 120 | 121 | # part 1: address + source 122 | 123 | # parse address and source 124 | addressline=address>>3 125 | 126 | # add address-source 127 | addressline<<=2 128 | addressline += source 129 | # the message starts at the frame address determined by the last 3 bits of the address 130 | cwnum = ((address % 8) << 1) # codeword number 131 | 132 | codeword[cwnum]=self.__CalculateCRCandParity(datatype = 0, data = addressline) 133 | 134 | 135 | # part 2: text 136 | 137 | # 2.1 convert text into int, also add EOT char 138 | ts=[ord(c) for c in txt] + [0x04] 139 | 140 | # 2.2 make sure all characers are 7 bit 141 | ts=list(map(lambda x: x%128, ts)) 142 | 143 | # 2.3 create one big list of bits 144 | textbits='' 145 | 146 | for c in ts: 147 | # BitArray(uint=c,length=7).bin to convert character to 7-character bit-string) 148 | # note, for transmission, the bit-order must be reversed 149 | charbits = BitArray(uint=c,length=7) 150 | # reverse order of bits 151 | charbits.reverse() 152 | 153 | # add to total string 154 | textbits += charbits.bin 155 | #end for 156 | 157 | nbits=len(textbits) 158 | 159 | # 2.4 make the list of bits a multiple of 20 bits 160 | bitstoadd=20-(nbits % 20) 161 | 162 | if bitstoadd == 20: 163 | bitstoadd = 0 164 | #end if 165 | bitstoadd_rest = bitstoadd%2 166 | bitstoadd_2bit = bitstoadd >> 1 167 | textbits += '01' * bitstoadd_2bit 168 | if bitstoadd_rest == 1: 169 | textbits += '0' 170 | #end if 171 | 172 | 173 | # 2.5 for every block of 20 bits, calculate crc and partity, and add to codeword 174 | ncw = len(textbits)/20 # number of codewords 175 | 176 | startbit=0 177 | stopbit=20 # (actually, the 19th bit) 178 | for i in range(ncw): 179 | thiscw=textbits[startbit:stopbit] 180 | thiscw_i=int(thiscw,2) # convert list of bits to int 181 | 182 | #move up all pointers 183 | startbit=stopbit # stopbit is startbit + 20 184 | stopbit += 20 185 | 186 | #codeword pointer 187 | cwnum += 1 188 | 189 | # calculate CRC for a text block (datatype = 1) 190 | codeword[cwnum]=self.__CalculateCRCandParity(datatype = 1, data = thiscw_i) 191 | #end for (number of 192 | 193 | 194 | # done 195 | 196 | # now create complete pocsag message 197 | # sync pattern 198 | ret = [syncpattern for i in range(18)] 199 | 200 | # add sync codeword of 1st batch 201 | ret.append(synccodeword) 202 | 203 | # add frames 0 to 7 (i.e. codewords 0 to 15) 204 | ret += [codeword[n] for n in range(16)] 205 | 206 | 207 | 208 | # create add 2nd batch if the text has spilled into the 2nd batch 209 | if cwnum >= 16: 210 | # long message, 2 batches 211 | nbatch=2 212 | 213 | # add sync codeword 214 | ret.append(synccodeword) 215 | 216 | # and add frames 8 to 15 (i.e. codewords 16 to 31) 217 | ret += [codeword[n] for n in range(16,32)] 218 | else: 219 | # short message, 1 batch 220 | nbatch=1 221 | 222 | #end else - ifif 223 | 224 | 225 | return((nbatch,ret)) 226 | 227 | # end "createpocsagmsg" 228 | 229 | 230 | def __init__(self, number = 2060073, source = 0, sleeptime= 5, text="ON1ARF pocsag Python gnuradio"): # only default arguments here 231 | gr.sync_block.__init__( 232 | self, 233 | name='pocsag generator', 234 | in_sig=[], 235 | out_sig=[np.int8] 236 | ) 237 | 238 | 239 | self.set_output_multiple(640) 240 | # pocsag messages are either 1 batch (35 codeword = 120 octets), or 2 batches (52 codewords = 208 octets) 241 | # make the stream a little bit longer to fit a multiple of 20 codewords 242 | # pocsag message of 1 batch -> add 5 cw -> 40 (= 20 * 2) codewords 243 | # pocsag message of 2 batches -> add 8 cw -> 60 (=20 *3) codewords 244 | # 20 cw = 80 octets = 640 bits 245 | 246 | self.state = 0 247 | self.sleeptime = sleeptime 248 | self.number= int(number) 249 | self.source = int(source) 250 | 251 | 252 | nbatch,psmsg=self.__createpocsagmsg(number,source,text) 253 | 254 | # for a short message (nbtach=1), add 5 octest = 40 bits 255 | # for a long message (nbatch=2), add 8 octets = 64 bits 256 | 257 | if nbatch == 1: 258 | self.pocsagmsg=[0 for i in range(20)] 259 | elif nbatch == 2: 260 | self.pocsagmsg=[0 for i in range(32)] 261 | else: 262 | raise ValueError(nbatch) 263 | #end if 264 | 265 | 266 | for thismsg in psmsg: 267 | for c in BitArray(uint=thismsg,length=32).bin: 268 | self.pocsagmsg.append(1 if c == '1' else -1) 269 | #end for 270 | 271 | # now add tail (all 0) 272 | if nbatch == 1: 273 | self.pocsagmsg+=[0 for i in range(20)] 274 | elif nbatch == 2: 275 | self.pocsagmsg+=[0 for i in range(32)] 276 | else: 277 | raise ValueError(nbatch) 278 | #end if 279 | 280 | self.msglen=len(self.pocsagmsg) 281 | 282 | #end __init__ 283 | 284 | #def forecast(self, noutput_items, ninput_items_required): 285 | # #setup size of input_items[i] for work call 286 | # for i in range(len(ninput_items_required)): 287 | # ninput_items_required[i] = noutput_items 288 | 289 | def work(self, input_items, output_items): 290 | 291 | if self.state == 0: 292 | # state 0 -> send pocsag message 293 | 294 | output_items[0][:self.msglen]=np.array(self.pocsagmsg, dtype=np.int8) 295 | 296 | self.state = 1 # after this, go to sleep 297 | return self.msglen 298 | #end if 299 | 300 | # state 1: sleep 301 | #time.sleep(self.sleeptime) 302 | 303 | # then go back to state 0 (transmit) 304 | self.state = 0 305 | 306 | return -1 # return without any data 307 | #end work 308 | # end class "pocsagsender" 309 | -------------------------------------------------------------------------------- /pocsag_sender.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | ################################################## 4 | # GNU Radio Python Flow Graph 5 | # Title: POCSAG Sender via HackRF 6 | # Author: ON1ARF & Tauebenuss 7 | # Description: Sending Pocsag Messages via HACKRF One 8 | # Generated: Mon Oct 22 21:00:22 2018 9 | ################################################## 10 | 11 | 12 | from gnuradio import analog 13 | from gnuradio import blocks 14 | from gnuradio import eng_notation 15 | from gnuradio import gr 16 | from gnuradio.eng_option import eng_option 17 | from gnuradio.filter import firdes 18 | from gnuradio.filter import pfb 19 | from optparse import OptionParser 20 | import math 21 | import osmosdr 22 | import pocsag_generator 23 | import time 24 | 25 | 26 | class pocsag_sender(gr.top_block): 27 | 28 | def __init__(self, RIC=1122551, SubRIC=0, Text='Testmessage by HACRF One'): 29 | gr.top_block.__init__(self, "POCSAG Sender via HackRF") 30 | 31 | ################################################## 32 | # Parameters 33 | ################################################## 34 | self.RIC = RIC 35 | self.SubRIC = SubRIC 36 | self.Text = Text 37 | 38 | ################################################## 39 | # Variables 40 | ################################################## 41 | self.tx_gain = tx_gain = 3 42 | self.symrate = symrate = 38400 43 | self.samp_rate = samp_rate = 12000000 44 | self.pocsagbitrate = pocsagbitrate = 1200 45 | self.pagerfreq = pagerfreq = 100880000 46 | self.max_deviation = max_deviation = 4500.0 47 | self.af_gain = af_gain = 190 48 | 49 | ################################################## 50 | # Blocks 51 | ################################################## 52 | self.pocsag_generator = pocsag_generator.pocsagsender(number=RIC, source=SubRIC, sleeptime=5, text=Text) 53 | self.pfb_arb_resampler_xxx_0 = pfb.arb_resampler_ccf( 54 | float(samp_rate)/float(symrate), 55 | taps=None, 56 | flt_size=16) 57 | self.pfb_arb_resampler_xxx_0.declare_sample_delay(0) 58 | 59 | self.osmosdr_sink_0 = osmosdr.sink( args="numchan=" + str(1) + " " + 'hackrf' ) 60 | self.osmosdr_sink_0.set_sample_rate(samp_rate) 61 | self.osmosdr_sink_0.set_center_freq(pagerfreq, 0) 62 | self.osmosdr_sink_0.set_freq_corr(0, 0) 63 | self.osmosdr_sink_0.set_gain(0, 0) 64 | self.osmosdr_sink_0.set_if_gain(tx_gain, 0) 65 | self.osmosdr_sink_0.set_bb_gain(20, 0) 66 | self.osmosdr_sink_0.set_antenna('', 0) 67 | self.osmosdr_sink_0.set_bandwidth(0, 0) 68 | 69 | self.blocks_repeat_0 = blocks.repeat(gr.sizeof_char*1, symrate/pocsagbitrate) 70 | self.blocks_multiply_const_vxx_0 = blocks.multiply_const_vcc((af_gain/100, )) 71 | self.blocks_char_to_float_0 = blocks.char_to_float(1, af_gain*0.7/1000) 72 | self.analog_frequency_modulator_fc_0 = analog.frequency_modulator_fc(2.0 * math.pi * max_deviation / float(symrate)) 73 | 74 | ################################################## 75 | # Connections 76 | ################################################## 77 | self.connect((self.analog_frequency_modulator_fc_0, 0), (self.blocks_multiply_const_vxx_0, 0)) 78 | self.connect((self.blocks_char_to_float_0, 0), (self.analog_frequency_modulator_fc_0, 0)) 79 | self.connect((self.blocks_multiply_const_vxx_0, 0), (self.pfb_arb_resampler_xxx_0, 0)) 80 | self.connect((self.blocks_repeat_0, 0), (self.blocks_char_to_float_0, 0)) 81 | self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.osmosdr_sink_0, 0)) 82 | self.connect((self.pocsag_generator, 0), (self.blocks_repeat_0, 0)) 83 | 84 | def get_RIC(self): 85 | return self.RIC 86 | 87 | def set_RIC(self, RIC): 88 | self.RIC = RIC 89 | self.pocsag_generator.number = self.RIC 90 | 91 | def get_SubRIC(self): 92 | return self.SubRIC 93 | 94 | def set_SubRIC(self, SubRIC): 95 | self.SubRIC = SubRIC 96 | self.pocsag_generator.source = self.SubRIC 97 | 98 | def get_Text(self): 99 | return self.Text 100 | 101 | def set_Text(self, Text): 102 | self.Text = Text 103 | 104 | def get_tx_gain(self): 105 | return self.tx_gain 106 | 107 | def set_tx_gain(self, tx_gain): 108 | self.tx_gain = tx_gain 109 | self.osmosdr_sink_0.set_if_gain(self.tx_gain, 0) 110 | 111 | def get_symrate(self): 112 | return self.symrate 113 | 114 | def set_symrate(self, symrate): 115 | self.symrate = symrate 116 | self.pfb_arb_resampler_xxx_0.set_rate(float(self.samp_rate)/float(self.symrate)) 117 | self.blocks_repeat_0.set_interpolation(self.symrate/self.pocsagbitrate) 118 | self.analog_frequency_modulator_fc_0.set_sensitivity(2.0 * math.pi * self.max_deviation / float(self.symrate)) 119 | 120 | def get_samp_rate(self): 121 | return self.samp_rate 122 | 123 | def set_samp_rate(self, samp_rate): 124 | self.samp_rate = samp_rate 125 | self.pfb_arb_resampler_xxx_0.set_rate(float(self.samp_rate)/float(self.symrate)) 126 | self.osmosdr_sink_0.set_sample_rate(self.samp_rate) 127 | 128 | def get_pocsagbitrate(self): 129 | return self.pocsagbitrate 130 | 131 | def set_pocsagbitrate(self, pocsagbitrate): 132 | self.pocsagbitrate = pocsagbitrate 133 | self.blocks_repeat_0.set_interpolation(self.symrate/self.pocsagbitrate) 134 | 135 | def get_pagerfreq(self): 136 | return self.pagerfreq 137 | 138 | def set_pagerfreq(self, pagerfreq): 139 | self.pagerfreq = pagerfreq 140 | self.osmosdr_sink_0.set_center_freq(self.pagerfreq, 0) 141 | 142 | def get_max_deviation(self): 143 | return self.max_deviation 144 | 145 | def set_max_deviation(self, max_deviation): 146 | self.max_deviation = max_deviation 147 | self.analog_frequency_modulator_fc_0.set_sensitivity(2.0 * math.pi * self.max_deviation / float(self.symrate)) 148 | 149 | def get_af_gain(self): 150 | return self.af_gain 151 | 152 | def set_af_gain(self, af_gain): 153 | self.af_gain = af_gain 154 | self.blocks_multiply_const_vxx_0.set_k((self.af_gain/100, )) 155 | self.blocks_char_to_float_0.set_scale(self.af_gain*0.7/1000) 156 | 157 | 158 | def argument_parser(): 159 | description = 'Sending Pocsag Messages via HACKRF One' 160 | parser = OptionParser(usage="%prog: [options]", option_class=eng_option, description=description) 161 | parser.add_option( 162 | "", "--RIC", dest="RIC", type="intx", default=1122551, 163 | help="Set RIC [default=%default]") 164 | parser.add_option( 165 | "", "--SubRIC", dest="SubRIC", type="intx", default=0, 166 | help="Set SubRIC [default=%default]") 167 | parser.add_option( 168 | "", "--Text", dest="Text", type="string", default='Testmessage by HACRF One', 169 | help="Set Testmessage by HACRF One [default=%default]") 170 | return parser 171 | 172 | 173 | def main(top_block_cls=pocsag_sender, options=None): 174 | if options is None: 175 | options, _ = argument_parser().parse_args() 176 | 177 | tb = top_block_cls(RIC=options.RIC, SubRIC=options.SubRIC, Text=options.Text) 178 | tb.start() 179 | tb.wait() 180 | 181 | 182 | if __name__ == '__main__': 183 | main() 184 | -------------------------------------------------------------------------------- /pocsagsend.grc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tue Aug 14 22:09:38 2018 5 | 6 | options 7 | 8 | author 9 | ON1ARF & Tauebenuss 10 | 11 | 12 | window_size 13 | 14 | 15 | 16 | category 17 | Custom 18 | 19 | 20 | comment 21 | 22 | 23 | 24 | description 25 | Sending Pocsag Messages via HACKRF One 26 | 27 | 28 | _enabled 29 | True 30 | 31 | 32 | _coordinate 33 | (9, 9) 34 | 35 | 36 | _rotation 37 | 0 38 | 39 | 40 | generate_options 41 | no_gui 42 | 43 | 44 | hier_block_src_path 45 | .: 46 | 47 | 48 | id 49 | pocsag_sender 50 | 51 | 52 | max_nouts 53 | 0 54 | 55 | 56 | qt_qss_theme 57 | 58 | 59 | 60 | realtime_scheduling 61 | 62 | 63 | 64 | run_command 65 | {python} -u {filename} 66 | 67 | 68 | run_options 69 | run 70 | 71 | 72 | run 73 | True 74 | 75 | 76 | thread_safe_setters 77 | 78 | 79 | 80 | title 81 | POCSAG Sender via HackRF 82 | 83 | 84 | 85 | variable 86 | 87 | comment 88 | 89 | 90 | 91 | _enabled 92 | True 93 | 94 | 95 | _coordinate 96 | (1106, 23) 97 | 98 | 99 | _rotation 100 | 0 101 | 102 | 103 | id 104 | af_gain 105 | 106 | 107 | value 108 | 190 109 | 110 | 111 | 112 | variable 113 | 114 | comment 115 | 116 | 117 | 118 | _enabled 119 | True 120 | 121 | 122 | _coordinate 123 | (400, 23) 124 | 125 | 126 | _rotation 127 | 0 128 | 129 | 130 | id 131 | max_deviation 132 | 133 | 134 | value 135 | 4500.0 136 | 137 | 138 | 139 | variable 140 | 141 | comment 142 | 143 | 144 | 145 | _enabled 146 | True 147 | 148 | 149 | _coordinate 150 | (725, 23) 151 | 152 | 153 | _rotation 154 | 0 155 | 156 | 157 | id 158 | pagerfreq 159 | 160 | 161 | value 162 | 100880000 163 | 164 | 165 | 166 | variable 167 | 168 | comment 169 | 170 | 171 | 172 | _enabled 173 | True 174 | 175 | 176 | _coordinate 177 | (837, 23) 178 | 179 | 180 | _rotation 181 | 0 182 | 183 | 184 | id 185 | pocsagbitrate 186 | 187 | 188 | value 189 | 1200 190 | 191 | 192 | 193 | variable 194 | 195 | comment 196 | 197 | 198 | 199 | _enabled 200 | True 201 | 202 | 203 | _coordinate 204 | (624, 23) 205 | 206 | 207 | _rotation 208 | 0 209 | 210 | 211 | id 212 | samp_rate 213 | 214 | 215 | value 216 | 12000000 217 | 218 | 219 | 220 | variable 221 | 222 | comment 223 | 224 | 225 | 226 | _enabled 227 | True 228 | 229 | 230 | _coordinate 231 | (530, 23) 232 | 233 | 234 | _rotation 235 | 0 236 | 237 | 238 | id 239 | symrate 240 | 241 | 242 | value 243 | 38400 244 | 245 | 246 | 247 | variable 248 | 249 | comment 250 | 251 | 252 | 253 | _enabled 254 | True 255 | 256 | 257 | _coordinate 258 | (976, 23) 259 | 260 | 261 | _rotation 262 | 0 263 | 264 | 265 | id 266 | tx_gain 267 | 268 | 269 | value 270 | 3 271 | 272 | 273 | 274 | parameter 275 | 276 | alias 277 | 278 | 279 | 280 | comment 281 | 282 | 283 | 284 | _enabled 285 | True 286 | 287 | 288 | _coordinate 289 | (18, 186) 290 | 291 | 292 | _rotation 293 | 0 294 | 295 | 296 | id 297 | RIC 298 | 299 | 300 | label 301 | RIC 302 | 303 | 304 | short_id 305 | 306 | 307 | 308 | type 309 | intx 310 | 311 | 312 | value 313 | 1122551 314 | 315 | 316 | 317 | parameter 318 | 319 | alias 320 | 321 | 322 | 323 | comment 324 | 325 | 326 | 327 | _enabled 328 | True 329 | 330 | 331 | _coordinate 332 | (18, 288) 333 | 334 | 335 | _rotation 336 | 0 337 | 338 | 339 | id 340 | SubRIC 341 | 342 | 343 | label 344 | SubRIC 345 | 346 | 347 | short_id 348 | 349 | 350 | 351 | type 352 | intx 353 | 354 | 355 | value 356 | 0 357 | 358 | 359 | 360 | parameter 361 | 362 | alias 363 | 364 | 365 | 366 | comment 367 | 368 | 369 | 370 | _enabled 371 | True 372 | 373 | 374 | _coordinate 375 | (18, 390) 376 | 377 | 378 | _rotation 379 | 0 380 | 381 | 382 | id 383 | Text 384 | 385 | 386 | label 387 | Text 388 | 389 | 390 | short_id 391 | 392 | 393 | 394 | type 395 | string 396 | 397 | 398 | value 399 | Testmessage by HACRF One 400 | 401 | 402 | 403 | analog_frequency_modulator_fc 404 | 405 | alias 406 | 407 | 408 | 409 | comment 410 | 411 | 412 | 413 | affinity 414 | 415 | 416 | 417 | _enabled 418 | 1 419 | 420 | 421 | _coordinate 422 | (1060, 144) 423 | 424 | 425 | _rotation 426 | 0 427 | 428 | 429 | id 430 | analog_frequency_modulator_fc_0 431 | 432 | 433 | maxoutbuf 434 | 0 435 | 436 | 437 | minoutbuf 438 | 0 439 | 440 | 441 | sensitivity 442 | 2.0 * math.pi * max_deviation / float(symrate) 443 | 444 | 445 | 446 | blocks_char_to_float 447 | 448 | alias 449 | 450 | 451 | 452 | comment 453 | 454 | 455 | 456 | affinity 457 | 458 | 459 | 460 | _enabled 461 | True 462 | 463 | 464 | _coordinate 465 | (902, 144) 466 | 467 | 468 | _rotation 469 | 0 470 | 471 | 472 | id 473 | blocks_char_to_float_0 474 | 475 | 476 | maxoutbuf 477 | 0 478 | 479 | 480 | minoutbuf 481 | 0 482 | 483 | 484 | scale 485 | af_gain*0.7/1000 486 | 487 | 488 | vlen 489 | 1 490 | 491 | 492 | 493 | blocks_multiply_const_vxx 494 | 495 | alias 496 | 497 | 498 | 499 | comment 500 | 501 | 502 | 503 | const 504 | af_gain/100 505 | 506 | 507 | affinity 508 | 509 | 510 | 511 | _enabled 512 | 1 513 | 514 | 515 | _coordinate 516 | (539, 320) 517 | 518 | 519 | _rotation 520 | 0 521 | 522 | 523 | id 524 | blocks_multiply_const_vxx_0 525 | 526 | 527 | type 528 | complex 529 | 530 | 531 | maxoutbuf 532 | 0 533 | 534 | 535 | minoutbuf 536 | 0 537 | 538 | 539 | vlen 540 | 1 541 | 542 | 543 | 544 | blocks_repeat 545 | 546 | alias 547 | 548 | 549 | 550 | comment 551 | 552 | 553 | 554 | affinity 555 | 556 | 557 | 558 | _enabled 559 | True 560 | 561 | 562 | _coordinate 563 | (697, 144) 564 | 565 | 566 | _rotation 567 | 0 568 | 569 | 570 | id 571 | blocks_repeat_0 572 | 573 | 574 | interp 575 | symrate/pocsagbitrate 576 | 577 | 578 | maxoutbuf 579 | 0 580 | 581 | 582 | minoutbuf 583 | 0 584 | 585 | 586 | type 587 | byte 588 | 589 | 590 | vlen 591 | 1 592 | 593 | 594 | 595 | import 596 | 597 | alias 598 | 599 | 600 | 601 | comment 602 | 603 | 604 | 605 | _enabled 606 | True 607 | 608 | 609 | _coordinate 610 | (297, 23) 611 | 612 | 613 | _rotation 614 | 0 615 | 616 | 617 | id 618 | import_0 619 | 620 | 621 | import 622 | import math 623 | 624 | 625 | 626 | osmosdr_sink 627 | 628 | alias 629 | 630 | 631 | 632 | ant0 633 | 634 | 635 | 636 | bb_gain0 637 | 20 638 | 639 | 640 | bw0 641 | 0 642 | 643 | 644 | corr0 645 | 0 646 | 647 | 648 | freq0 649 | pagerfreq 650 | 651 | 652 | if_gain0 653 | tx_gain 654 | 655 | 656 | gain0 657 | 0 658 | 659 | 660 | ant10 661 | 662 | 663 | 664 | bb_gain10 665 | 20 666 | 667 | 668 | bw10 669 | 0 670 | 671 | 672 | corr10 673 | 0 674 | 675 | 676 | freq10 677 | 100e6 678 | 679 | 680 | if_gain10 681 | 20 682 | 683 | 684 | gain10 685 | 10 686 | 687 | 688 | ant11 689 | 690 | 691 | 692 | bb_gain11 693 | 20 694 | 695 | 696 | bw11 697 | 0 698 | 699 | 700 | corr11 701 | 0 702 | 703 | 704 | freq11 705 | 100e6 706 | 707 | 708 | if_gain11 709 | 20 710 | 711 | 712 | gain11 713 | 10 714 | 715 | 716 | ant12 717 | 718 | 719 | 720 | bb_gain12 721 | 20 722 | 723 | 724 | bw12 725 | 0 726 | 727 | 728 | corr12 729 | 0 730 | 731 | 732 | freq12 733 | 100e6 734 | 735 | 736 | if_gain12 737 | 20 738 | 739 | 740 | gain12 741 | 10 742 | 743 | 744 | ant13 745 | 746 | 747 | 748 | bb_gain13 749 | 20 750 | 751 | 752 | bw13 753 | 0 754 | 755 | 756 | corr13 757 | 0 758 | 759 | 760 | freq13 761 | 100e6 762 | 763 | 764 | if_gain13 765 | 20 766 | 767 | 768 | gain13 769 | 10 770 | 771 | 772 | ant14 773 | 774 | 775 | 776 | bb_gain14 777 | 20 778 | 779 | 780 | bw14 781 | 0 782 | 783 | 784 | corr14 785 | 0 786 | 787 | 788 | freq14 789 | 100e6 790 | 791 | 792 | if_gain14 793 | 20 794 | 795 | 796 | gain14 797 | 10 798 | 799 | 800 | ant15 801 | 802 | 803 | 804 | bb_gain15 805 | 20 806 | 807 | 808 | bw15 809 | 0 810 | 811 | 812 | corr15 813 | 0 814 | 815 | 816 | freq15 817 | 100e6 818 | 819 | 820 | if_gain15 821 | 20 822 | 823 | 824 | gain15 825 | 10 826 | 827 | 828 | ant16 829 | 830 | 831 | 832 | bb_gain16 833 | 20 834 | 835 | 836 | bw16 837 | 0 838 | 839 | 840 | corr16 841 | 0 842 | 843 | 844 | freq16 845 | 100e6 846 | 847 | 848 | if_gain16 849 | 20 850 | 851 | 852 | gain16 853 | 10 854 | 855 | 856 | ant17 857 | 858 | 859 | 860 | bb_gain17 861 | 20 862 | 863 | 864 | bw17 865 | 0 866 | 867 | 868 | corr17 869 | 0 870 | 871 | 872 | freq17 873 | 100e6 874 | 875 | 876 | if_gain17 877 | 20 878 | 879 | 880 | gain17 881 | 10 882 | 883 | 884 | ant18 885 | 886 | 887 | 888 | bb_gain18 889 | 20 890 | 891 | 892 | bw18 893 | 0 894 | 895 | 896 | corr18 897 | 0 898 | 899 | 900 | freq18 901 | 100e6 902 | 903 | 904 | if_gain18 905 | 20 906 | 907 | 908 | gain18 909 | 10 910 | 911 | 912 | ant19 913 | 914 | 915 | 916 | bb_gain19 917 | 20 918 | 919 | 920 | bw19 921 | 0 922 | 923 | 924 | corr19 925 | 0 926 | 927 | 928 | freq19 929 | 100e6 930 | 931 | 932 | if_gain19 933 | 20 934 | 935 | 936 | gain19 937 | 10 938 | 939 | 940 | ant1 941 | 942 | 943 | 944 | bb_gain1 945 | 20 946 | 947 | 948 | bw1 949 | 0 950 | 951 | 952 | corr1 953 | 0 954 | 955 | 956 | freq1 957 | 100e6 958 | 959 | 960 | if_gain1 961 | 20 962 | 963 | 964 | gain1 965 | 10 966 | 967 | 968 | ant20 969 | 970 | 971 | 972 | bb_gain20 973 | 20 974 | 975 | 976 | bw20 977 | 0 978 | 979 | 980 | corr20 981 | 0 982 | 983 | 984 | freq20 985 | 100e6 986 | 987 | 988 | if_gain20 989 | 20 990 | 991 | 992 | gain20 993 | 10 994 | 995 | 996 | ant21 997 | 998 | 999 | 1000 | bb_gain21 1001 | 20 1002 | 1003 | 1004 | bw21 1005 | 0 1006 | 1007 | 1008 | corr21 1009 | 0 1010 | 1011 | 1012 | freq21 1013 | 100e6 1014 | 1015 | 1016 | if_gain21 1017 | 20 1018 | 1019 | 1020 | gain21 1021 | 10 1022 | 1023 | 1024 | ant22 1025 | 1026 | 1027 | 1028 | bb_gain22 1029 | 20 1030 | 1031 | 1032 | bw22 1033 | 0 1034 | 1035 | 1036 | corr22 1037 | 0 1038 | 1039 | 1040 | freq22 1041 | 100e6 1042 | 1043 | 1044 | if_gain22 1045 | 20 1046 | 1047 | 1048 | gain22 1049 | 10 1050 | 1051 | 1052 | ant23 1053 | 1054 | 1055 | 1056 | bb_gain23 1057 | 20 1058 | 1059 | 1060 | bw23 1061 | 0 1062 | 1063 | 1064 | corr23 1065 | 0 1066 | 1067 | 1068 | freq23 1069 | 100e6 1070 | 1071 | 1072 | if_gain23 1073 | 20 1074 | 1075 | 1076 | gain23 1077 | 10 1078 | 1079 | 1080 | ant24 1081 | 1082 | 1083 | 1084 | bb_gain24 1085 | 20 1086 | 1087 | 1088 | bw24 1089 | 0 1090 | 1091 | 1092 | corr24 1093 | 0 1094 | 1095 | 1096 | freq24 1097 | 100e6 1098 | 1099 | 1100 | if_gain24 1101 | 20 1102 | 1103 | 1104 | gain24 1105 | 10 1106 | 1107 | 1108 | ant25 1109 | 1110 | 1111 | 1112 | bb_gain25 1113 | 20 1114 | 1115 | 1116 | bw25 1117 | 0 1118 | 1119 | 1120 | corr25 1121 | 0 1122 | 1123 | 1124 | freq25 1125 | 100e6 1126 | 1127 | 1128 | if_gain25 1129 | 20 1130 | 1131 | 1132 | gain25 1133 | 10 1134 | 1135 | 1136 | ant26 1137 | 1138 | 1139 | 1140 | bb_gain26 1141 | 20 1142 | 1143 | 1144 | bw26 1145 | 0 1146 | 1147 | 1148 | corr26 1149 | 0 1150 | 1151 | 1152 | freq26 1153 | 100e6 1154 | 1155 | 1156 | if_gain26 1157 | 20 1158 | 1159 | 1160 | gain26 1161 | 10 1162 | 1163 | 1164 | ant27 1165 | 1166 | 1167 | 1168 | bb_gain27 1169 | 20 1170 | 1171 | 1172 | bw27 1173 | 0 1174 | 1175 | 1176 | corr27 1177 | 0 1178 | 1179 | 1180 | freq27 1181 | 100e6 1182 | 1183 | 1184 | if_gain27 1185 | 20 1186 | 1187 | 1188 | gain27 1189 | 10 1190 | 1191 | 1192 | ant28 1193 | 1194 | 1195 | 1196 | bb_gain28 1197 | 20 1198 | 1199 | 1200 | bw28 1201 | 0 1202 | 1203 | 1204 | corr28 1205 | 0 1206 | 1207 | 1208 | freq28 1209 | 100e6 1210 | 1211 | 1212 | if_gain28 1213 | 20 1214 | 1215 | 1216 | gain28 1217 | 10 1218 | 1219 | 1220 | ant29 1221 | 1222 | 1223 | 1224 | bb_gain29 1225 | 20 1226 | 1227 | 1228 | bw29 1229 | 0 1230 | 1231 | 1232 | corr29 1233 | 0 1234 | 1235 | 1236 | freq29 1237 | 100e6 1238 | 1239 | 1240 | if_gain29 1241 | 20 1242 | 1243 | 1244 | gain29 1245 | 10 1246 | 1247 | 1248 | ant2 1249 | 1250 | 1251 | 1252 | bb_gain2 1253 | 20 1254 | 1255 | 1256 | bw2 1257 | 0 1258 | 1259 | 1260 | corr2 1261 | 0 1262 | 1263 | 1264 | freq2 1265 | 100e6 1266 | 1267 | 1268 | if_gain2 1269 | 20 1270 | 1271 | 1272 | gain2 1273 | 10 1274 | 1275 | 1276 | ant30 1277 | 1278 | 1279 | 1280 | bb_gain30 1281 | 20 1282 | 1283 | 1284 | bw30 1285 | 0 1286 | 1287 | 1288 | corr30 1289 | 0 1290 | 1291 | 1292 | freq30 1293 | 100e6 1294 | 1295 | 1296 | if_gain30 1297 | 20 1298 | 1299 | 1300 | gain30 1301 | 10 1302 | 1303 | 1304 | ant31 1305 | 1306 | 1307 | 1308 | bb_gain31 1309 | 20 1310 | 1311 | 1312 | bw31 1313 | 0 1314 | 1315 | 1316 | corr31 1317 | 0 1318 | 1319 | 1320 | freq31 1321 | 100e6 1322 | 1323 | 1324 | if_gain31 1325 | 20 1326 | 1327 | 1328 | gain31 1329 | 10 1330 | 1331 | 1332 | ant3 1333 | 1334 | 1335 | 1336 | bb_gain3 1337 | 20 1338 | 1339 | 1340 | bw3 1341 | 0 1342 | 1343 | 1344 | corr3 1345 | 0 1346 | 1347 | 1348 | freq3 1349 | 100e6 1350 | 1351 | 1352 | if_gain3 1353 | 20 1354 | 1355 | 1356 | gain3 1357 | 10 1358 | 1359 | 1360 | ant4 1361 | 1362 | 1363 | 1364 | bb_gain4 1365 | 20 1366 | 1367 | 1368 | bw4 1369 | 0 1370 | 1371 | 1372 | corr4 1373 | 0 1374 | 1375 | 1376 | freq4 1377 | 100e6 1378 | 1379 | 1380 | if_gain4 1381 | 20 1382 | 1383 | 1384 | gain4 1385 | 10 1386 | 1387 | 1388 | ant5 1389 | 1390 | 1391 | 1392 | bb_gain5 1393 | 20 1394 | 1395 | 1396 | bw5 1397 | 0 1398 | 1399 | 1400 | corr5 1401 | 0 1402 | 1403 | 1404 | freq5 1405 | 100e6 1406 | 1407 | 1408 | if_gain5 1409 | 20 1410 | 1411 | 1412 | gain5 1413 | 10 1414 | 1415 | 1416 | ant6 1417 | 1418 | 1419 | 1420 | bb_gain6 1421 | 20 1422 | 1423 | 1424 | bw6 1425 | 0 1426 | 1427 | 1428 | corr6 1429 | 0 1430 | 1431 | 1432 | freq6 1433 | 100e6 1434 | 1435 | 1436 | if_gain6 1437 | 20 1438 | 1439 | 1440 | gain6 1441 | 10 1442 | 1443 | 1444 | ant7 1445 | 1446 | 1447 | 1448 | bb_gain7 1449 | 20 1450 | 1451 | 1452 | bw7 1453 | 0 1454 | 1455 | 1456 | corr7 1457 | 0 1458 | 1459 | 1460 | freq7 1461 | 100e6 1462 | 1463 | 1464 | if_gain7 1465 | 20 1466 | 1467 | 1468 | gain7 1469 | 10 1470 | 1471 | 1472 | ant8 1473 | 1474 | 1475 | 1476 | bb_gain8 1477 | 20 1478 | 1479 | 1480 | bw8 1481 | 0 1482 | 1483 | 1484 | corr8 1485 | 0 1486 | 1487 | 1488 | freq8 1489 | 100e6 1490 | 1491 | 1492 | if_gain8 1493 | 20 1494 | 1495 | 1496 | gain8 1497 | 10 1498 | 1499 | 1500 | ant9 1501 | 1502 | 1503 | 1504 | bb_gain9 1505 | 20 1506 | 1507 | 1508 | bw9 1509 | 0 1510 | 1511 | 1512 | corr9 1513 | 0 1514 | 1515 | 1516 | freq9 1517 | 100e6 1518 | 1519 | 1520 | if_gain9 1521 | 20 1522 | 1523 | 1524 | gain9 1525 | 10 1526 | 1527 | 1528 | comment 1529 | 1530 | 1531 | 1532 | affinity 1533 | 1534 | 1535 | 1536 | args 1537 | hackrf 1538 | 1539 | 1540 | _enabled 1541 | 1 1542 | 1543 | 1544 | _coordinate 1545 | (1013, 269) 1546 | 1547 | 1548 | _rotation 1549 | 0 1550 | 1551 | 1552 | id 1553 | osmosdr_sink_0 1554 | 1555 | 1556 | type 1557 | fc32 1558 | 1559 | 1560 | clock_source0 1561 | 1562 | 1563 | 1564 | time_source0 1565 | 1566 | 1567 | 1568 | clock_source1 1569 | 1570 | 1571 | 1572 | time_source1 1573 | 1574 | 1575 | 1576 | clock_source2 1577 | 1578 | 1579 | 1580 | time_source2 1581 | 1582 | 1583 | 1584 | clock_source3 1585 | 1586 | 1587 | 1588 | time_source3 1589 | 1590 | 1591 | 1592 | clock_source4 1593 | 1594 | 1595 | 1596 | time_source4 1597 | 1598 | 1599 | 1600 | clock_source5 1601 | 1602 | 1603 | 1604 | time_source5 1605 | 1606 | 1607 | 1608 | clock_source6 1609 | 1610 | 1611 | 1612 | time_source6 1613 | 1614 | 1615 | 1616 | clock_source7 1617 | 1618 | 1619 | 1620 | time_source7 1621 | 1622 | 1623 | 1624 | nchan 1625 | 1 1626 | 1627 | 1628 | num_mboards 1629 | 1 1630 | 1631 | 1632 | sample_rate 1633 | samp_rate 1634 | 1635 | 1636 | sync 1637 | 1638 | 1639 | 1640 | 1641 | pfb_arb_resampler_xxx 1642 | 1643 | alias 1644 | 1645 | 1646 | 1647 | comment 1648 | 1649 | 1650 | 1651 | affinity 1652 | 1653 | 1654 | 1655 | _enabled 1656 | 1 1657 | 1658 | 1659 | _coordinate 1660 | (706, 297) 1661 | 1662 | 1663 | _rotation 1664 | 0 1665 | 1666 | 1667 | id 1668 | pfb_arb_resampler_xxx_0 1669 | 1670 | 1671 | maxoutbuf 1672 | 0 1673 | 1674 | 1675 | minoutbuf 1676 | 0 1677 | 1678 | 1679 | nfilts 1680 | 16 1681 | 1682 | 1683 | rrate 1684 | float(samp_rate)/float(symrate) 1685 | 1686 | 1687 | samp_delay 1688 | 0 1689 | 1690 | 1691 | atten 1692 | 100 1693 | 1694 | 1695 | taps 1696 | 1697 | 1698 | 1699 | type 1700 | ccf 1701 | 1702 | 1703 | 1704 | epy_block 1705 | 1706 | alias 1707 | 1708 | 1709 | 1710 | _io_cache 1711 | ('pocsag generator', 'pocsagsender', [('number', '2060073'), ('source', '0'), ('sleeptime', '5'), ('text', "'ON1ARF pocsag Python gnuradio'")], [], [('0', 'byte', 1)], '', ['number', 'sleeptime', 'source']) 1712 | 1713 | 1714 | _source_code 1715 | """ 1716 | Embedded Python Blocks: 1717 | 1718 | Each this file is saved, GRC will instantiate the first class it finds to get 1719 | ports and parameters of your block. The arguments to __init__ will be the 1720 | parameters. All of them are required to have default values! 1721 | 1722 | 1723 | """ 1724 | #gr-pocsag: a gnuradio embedded python block for sending pocsag messages 1725 | #----------------------------------------------------------------------- 1726 | # 1727 | # 1728 | #This is the alpha test release of gr-pocsag, a embedded python block for generating and 1729 | #sending pocsag message. 1730 | # 1731 | #The gnu-radio companion example uses a hackRF to send a pocsag-message on 433.900 Mhz. 1732 | # 1733 | # 1734 | #The block generates the bitpattern for a POCSAG-message, broadcasts it once and then going into a loop 1735 | #to shut down the transmitter module of the HackRF. 1736 | # 1737 | # 1738 | #The python code itself is located inside the "pocsag generator" epy_block and as a seperate file "gr-pocsagsend.py". 1739 | # 1740 | # 1741 | # (C) Kristoff Bonne, ON1ARF 1742 | # GPL v3 1743 | # 1744 | #Release-information: 1745 | #Version: 0.0.1 (20180826) 1746 | #Version: 0.0.2 (20180828) 1747 | #Version: 0.0.3 (20181006): correction idle pattern and changing "20 bits padding" pattern 1748 | # 1749 | # 73s . kristoff - ON1ARF 1750 | 1751 | 1752 | import numpy as np 1753 | from gnuradio import gr 1754 | 1755 | from bitstring import BitArray 1756 | 1757 | import time 1758 | 1759 | 1760 | 1761 | class pocsagsender(gr.sync_block): 1762 | def __CalculateCRCandParity(self,datatype,data): 1763 | cw=data<<11 1764 | 1765 | # make leftmost bit "1" for datatype 1 (text) 1766 | if datatype == 1: 1767 | cw |= 0x80000000 1768 | #end if 1769 | 1770 | local_cw=cw 1771 | 1772 | for i in range(21): 1773 | if (cw & 0x80000000) > 0: 1774 | cw ^= 0xED200000 1775 | #end if 1776 | cw <<= 1 1777 | #end for 1778 | 1779 | local_cw |= (cw >> 21) 1780 | 1781 | 1782 | parity=0 1783 | cw=local_cw 1784 | for i in range(32): 1785 | if (cw & 0x80000000) > 0: 1786 | parity += 1 1787 | #end if 1788 | cw <<= 1 1789 | #end for 1790 | 1791 | # make even parity 1792 | local_cw += (parity % 2) 1793 | 1794 | return(local_cw) 1795 | # end Calculate CRC and Pairy 1796 | 1797 | 1798 | def __createpocsagmsg(self,address,source,txt): 1799 | 1800 | # checking input 1801 | if not (0 < address <= 0x1fffff): 1802 | raise ValueError(address) 1803 | #end if 1804 | 1805 | if not (0 <= source <= 3): 1806 | raise ValueError(source) 1807 | #nd if 1808 | 1809 | 1810 | if len(txt) == 0: 1811 | raise ValueError(txt) 1812 | #end if 1813 | 1814 | 1815 | # if len(txt) >= 40: 1816 | # txt=txt[:39] 1817 | # print("Warning, text truncated to 39 characters: {txt}".format(txt=txt)) 1818 | #end if 1819 | 1820 | # init pocsag message 1821 | # init data 1822 | # 2 batches 1823 | # 1 batch = sync codeword + 8 frames 1824 | # 1 frame = 1 codeword 1825 | 1826 | syncpattern = 0xAAAAAAAA 1827 | synccodeword = 0x7cd215d8 1828 | 1829 | idlepattern = 0x7ac9c197 1830 | 1831 | # init all codewords with idle pattern: 2 batches = 16 frames = 32 codewords 1832 | codeword = [idlepattern for i in range(32)] 1833 | 1834 | 1835 | # part 1: address + source 1836 | 1837 | # parse address and source 1838 | addressline=address>>3 1839 | 1840 | # add address-source 1841 | addressline<<=2 1842 | addressline += source 1843 | # the message starts at the frame address determined by the last 3 bits of the address 1844 | cwnum = ((address % 8) << 1) # codeword number 1845 | 1846 | codeword[cwnum]=self.__CalculateCRCandParity(datatype = 0, data = addressline) 1847 | 1848 | 1849 | # part 2: text 1850 | 1851 | # 2.1 convert text into int, also add EOT char 1852 | ts=[ord(c) for c in txt] + [0x04] 1853 | 1854 | # 2.2 make sure all characers are 7 bit 1855 | ts=list(map(lambda x: x%128, ts)) 1856 | 1857 | # 2.3 create one big list of bits 1858 | textbits='' 1859 | 1860 | for c in ts: 1861 | # BitArray(uint=c,length=7).bin to convert character to 7-character bit-string) 1862 | # note, for transmission, the bit-order must be reversed 1863 | charbits = BitArray(uint=c,length=7) 1864 | # reverse order of bits 1865 | charbits.reverse() 1866 | 1867 | # add to total string 1868 | textbits += charbits.bin 1869 | #end for 1870 | 1871 | nbits=len(textbits) 1872 | 1873 | # 2.4 make the list of bits a multiple of 20 bits 1874 | bitstoadd=20-(nbits % 20) 1875 | 1876 | if bitstoadd == 20: 1877 | bitstoadd = 0 1878 | #end if 1879 | bitstoadd_rest = bitstoadd%2 1880 | bitstoadd_2bit = bitstoadd >> 1 1881 | textbits += '01' * bitstoadd_2bit 1882 | if bitstoadd_rest == 1: 1883 | textbits += '0' 1884 | #end if 1885 | 1886 | 1887 | # 2.5 for every block of 20 bits, calculate crc and partity, and add to codeword 1888 | ncw = len(textbits)/20 # number of codewords 1889 | 1890 | startbit=0 1891 | stopbit=20 # (actually, the 19th bit) 1892 | for i in range(ncw): 1893 | thiscw=textbits[startbit:stopbit] 1894 | thiscw_i=int(thiscw,2) # convert list of bits to int 1895 | 1896 | #move up all pointers 1897 | startbit=stopbit # stopbit is startbit + 20 1898 | stopbit += 20 1899 | 1900 | #codeword pointer 1901 | cwnum += 1 1902 | 1903 | # calculate CRC for a text block (datatype = 1) 1904 | codeword[cwnum]=self.__CalculateCRCandParity(datatype = 1, data = thiscw_i) 1905 | #end for (number of 1906 | 1907 | 1908 | # done 1909 | 1910 | # now create complete pocsag message 1911 | # sync pattern 1912 | ret = [syncpattern for i in range(18)] 1913 | 1914 | # add sync codeword of 1st batch 1915 | ret.append(synccodeword) 1916 | 1917 | # add frames 0 to 7 (i.e. codewords 0 to 15) 1918 | ret += [codeword[n] for n in range(16)] 1919 | 1920 | 1921 | 1922 | # create add 2nd batch if the text has spilled into the 2nd batch 1923 | if cwnum >= 16: 1924 | # long message, 2 batches 1925 | nbatch=2 1926 | 1927 | # add sync codeword 1928 | ret.append(synccodeword) 1929 | 1930 | # and add frames 8 to 15 (i.e. codewords 16 to 31) 1931 | ret += [codeword[n] for n in range(16,32)] 1932 | else: 1933 | # short message, 1 batch 1934 | nbatch=1 1935 | 1936 | #end else - ifif 1937 | 1938 | 1939 | return((nbatch,ret)) 1940 | 1941 | # end "createpocsagmsg" 1942 | 1943 | 1944 | def __init__(self, number = 2060073, source = 0, sleeptime= 5, text="ON1ARF pocsag Python gnuradio"): # only default arguments here 1945 | gr.sync_block.__init__( 1946 | self, 1947 | name='pocsag generator', 1948 | in_sig=[], 1949 | out_sig=[np.int8] 1950 | ) 1951 | 1952 | 1953 | self.set_output_multiple(640) 1954 | # pocsag messages are either 1 batch (35 codeword = 120 octets), or 2 batches (52 codewords = 208 octets) 1955 | # make the stream a little bit longer to fit a multiple of 20 codewords 1956 | # pocsag message of 1 batch -> add 5 cw -> 40 (= 20 * 2) codewords 1957 | # pocsag message of 2 batches -> add 8 cw -> 60 (=20 *3) codewords 1958 | # 20 cw = 80 octets = 640 bits 1959 | 1960 | self.state = 0 1961 | self.sleeptime = sleeptime 1962 | self.number= int(number) 1963 | self.source = int(source) 1964 | 1965 | 1966 | nbatch,psmsg=self.__createpocsagmsg(number,source,text) 1967 | 1968 | # for a short message (nbtach=1), add 5 octest = 40 bits 1969 | # for a long message (nbatch=2), add 8 octets = 64 bits 1970 | 1971 | if nbatch == 1: 1972 | self.pocsagmsg=[0 for i in range(20)] 1973 | elif nbatch == 2: 1974 | self.pocsagmsg=[0 for i in range(32)] 1975 | else: 1976 | raise ValueError(nbatch) 1977 | #end if 1978 | 1979 | 1980 | for thismsg in psmsg: 1981 | for c in BitArray(uint=thismsg,length=32).bin: 1982 | self.pocsagmsg.append(1 if c == '1' else -1) 1983 | #end for 1984 | 1985 | # now add tail (all 0) 1986 | if nbatch == 1: 1987 | self.pocsagmsg+=[0 for i in range(20)] 1988 | elif nbatch == 2: 1989 | self.pocsagmsg+=[0 for i in range(32)] 1990 | else: 1991 | raise ValueError(nbatch) 1992 | #end if 1993 | 1994 | self.msglen=len(self.pocsagmsg) 1995 | 1996 | #end __init__ 1997 | 1998 | #def forecast(self, noutput_items, ninput_items_required): 1999 | # #setup size of input_items[i] for work call 2000 | # for i in range(len(ninput_items_required)): 2001 | # ninput_items_required[i] = noutput_items 2002 | 2003 | def work(self, input_items, output_items): 2004 | 2005 | if self.state == 0: 2006 | # state 0 -> send pocsag message 2007 | 2008 | output_items[0][:self.msglen]=np.array(self.pocsagmsg, dtype=np.int8) 2009 | 2010 | self.state = 1 # after this, go to sleep 2011 | return self.msglen 2012 | #end if 2013 | 2014 | # state 1: sleep 2015 | #time.sleep(self.sleeptime) 2016 | 2017 | # then go back to state 0 (transmit) 2018 | self.state = 0 2019 | 2020 | return -1 # return without any data 2021 | #end work 2022 | # end class "pocsagsender" 2023 | 2024 | 2025 | 2026 | comment 2027 | 2028 | 2029 | 2030 | _enabled 2031 | True 2032 | 2033 | 2034 | _coordinate 2035 | (427, 120) 2036 | 2037 | 2038 | _rotation 2039 | 0 2040 | 2041 | 2042 | id 2043 | pocsag_generator 2044 | 2045 | 2046 | number 2047 | RIC 2048 | 2049 | 2050 | sleeptime 2051 | 5 2052 | 2053 | 2054 | source 2055 | SubRIC 2056 | 2057 | 2058 | text 2059 | Text 2060 | 2061 | 2062 | 2063 | analog_frequency_modulator_fc_0 2064 | blocks_multiply_const_vxx_0 2065 | 0 2066 | 0 2067 | 2068 | 2069 | blocks_char_to_float_0 2070 | analog_frequency_modulator_fc_0 2071 | 0 2072 | 0 2073 | 2074 | 2075 | blocks_multiply_const_vxx_0 2076 | pfb_arb_resampler_xxx_0 2077 | 0 2078 | 0 2079 | 2080 | 2081 | blocks_repeat_0 2082 | blocks_char_to_float_0 2083 | 0 2084 | 0 2085 | 2086 | 2087 | pfb_arb_resampler_xxx_0 2088 | osmosdr_sink_0 2089 | 0 2090 | 0 2091 | 2092 | 2093 | pocsag_generator 2094 | blocks_repeat_0 2095 | 0 2096 | 0 2097 | 2098 | 2099 | --------------------------------------------------------------------------------